summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/numeric/ublas/test/tensor
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/libs/numeric/ublas/test/tensor')
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/Jamfile42
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_algorithms.cpp288
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_einstein_notation.cpp122
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_expression.cpp170
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_expression_evaluation.cpp240
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_extents.cpp449
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_functions.cpp453
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_multi_index.cpp146
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_multi_index_utility.cpp564
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_multiplication.cpp491
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_operators_arithmetic.cpp267
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_operators_comparison.cpp246
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_strides.cpp172
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_tensor.cpp473
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/test_tensor_matrix_vector.cpp472
-rw-r--r--src/boost/libs/numeric/ublas/test/tensor/utility.hpp56
16 files changed, 4651 insertions, 0 deletions
diff --git a/src/boost/libs/numeric/ublas/test/tensor/Jamfile b/src/boost/libs/numeric/ublas/test/tensor/Jamfile
new file mode 100644
index 00000000..0bbf2c56
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/Jamfile
@@ -0,0 +1,42 @@
+# Boost.uBLAS
+#
+# Copyright (c) 2018 Cem Bassoy
+#
+# Use, modification and distribution is subject to the Boost Software License,
+# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+project boost/ublas/test/tensor
+ : requirements
+ # these tests require C++17
+ <cxxstd>11:<build>no
+ <toolset>gcc:<cxxflags>"-Wall -Wno-unknown-pragmas -Wno-sign-compare -Wno-unused-but-set-variable"
+ ;
+
+alias unit_test_framework
+ : # sources
+ /boost//unit_test_framework
+ ;
+
+# make aliases explicit so the libraries will only be built when requested
+explicit unit_test_framework ;
+
+
+
+test-suite boost-ublas-tensor-test
+ :
+ [ run test_tensor.cpp
+ test_strides.cpp
+ test_operators_comparison.cpp
+ test_operators_arithmetic.cpp
+ test_multiplication.cpp
+ test_multi_index_utility.cpp
+ test_multi_index.cpp
+ test_functions.cpp
+ test_extents.cpp
+ test_expression_evaluation.cpp
+ test_einstein_notation.cpp
+ test_algorithms.cpp
+ test_tensor_matrix_vector.cpp
+ unit_test_framework ]
+ ;
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_algorithms.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_algorithms.cpp
new file mode 100644
index 00000000..4215fc2a
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_algorithms.cpp
@@ -0,0 +1,288 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+
+
+#include <iostream>
+#include <algorithm>
+#include <vector>
+#include <boost/numeric/ublas/tensor/algorithms.hpp>
+#include <boost/numeric/ublas/tensor/extents.hpp>
+#include <boost/numeric/ublas/tensor/strides.hpp>
+#include "utility.hpp"
+
+#include <boost/test/unit_test.hpp>
+
+
+BOOST_AUTO_TEST_SUITE ( test_tensor_algorithms,
+ * boost::unit_test::depends_on("test_extents")
+ * boost::unit_test::depends_on("test_strides"))
+
+
+using test_types = zip<int,long,float,double,std::complex<float>>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
+using test_types2 = std::tuple<int,long,float,double,std::complex<float>>;
+
+struct fixture
+{
+ using extents_type = boost::numeric::ublas::shape;
+ fixture()
+ : extents {
+ extents_type{1,1}, // 1
+ extents_type{1,2}, // 2
+ extents_type{2,1}, // 3
+ extents_type{2,3}, // 4
+ extents_type{2,3,1}, // 5
+ extents_type{4,1,3}, // 6
+ extents_type{1,2,3}, // 7
+ extents_type{4,2,3}, // 8
+ extents_type{4,2,3,5} } // 9
+ {
+ }
+ std::vector<extents_type> extents;
+};
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_algorithms_copy, value, test_types2, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = value;
+ using vector_type = std::vector<value_type>;
+
+
+ for(auto const& n : extents) {
+
+ auto a = vector_type(n.product());
+ auto b = vector_type(n.product());
+ auto c = vector_type(n.product());
+
+ auto wa = ublas::strides<ublas::first_order>(n);
+ auto wb = ublas::strides<ublas::last_order> (n);
+ auto wc = ublas::strides<ublas::first_order>(n);
+
+ auto v = value_type{};
+ for(auto i = 0ul; i < a.size(); ++i, v+=1){
+ a[i]=v;
+ }
+
+ ublas::copy( n.size(), n.data(), b.data(), wb.data(), a.data(), wa.data() );
+ ublas::copy( n.size(), n.data(), c.data(), wc.data(), b.data(), wb.data() );
+
+ for(auto i = 1ul; i < c.size(); ++i)
+ BOOST_CHECK_EQUAL( c[i], a[i] );
+
+ using size_type = typename ublas::strides<ublas::first_order>::value_type;
+ size_type const*const p0 = nullptr;
+ BOOST_CHECK_THROW( ublas::copy( n.size(), p0, c.data(), wc.data(), b.data(), wb.data() ), std::length_error );
+ BOOST_CHECK_THROW( ublas::copy( n.size(), n.data(), c.data(), p0, b.data(), wb.data() ), std::length_error );
+ BOOST_CHECK_THROW( ublas::copy( n.size(), n.data(), c.data(), wc.data(), b.data(), p0 ), std::length_error );
+
+ value_type* c0 = nullptr;
+ BOOST_CHECK_THROW( ublas::copy( n.size(), n.data(), c0, wc.data(), b.data(), wb.data() ), std::length_error );
+ }
+
+ // special case rank == 0
+ {
+ auto n = ublas::shape{};
+
+ auto a = vector_type(n.product());
+ auto b = vector_type(n.product());
+ auto c = vector_type(n.product());
+
+
+ auto wa = ublas::strides<ublas::first_order>(n);
+ auto wb = ublas::strides<ublas::last_order> (n);
+ auto wc = ublas::strides<ublas::first_order>(n);
+
+ ublas::copy( n.size(), n.data(), b.data(), wb.data(), a.data(), wa.data() );
+ ublas::copy( n.size(), n.data(), c.data(), wc.data(), b.data(), wb.data() );
+
+
+
+ BOOST_CHECK_NO_THROW( ublas::copy( n.size(), n.data(), c.data(), wc.data(), b.data(), wb.data() ) );
+
+ }
+
+
+
+
+
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_algorithms_transform, value, test_types2, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = value;
+ using vector_type = std::vector<value_type>;
+
+
+ for(auto const& n : extents) {
+
+ auto a = vector_type(n.product());
+ auto b = vector_type(n.product());
+ auto c = vector_type(n.product());
+
+ auto wa = ublas::strides<ublas::first_order>(n);
+ auto wb = ublas::strides<ublas::last_order> (n);
+ auto wc = ublas::strides<ublas::first_order>(n);
+
+ auto v = value_type{};
+ for(auto i = 0ul; i < a.size(); ++i, v+=1){
+ a[i]=v;
+ }
+
+ ublas::transform( n.size(), n.data(), b.data(), wb.data(), a.data(), wa.data(), [](value_type const& a){ return a + value_type(1);} );
+ ublas::transform( n.size(), n.data(), c.data(), wc.data(), b.data(), wb.data(), [](value_type const& a){ return a - value_type(1);} );
+
+ for(auto i = 1ul; i < c.size(); ++i)
+ BOOST_CHECK_EQUAL( c[i], a[i] );
+
+ }
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_algorithms_accumulate, value, test_types2, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = value;
+ using vector_type = std::vector<value_type>;
+
+
+ for(auto const& n : extents) {
+
+ auto const s = n.product();
+
+ auto a = vector_type(n.product());
+ // auto b = vector_type(n.product());
+ // auto c = vector_type(n.product());
+
+ auto wa = ublas::strides<ublas::first_order>(n);
+ // auto wb = ublas::strides<ublas::last_order> (n);
+ // auto wc = ublas::strides<ublas::first_order>(n);
+
+ auto v = value_type{};
+ for(auto i = 0ul; i < a.size(); ++i, v+=value_type(1)){
+ a[i]=v;
+ }
+
+ auto acc = ublas::accumulate( n.size(), n.data(), a.data(), wa.data(), v);
+
+ BOOST_CHECK_EQUAL( acc, value_type( s*(s+1) / 2 ) );
+
+
+ auto acc2 = ublas::accumulate( n.size(), n.data(), a.data(), wa.data(), v,
+ [](auto const& l, auto const& r){return l + r; });
+
+ BOOST_CHECK_EQUAL( acc2, value_type( s*(s+1) / 2 ) );
+
+ }
+}
+
+
+
+
+template<class V>
+void init(std::vector<V>& a)
+{
+ auto v = V(1);
+ for(auto i = 0u; i < a.size(); ++i, ++v){
+ a[i] = v;
+ }
+}
+
+template<class V>
+void init(std::vector<std::complex<V>>& a)
+{
+ auto v = std::complex<V>(1,1);
+ for(auto i = 0u; i < a.size(); ++i){
+ a[i] = v;
+ v.real(v.real()+1);
+ v.imag(v.imag()+1);
+ }
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_algorithms_trans, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using vector_type = std::vector<value_type>;
+ using strides_type = ublas::strides<layout_type>;
+ using extents_type = ublas::shape;
+ using size_type = typename extents_type::value_type;
+ using permutation_type = std::vector<size_type>;
+
+
+ for(auto const& n : extents) {
+
+ auto p = n.size();
+ auto s = n.product();
+
+ auto pi = permutation_type(p);
+ auto a = vector_type(s);
+ auto b1 = vector_type(s);
+ auto b2 = vector_type(s);
+ auto c1 = vector_type(s);
+ auto c2 = vector_type(s);
+
+ auto wa = strides_type(n);
+
+ init(a);
+
+ // so wie last-order.
+ for(auto i = size_type(0), j = p; i < n.size(); ++i, --j)
+ pi[i] = j;
+
+ auto nc = typename extents_type::base_type (p);
+ for(auto i = 0u; i < p; ++i)
+ nc[pi[i]-1] = n[i];
+
+ auto wc = strides_type(extents_type(nc));
+ auto wc_pi = typename strides_type::base_type (p);
+ for(auto i = 0u; i < p; ++i)
+ wc_pi[pi[i]-1] = wc[i];
+
+ ublas::copy ( p, n.data(), c1.data(), wc_pi.data(), a.data(), wa.data());
+ ublas::trans( p, n.data(), pi.data(), c2.data(), wc.data(), a.data(), wa.data() );
+
+ if(!std::is_compound_v<value_type>)
+ for(auto i = 0ul; i < s; ++i)
+ BOOST_CHECK_EQUAL( c1[i], c2[i] );
+
+
+ auto nb = typename extents_type::base_type (p);
+ for(auto i = 0u; i < p; ++i)
+ nb[pi[i]-1] = nc[i];
+
+ auto wb = strides_type (extents_type(nb));
+ auto wb_pi = typename strides_type::base_type (p);
+ for(auto i = 0u; i < p; ++i)
+ wb_pi[pi[i]-1] = wb[i];
+
+ ublas::copy ( p, nc.data(), b1.data(), wb_pi.data(), c1.data(), wc.data());
+ ublas::trans( p, nc.data(), pi.data(), b2.data(), wb.data(), c2.data(), wc.data() );
+
+ if(!std::is_compound_v<value_type>)
+ for(auto i = 0ul; i < s; ++i)
+ BOOST_CHECK_EQUAL( b1[i], b2[i] );
+
+ for(auto i = 0ul; i < s; ++i)
+ BOOST_CHECK_EQUAL( a[i], b2[i] );
+
+ }
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_einstein_notation.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_einstein_notation.cpp
new file mode 100644
index 00000000..b0326c80
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_einstein_notation.cpp
@@ -0,0 +1,122 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+// And we acknowledge the support from all contributors.
+
+
+#include <iostream>
+#include <algorithm>
+#include <boost/numeric/ublas/tensor.hpp>
+
+#include <boost/test/unit_test.hpp>
+
+#include "utility.hpp"
+
+BOOST_AUTO_TEST_SUITE ( test_einstein_notation, * boost::unit_test::depends_on("test_multi_index") )
+
+
+using test_types = zip<int,long,float,double,std::complex<float>>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
+
+//using test_types = zip<int>::with_t<boost::numeric::ublas::first_order>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE( test_einstein_multiplication, value, test_types )
+{
+ using namespace boost::numeric::ublas;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = tensor<value_type,layout_type>;
+ using namespace boost::numeric::ublas::index;
+
+ {
+ auto A = tensor_type{5,3};
+ auto B = tensor_type{3,4};
+ // auto C = tensor_type{4,5,6};
+
+ for(auto j = 0u; j < A.extents().at(1); ++j)
+ for(auto i = 0u; i < A.extents().at(0); ++i)
+ A.at( i,j ) = value_type(i+1);
+
+ for(auto j = 0u; j < B.extents().at(1); ++j)
+ for(auto i = 0u; i < B.extents().at(0); ++i)
+ B.at( i,j ) = value_type(i+1);
+
+
+
+ auto AB = A(_,_e) * B(_e,_);
+
+ // std::cout << "A = " << A << std::endl;
+ // std::cout << "B = " << B << std::endl;
+ // std::cout << "AB = " << AB << std::endl;
+
+ for(auto j = 0u; j < AB.extents().at(1); ++j)
+ for(auto i = 0u; i < AB.extents().at(0); ++i)
+ BOOST_CHECK_EQUAL( AB.at( i,j ) , value_type(A.at( i,0 ) * ( B.extents().at(0) * (B.extents().at(0)+1) / 2 )) );
+
+
+ }
+
+
+ {
+ auto A = tensor_type{4,5,3};
+ auto B = tensor_type{3,4,2};
+
+ for(auto k = 0u; k < A.extents().at(2); ++k)
+ for(auto j = 0u; j < A.extents().at(1); ++j)
+ for(auto i = 0u; i < A.extents().at(0); ++i)
+ A.at( i,j,k ) = value_type(i+1);
+
+ for(auto k = 0u; k < B.extents().at(2); ++k)
+ for(auto j = 0u; j < B.extents().at(1); ++j)
+ for(auto i = 0u; i < B.extents().at(0); ++i)
+ B.at( i,j,k ) = value_type(i+1);
+
+ auto AB = A(_d,_,_f) * B(_f,_d,_);
+
+ // std::cout << "A = " << A << std::endl;
+ // std::cout << "B = " << B << std::endl;
+ // std::cout << "AB = " << AB << std::endl;
+ // n*(n+1)/2;
+ auto const nf = ( B.extents().at(0) * (B.extents().at(0)+1) / 2 );
+ auto const nd = ( A.extents().at(0) * (A.extents().at(0)+1) / 2 );
+
+ for(auto j = 0u; j < AB.extents().at(1); ++j)
+ for(auto i = 0u; i < AB.extents().at(0); ++i)
+ BOOST_CHECK_EQUAL( AB.at( i,j ) , value_type(nf * nd) );
+
+ }
+
+
+ {
+ auto A = tensor_type{4,3};
+ auto B = tensor_type{3,4,2};
+
+ for(auto j = 0u; j < A.extents().at(1); ++j)
+ for(auto i = 0u; i < A.extents().at(0); ++i)
+ A.at( i,j ) = value_type(i+1);
+
+ for(auto k = 0u; k < B.extents().at(2); ++k)
+ for(auto j = 0u; j < B.extents().at(1); ++j)
+ for(auto i = 0u; i < B.extents().at(0); ++i)
+ B.at( i,j,k ) = value_type(i+1);
+
+ auto AB = A(_d,_f) * B(_f,_d,_);
+
+ // n*(n+1)/2;
+ auto const nf = ( B.extents().at(0) * (B.extents().at(0)+1) / 2 );
+ auto const nd = ( A.extents().at(0) * (A.extents().at(0)+1) / 2 );
+
+ for(auto i = 0u; i < AB.extents().at(0); ++i)
+ BOOST_CHECK_EQUAL ( AB.at( i ) , value_type(nf * nd) );
+
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_expression.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_expression.cpp
new file mode 100644
index 00000000..ae3ce205
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_expression.cpp
@@ -0,0 +1,170 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+
+
+
+
+#include <boost/numeric/ublas/tensor/expression.hpp>
+#include <boost/numeric/ublas/tensor/tensor.hpp>
+#include <boost/test/unit_test.hpp>
+#include "utility.hpp"
+
+#include <functional>
+#include <complex>
+
+
+
+using test_types = zip<int,long,float,double,std::complex<float>>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
+
+
+struct fixture
+{
+ using extents_type = boost::numeric::ublas::shape;
+ fixture()
+ : extents {
+ extents_type{}, // 0
+
+ extents_type{1,1}, // 1
+ extents_type{1,2}, // 2
+ extents_type{2,1}, // 3
+
+ extents_type{2,3}, // 4
+ extents_type{2,3,1}, // 5
+ extents_type{1,2,3}, // 6
+ extents_type{1,1,2,3}, // 7
+ extents_type{1,2,3,1,1}, // 8
+
+ extents_type{4,2,3}, // 9
+ extents_type{4,2,1,3}, // 10
+ extents_type{4,2,1,3,1}, // 11
+ extents_type{1,4,2,1,3,1} } // 12
+ {
+ }
+ std::vector<extents_type> extents;
+};
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_expression_access, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using tensor_expression_type = typename tensor_type::super_type;
+
+
+ for(auto const& e : extents) {
+
+ auto v = value_type{};
+ auto t = tensor_type(e);
+
+ for(auto& tt: t){ tt = v; v+=value_type{1}; }
+ const auto& tensor_expression_const = static_cast<tensor_expression_type const&>( t );
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( tensor_expression_const()(i), t(i) );
+
+ }
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_unary_expression, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ auto uplus1 = std::bind( std::plus<value_type>{}, std::placeholders::_1, value_type(1) );
+
+ for(auto const& e : extents) {
+
+ auto t = tensor_type(e);
+ auto v = value_type{};
+ for(auto& tt: t) { tt = v; v+=value_type{1}; }
+
+ const auto uexpr = ublas::detail::make_unary_tensor_expression<tensor_type>( t, uplus1 );
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( uexpr(i), uplus1(t(i)) );
+
+ auto uexpr_uexpr = ublas::detail::make_unary_tensor_expression<tensor_type>( uexpr, uplus1 );
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( uexpr_uexpr(i), uplus1(uplus1(t(i))) );
+
+ const auto & uexpr_e = uexpr.e;
+
+ BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr_e) >, tensor_type > ) );
+
+ const auto & uexpr_uexpr_e_e = uexpr_uexpr.e.e;
+
+ BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr_uexpr_e_e) >, tensor_type > ) );
+
+
+ }
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_binary_expression, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ auto uplus1 = std::bind( std::plus<value_type>{}, std::placeholders::_1, value_type(1) );
+ auto uplus2 = std::bind( std::plus<value_type>{}, std::placeholders::_1, value_type(2) );
+ auto bplus = std::plus <value_type>{};
+ auto bminus = std::minus<value_type>{};
+
+ for(auto const& e : extents) {
+
+ auto t = tensor_type(e);
+ auto v = value_type{};
+ for(auto& tt: t){ tt = v; v+=value_type{1}; }
+
+ auto uexpr1 = ublas::detail::make_unary_tensor_expression<tensor_type>( t, uplus1 );
+ auto uexpr2 = ublas::detail::make_unary_tensor_expression<tensor_type>( t, uplus2 );
+
+ BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr1.e) >, tensor_type > ) );
+ BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr2.e) >, tensor_type > ) );
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( uexpr1(i), uplus1(t(i)) );
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( uexpr2(i), uplus2(t(i)) );
+
+ auto bexpr_uexpr = ublas::detail::make_binary_tensor_expression<tensor_type>( uexpr1, uexpr2, bplus );
+
+ BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.el.e) >, tensor_type > ) );
+ BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.er.e) >, tensor_type > ) );
+
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( bexpr_uexpr(i), bplus(uexpr1(i),uexpr2(i)) );
+
+ auto bexpr_bexpr_uexpr = ublas::detail::make_binary_tensor_expression<tensor_type>( bexpr_uexpr, t, bminus );
+
+ BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.el.el.e) >, tensor_type > ) );
+ BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.el.er.e) >, tensor_type > ) );
+ BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.er) >, tensor_type > ) );
+ BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.er) >, tensor_type > ) );
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( bexpr_bexpr_uexpr(i), bminus(bexpr_uexpr(i),t(i)) );
+
+ }
+
+
+}
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_expression_evaluation.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_expression_evaluation.cpp
new file mode 100644
index 00000000..002d51a2
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_expression_evaluation.cpp
@@ -0,0 +1,240 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+
+
+
+#include <boost/numeric/ublas/tensor/expression_evaluation.hpp>
+#include <boost/numeric/ublas/tensor/expression.hpp>
+#include <boost/numeric/ublas/tensor/tensor.hpp>
+#include <boost/test/unit_test.hpp>
+#include "utility.hpp"
+
+#include <functional>
+
+using test_types = zip<int,long,float,double,std::complex<float>>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
+
+
+struct fixture
+{
+ using extents_type = boost::numeric::ublas::shape;
+ fixture()
+ : extents{
+ extents_type{}, // 0
+
+ extents_type{1,1}, // 1
+ extents_type{1,2}, // 2
+ extents_type{2,1}, // 3
+
+ extents_type{2,3}, // 4
+ extents_type{2,3,1}, // 5
+ extents_type{1,2,3}, // 6
+ extents_type{1,1,2,3}, // 7
+ extents_type{1,2,3,1,1}, // 8
+
+ extents_type{4,2,3}, // 9
+ extents_type{4,2,1,3}, // 10
+ extents_type{4,2,1,3,1}, // 11
+ extents_type{1,4,2,1,3,1}} // 12
+ {
+ }
+ std::vector<extents_type> extents;
+};
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_expression_retrieve_extents, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ auto uplus1 = std::bind( std::plus<value_type>{}, std::placeholders::_1, value_type(1) );
+ auto uplus2 = std::bind( std::plus<value_type>{}, value_type(2), std::placeholders::_2 );
+ auto bplus = std::plus <value_type>{};
+ auto bminus = std::minus<value_type>{};
+
+ for(auto const& e : extents) {
+
+ auto t = tensor_type(e);
+ auto v = value_type{};
+ for(auto& tt: t){ tt = v; v+=value_type{1}; }
+
+
+ BOOST_CHECK( ublas::detail::retrieve_extents( t ) == e );
+
+
+ // uexpr1 = t+1
+ // uexpr2 = 2+t
+ auto uexpr1 = ublas::detail::make_unary_tensor_expression<tensor_type>( t, uplus1 );
+ auto uexpr2 = ublas::detail::make_unary_tensor_expression<tensor_type>( t, uplus2 );
+
+ BOOST_CHECK( ublas::detail::retrieve_extents( uexpr1 ) == e );
+ BOOST_CHECK( ublas::detail::retrieve_extents( uexpr2 ) == e );
+
+ // bexpr_uexpr = (t+1) + (2+t)
+ auto bexpr_uexpr = ublas::detail::make_binary_tensor_expression<tensor_type>( uexpr1, uexpr2, bplus );
+
+ BOOST_CHECK( ublas::detail::retrieve_extents( bexpr_uexpr ) == e );
+
+
+ // bexpr_bexpr_uexpr = ((t+1) + (2+t)) - t
+ auto bexpr_bexpr_uexpr = ublas::detail::make_binary_tensor_expression<tensor_type>( bexpr_uexpr, t, bminus );
+
+ BOOST_CHECK( ublas::detail::retrieve_extents( bexpr_bexpr_uexpr ) == e );
+
+ }
+
+
+ for(auto i = 0u; i < extents.size()-1; ++i)
+ {
+
+ auto v = value_type{};
+
+ auto t1 = tensor_type(extents[i]);
+ for(auto& tt: t1){ tt = v; v+=value_type{1}; }
+
+ auto t2 = tensor_type(extents[i+1]);
+ for(auto& tt: t2){ tt = v; v+=value_type{2}; }
+
+ BOOST_CHECK( ublas::detail::retrieve_extents( t1 ) != ublas::detail::retrieve_extents( t2 ) );
+
+ // uexpr1 = t1+1
+ // uexpr2 = 2+t2
+ auto uexpr1 = ublas::detail::make_unary_tensor_expression<tensor_type>( t1, uplus1 );
+ auto uexpr2 = ublas::detail::make_unary_tensor_expression<tensor_type>( t2, uplus2 );
+
+ BOOST_CHECK( ublas::detail::retrieve_extents( t1 ) == ublas::detail::retrieve_extents( uexpr1 ) );
+ BOOST_CHECK( ublas::detail::retrieve_extents( t2 ) == ublas::detail::retrieve_extents( uexpr2 ) );
+ BOOST_CHECK( ublas::detail::retrieve_extents( uexpr1 ) != ublas::detail::retrieve_extents( uexpr2 ) );
+
+ // bexpr_uexpr = (t1+1) + (2+t2)
+ auto bexpr_uexpr = ublas::detail::make_binary_tensor_expression<tensor_type>( uexpr1, uexpr2, bplus );
+
+ BOOST_CHECK( ublas::detail::retrieve_extents( bexpr_uexpr ) == ublas::detail::retrieve_extents(t1) );
+
+
+ // bexpr_bexpr_uexpr = ((t1+1) + (2+t2)) - t2
+ auto bexpr_bexpr_uexpr1 = ublas::detail::make_binary_tensor_expression<tensor_type>( bexpr_uexpr, t2, bminus );
+
+ BOOST_CHECK( ublas::detail::retrieve_extents( bexpr_bexpr_uexpr1 ) == ublas::detail::retrieve_extents(t2) );
+
+
+ // bexpr_bexpr_uexpr = t2 - ((t1+1) + (2+t2))
+ auto bexpr_bexpr_uexpr2 = ublas::detail::make_binary_tensor_expression<tensor_type>( t2, bexpr_uexpr, bminus );
+
+ BOOST_CHECK( ublas::detail::retrieve_extents( bexpr_bexpr_uexpr2 ) == ublas::detail::retrieve_extents(t2) );
+ }
+}
+
+
+
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_expression_all_extents_equal, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ auto uplus1 = std::bind( std::plus<value_type>{}, std::placeholders::_1, value_type(1) );
+ auto uplus2 = std::bind( std::plus<value_type>{}, value_type(2), std::placeholders::_2 );
+ auto bplus = std::plus <value_type>{};
+ auto bminus = std::minus<value_type>{};
+
+ for(auto const& e : extents) {
+
+ auto t = tensor_type(e);
+ auto v = value_type{};
+ for(auto& tt: t){ tt = v; v+=value_type{1}; }
+
+
+ BOOST_CHECK( ublas::detail::all_extents_equal( t , e ) );
+
+
+ // uexpr1 = t+1
+ // uexpr2 = 2+t
+ auto uexpr1 = ublas::detail::make_unary_tensor_expression<tensor_type>( t, uplus1 );
+ auto uexpr2 = ublas::detail::make_unary_tensor_expression<tensor_type>( t, uplus2 );
+
+ BOOST_CHECK( ublas::detail::all_extents_equal( uexpr1, e ) );
+ BOOST_CHECK( ublas::detail::all_extents_equal( uexpr2, e ) );
+
+ // bexpr_uexpr = (t+1) + (2+t)
+ auto bexpr_uexpr = ublas::detail::make_binary_tensor_expression<tensor_type>( uexpr1, uexpr2, bplus );
+
+ BOOST_CHECK( ublas::detail::all_extents_equal( bexpr_uexpr, e ) );
+
+
+ // bexpr_bexpr_uexpr = ((t+1) + (2+t)) - t
+ auto bexpr_bexpr_uexpr = ublas::detail::make_binary_tensor_expression<tensor_type>( bexpr_uexpr, t, bminus );
+
+ BOOST_CHECK( ublas::detail::all_extents_equal( bexpr_bexpr_uexpr , e ) );
+
+ }
+
+
+ for(auto i = 0u; i < extents.size()-1; ++i)
+ {
+
+ auto v = value_type{};
+
+ auto t1 = tensor_type(extents[i]);
+ for(auto& tt: t1){ tt = v; v+=value_type{1}; }
+
+ auto t2 = tensor_type(extents[i+1]);
+ for(auto& tt: t2){ tt = v; v+=value_type{2}; }
+
+ BOOST_CHECK( ublas::detail::all_extents_equal( t1, ublas::detail::retrieve_extents(t1) ) );
+ BOOST_CHECK( ublas::detail::all_extents_equal( t2, ublas::detail::retrieve_extents(t2) ) );
+
+ // uexpr1 = t1+1
+ // uexpr2 = 2+t2
+ auto uexpr1 = ublas::detail::make_unary_tensor_expression<tensor_type>( t1, uplus1 );
+ auto uexpr2 = ublas::detail::make_unary_tensor_expression<tensor_type>( t2, uplus2 );
+
+ BOOST_CHECK( ublas::detail::all_extents_equal( uexpr1, ublas::detail::retrieve_extents(uexpr1) ) );
+ BOOST_CHECK( ublas::detail::all_extents_equal( uexpr2, ublas::detail::retrieve_extents(uexpr2) ) );
+
+ // bexpr_uexpr = (t1+1) + (2+t2)
+ auto bexpr_uexpr = ublas::detail::make_binary_tensor_expression<tensor_type>( uexpr1, uexpr2, bplus );
+
+ BOOST_CHECK( ! ublas::detail::all_extents_equal( bexpr_uexpr, ublas::detail::retrieve_extents( bexpr_uexpr ) ) );
+
+ // bexpr_bexpr_uexpr = ((t1+1) + (2+t2)) - t2
+ auto bexpr_bexpr_uexpr1 = ublas::detail::make_binary_tensor_expression<tensor_type>( bexpr_uexpr, t2, bminus );
+
+ BOOST_CHECK( ! ublas::detail::all_extents_equal( bexpr_bexpr_uexpr1, ublas::detail::retrieve_extents( bexpr_bexpr_uexpr1 ) ) );
+
+ // bexpr_bexpr_uexpr = t2 - ((t1+1) + (2+t2))
+ auto bexpr_bexpr_uexpr2 = ublas::detail::make_binary_tensor_expression<tensor_type>( t2, bexpr_uexpr, bminus );
+
+ BOOST_CHECK( ! ublas::detail::all_extents_equal( bexpr_bexpr_uexpr2, ublas::detail::retrieve_extents( bexpr_bexpr_uexpr2 ) ) );
+
+
+ // bexpr_uexpr2 = (t1+1) + t2
+ auto bexpr_uexpr2 = ublas::detail::make_binary_tensor_expression<tensor_type>( uexpr1, t2, bplus );
+ BOOST_CHECK( ! ublas::detail::all_extents_equal( bexpr_uexpr2, ublas::detail::retrieve_extents( bexpr_uexpr2 ) ) );
+
+
+ // bexpr_uexpr2 = ((t1+1) + t2) + t1
+ auto bexpr_bexpr_uexpr3 = ublas::detail::make_binary_tensor_expression<tensor_type>( bexpr_uexpr2, t1, bplus );
+ BOOST_CHECK( ! ublas::detail::all_extents_equal( bexpr_bexpr_uexpr3, ublas::detail::retrieve_extents( bexpr_bexpr_uexpr3 ) ) );
+
+ // bexpr_uexpr2 = t1 + (((t1+1) + t2) + t1)
+ auto bexpr_bexpr_uexpr4 = ublas::detail::make_binary_tensor_expression<tensor_type>( t1, bexpr_bexpr_uexpr3, bplus );
+ BOOST_CHECK( ! ublas::detail::all_extents_equal( bexpr_bexpr_uexpr4, ublas::detail::retrieve_extents( bexpr_bexpr_uexpr4 ) ) );
+
+ }
+}
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_extents.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_extents.cpp
new file mode 100644
index 00000000..9fbeee94
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_extents.cpp
@@ -0,0 +1,449 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <boost/test/unit_test.hpp>
+#include <boost/numeric/ublas/tensor/extents.hpp>
+#include <vector>
+
+BOOST_AUTO_TEST_SUITE ( test_extents )
+
+
+//*boost::unit_test::label("extents")
+//*boost::unit_test::label("constructor")
+
+BOOST_AUTO_TEST_CASE(test_extents_ctor)
+{
+ using namespace boost::numeric;
+ using extents = ublas::basic_extents<unsigned>;
+
+
+ auto e0 = extents{};
+ BOOST_CHECK( e0.empty());
+ BOOST_CHECK_EQUAL ( e0.size(),0);
+
+ auto e1 = extents{1,1};
+ BOOST_CHECK(!e1.empty());
+ BOOST_CHECK_EQUAL ( e1.size(),2);
+
+ auto e2 = extents{1,2};
+ BOOST_CHECK(!e2.empty());
+ BOOST_CHECK_EQUAL ( e2.size(),2);
+
+ auto e3 = extents{2,1};
+ BOOST_CHECK (!e3.empty());
+ BOOST_CHECK_EQUAL ( e3.size(),2);
+
+ auto e4 = extents{2,3};
+ BOOST_CHECK(!e4.empty());
+ BOOST_CHECK_EQUAL ( e4.size(),2);
+
+ auto e5 = extents{2,3,1};
+ BOOST_CHECK (!e5.empty());
+ BOOST_CHECK_EQUAL ( e5.size(),3);
+
+ auto e6 = extents{1,2,3}; // 6
+ BOOST_CHECK(!e6.empty());
+ BOOST_CHECK_EQUAL ( e6.size(),3);
+
+ auto e7 = extents{4,2,3}; // 7
+ BOOST_CHECK(!e7.empty());
+ BOOST_CHECK_EQUAL ( e7.size(),3);
+
+ BOOST_CHECK_THROW( extents({1,0}), std::length_error );
+ BOOST_CHECK_THROW( extents({0} ), std::length_error );
+ BOOST_CHECK_THROW( extents({3} ), std::length_error );
+ BOOST_CHECK_THROW( extents({0,1}), std::length_error );
+}
+
+
+
+
+struct fixture {
+ using extents_type = boost::numeric::ublas::basic_extents<unsigned>;
+ fixture() : extents{
+ extents_type{}, // 0
+
+ extents_type{1,1}, // 1
+ extents_type{1,2}, // 2
+ extents_type{2,1}, // 3
+
+ extents_type{2,3}, // 4
+ extents_type{2,3,1}, // 5
+ extents_type{1,2,3}, // 6
+ extents_type{1,1,2,3}, // 7
+ extents_type{1,2,3,1,1}, // 8
+
+ extents_type{4,2,3}, // 9
+ extents_type{4,2,1,3}, // 10
+ extents_type{4,2,1,3,1}, // 11
+ extents_type{1,4,2,1,3,1}, // 12
+} // 13
+ {}
+ std::vector<extents_type> extents;
+};
+
+BOOST_FIXTURE_TEST_CASE(test_extents_access, fixture, *boost::unit_test::label("extents") *boost::unit_test::label("access"))
+{
+ using namespace boost::numeric;
+
+ BOOST_REQUIRE_EQUAL(extents.size(),13);
+
+ BOOST_CHECK_EQUAL (extents[ 0].size(), 0);
+ BOOST_CHECK (extents[ 0].empty() );
+
+ BOOST_REQUIRE_EQUAL(extents[ 1].size(), 2);
+ BOOST_REQUIRE_EQUAL(extents[ 2].size(), 2);
+ BOOST_REQUIRE_EQUAL(extents[ 3].size(), 2);
+ BOOST_REQUIRE_EQUAL(extents[ 4].size(), 2);
+ BOOST_REQUIRE_EQUAL(extents[ 5].size(), 3);
+ BOOST_REQUIRE_EQUAL(extents[ 6].size(), 3);
+ BOOST_REQUIRE_EQUAL(extents[ 7].size(), 4);
+ BOOST_REQUIRE_EQUAL(extents[ 8].size(), 5);
+ BOOST_REQUIRE_EQUAL(extents[ 9].size(), 3);
+ BOOST_REQUIRE_EQUAL(extents[10].size(), 4);
+ BOOST_REQUIRE_EQUAL(extents[11].size(), 5);
+ BOOST_REQUIRE_EQUAL(extents[12].size(), 6);
+
+
+ BOOST_CHECK_EQUAL(extents[1][0],1);
+ BOOST_CHECK_EQUAL(extents[1][1],1);
+
+ BOOST_CHECK_EQUAL(extents[2][0],1);
+ BOOST_CHECK_EQUAL(extents[2][1],2);
+
+ BOOST_CHECK_EQUAL(extents[3][0],2);
+ BOOST_CHECK_EQUAL(extents[3][1],1);
+
+ BOOST_CHECK_EQUAL(extents[4][0],2);
+ BOOST_CHECK_EQUAL(extents[4][1],3);
+
+ BOOST_CHECK_EQUAL(extents[5][0],2);
+ BOOST_CHECK_EQUAL(extents[5][1],3);
+ BOOST_CHECK_EQUAL(extents[5][2],1);
+
+ BOOST_CHECK_EQUAL(extents[6][0],1);
+ BOOST_CHECK_EQUAL(extents[6][1],2);
+ BOOST_CHECK_EQUAL(extents[6][2],3);
+
+ BOOST_CHECK_EQUAL(extents[7][0],1);
+ BOOST_CHECK_EQUAL(extents[7][1],1);
+ BOOST_CHECK_EQUAL(extents[7][2],2);
+ BOOST_CHECK_EQUAL(extents[7][3],3);
+
+ BOOST_CHECK_EQUAL(extents[8][0],1);
+ BOOST_CHECK_EQUAL(extents[8][1],2);
+ BOOST_CHECK_EQUAL(extents[8][2],3);
+ BOOST_CHECK_EQUAL(extents[8][3],1);
+ BOOST_CHECK_EQUAL(extents[8][4],1);
+
+ BOOST_CHECK_EQUAL(extents[9][0],4);
+ BOOST_CHECK_EQUAL(extents[9][1],2);
+ BOOST_CHECK_EQUAL(extents[9][2],3);
+
+ BOOST_CHECK_EQUAL(extents[10][0],4);
+ BOOST_CHECK_EQUAL(extents[10][1],2);
+ BOOST_CHECK_EQUAL(extents[10][2],1);
+ BOOST_CHECK_EQUAL(extents[10][3],3);
+
+ BOOST_CHECK_EQUAL(extents[11][0],4);
+ BOOST_CHECK_EQUAL(extents[11][1],2);
+ BOOST_CHECK_EQUAL(extents[11][2],1);
+ BOOST_CHECK_EQUAL(extents[11][3],3);
+ BOOST_CHECK_EQUAL(extents[11][4],1);
+
+ BOOST_CHECK_EQUAL(extents[12][0],1);
+ BOOST_CHECK_EQUAL(extents[12][1],4);
+ BOOST_CHECK_EQUAL(extents[12][2],2);
+ BOOST_CHECK_EQUAL(extents[12][3],1);
+ BOOST_CHECK_EQUAL(extents[12][4],3);
+ BOOST_CHECK_EQUAL(extents[12][5],1);
+}
+
+BOOST_FIXTURE_TEST_CASE(test_extents_copy_ctor, fixture, *boost::unit_test::label("extents") *boost::unit_test::label("copy_ctor"))
+{
+ BOOST_REQUIRE_EQUAL(extents.size(),13);
+
+ auto e0 = extents[ 0]; // {}
+ auto e1 = extents[ 1]; // {1,1}
+ auto e2 = extents[ 2]; // {1,2}
+ auto e3 = extents[ 3]; // {2,1}
+ auto e4 = extents[ 4]; // {2,3}
+ auto e5 = extents[ 5]; // {2,3,1}
+ auto e6 = extents[ 6]; // {1,2,3}
+ auto e7 = extents[ 7]; // {1,1,2,3}
+ auto e8 = extents[ 8]; // {1,2,3,1,1}
+ auto e9 = extents[ 9]; // {4,2,3}
+ auto e10 = extents[10]; // {4,2,1,3}
+ auto e11 = extents[11]; // {4,2,1,3,1}
+ auto e12 = extents[12]; // {1,4,2,1,3,1}
+
+ BOOST_CHECK_EQUAL (e0.size(), 0);
+ BOOST_CHECK (e0.empty() );
+
+ BOOST_REQUIRE_EQUAL(e1 .size(), 2);
+ BOOST_REQUIRE_EQUAL(e2 .size(), 2);
+ BOOST_REQUIRE_EQUAL(e3 .size(), 2);
+ BOOST_REQUIRE_EQUAL(e4 .size(), 2);
+ BOOST_REQUIRE_EQUAL(e5 .size(), 3);
+ BOOST_REQUIRE_EQUAL(e6 .size(), 3);
+ BOOST_REQUIRE_EQUAL(e7 .size(), 4);
+ BOOST_REQUIRE_EQUAL(e8 .size(), 5);
+ BOOST_REQUIRE_EQUAL(e9 .size(), 3);
+ BOOST_REQUIRE_EQUAL(e10.size(), 4);
+ BOOST_REQUIRE_EQUAL(e11.size(), 5);
+ BOOST_REQUIRE_EQUAL(e12.size(), 6);
+
+
+ BOOST_CHECK_EQUAL(e1[0],1);
+ BOOST_CHECK_EQUAL(e1[1],1);
+
+ BOOST_CHECK_EQUAL(e2[0],1);
+ BOOST_CHECK_EQUAL(e2[1],2);
+
+ BOOST_CHECK_EQUAL(e3[0],2);
+ BOOST_CHECK_EQUAL(e3[1],1);
+
+ BOOST_CHECK_EQUAL(e4[0],2);
+ BOOST_CHECK_EQUAL(e4[1],3);
+
+ BOOST_CHECK_EQUAL(e5[0],2);
+ BOOST_CHECK_EQUAL(e5[1],3);
+ BOOST_CHECK_EQUAL(e5[2],1);
+
+ BOOST_CHECK_EQUAL(e6[0],1);
+ BOOST_CHECK_EQUAL(e6[1],2);
+ BOOST_CHECK_EQUAL(e6[2],3);
+
+ BOOST_CHECK_EQUAL(e7[0],1);
+ BOOST_CHECK_EQUAL(e7[1],1);
+ BOOST_CHECK_EQUAL(e7[2],2);
+ BOOST_CHECK_EQUAL(e7[3],3);
+
+ BOOST_CHECK_EQUAL(e8[0],1);
+ BOOST_CHECK_EQUAL(e8[1],2);
+ BOOST_CHECK_EQUAL(e8[2],3);
+ BOOST_CHECK_EQUAL(e8[3],1);
+ BOOST_CHECK_EQUAL(e8[4],1);
+
+ BOOST_CHECK_EQUAL(e9[0],4);
+ BOOST_CHECK_EQUAL(e9[1],2);
+ BOOST_CHECK_EQUAL(e9[2],3);
+
+ BOOST_CHECK_EQUAL(e10[0],4);
+ BOOST_CHECK_EQUAL(e10[1],2);
+ BOOST_CHECK_EQUAL(e10[2],1);
+ BOOST_CHECK_EQUAL(e10[3],3);
+
+ BOOST_CHECK_EQUAL(e11[0],4);
+ BOOST_CHECK_EQUAL(e11[1],2);
+ BOOST_CHECK_EQUAL(e11[2],1);
+ BOOST_CHECK_EQUAL(e11[3],3);
+ BOOST_CHECK_EQUAL(e11[4],1);
+
+ BOOST_CHECK_EQUAL(e12[0],1);
+ BOOST_CHECK_EQUAL(e12[1],4);
+ BOOST_CHECK_EQUAL(e12[2],2);
+ BOOST_CHECK_EQUAL(e12[3],1);
+ BOOST_CHECK_EQUAL(e12[4],3);
+ BOOST_CHECK_EQUAL(e12[5],1);
+
+}
+
+BOOST_FIXTURE_TEST_CASE(test_extents_is, fixture, *boost::unit_test::label("extents") *boost::unit_test::label("query"))
+{
+ BOOST_REQUIRE_EQUAL(extents.size(),13);
+
+ auto e0 = extents[ 0]; // {}
+ auto e1 = extents[ 1]; // {1,1}
+ auto e2 = extents[ 2]; // {1,2}
+ auto e3 = extents[ 3]; // {2,1}
+ auto e4 = extents[ 4]; // {2,3}
+ auto e5 = extents[ 5]; // {2,3,1}
+ auto e6 = extents[ 6]; // {1,2,3}
+ auto e7 = extents[ 7]; // {1,1,2,3}
+ auto e8 = extents[ 8]; // {1,2,3,1,1}
+ auto e9 = extents[ 9]; // {4,2,3}
+ auto e10 = extents[10]; // {4,2,1,3}
+ auto e11 = extents[11]; // {4,2,1,3,1}
+ auto e12 = extents[12]; // {1,4,2,1,3,1}
+
+ BOOST_CHECK( e0.empty ());
+ BOOST_CHECK( ! e0.is_scalar());
+ BOOST_CHECK( ! e0.is_vector());
+ BOOST_CHECK( ! e0.is_matrix());
+ BOOST_CHECK( ! e0.is_tensor());
+
+ BOOST_CHECK( ! e1.empty () );
+ BOOST_CHECK( e1.is_scalar() );
+ BOOST_CHECK( ! e1.is_vector() );
+ BOOST_CHECK( ! e1.is_matrix() );
+ BOOST_CHECK( ! e1.is_tensor() );
+
+ BOOST_CHECK( ! e2.empty () );
+ BOOST_CHECK( ! e2.is_scalar() );
+ BOOST_CHECK( e2.is_vector() );
+ BOOST_CHECK( ! e2.is_matrix() );
+ BOOST_CHECK( ! e2.is_tensor() );
+
+ BOOST_CHECK( ! e3.empty () );
+ BOOST_CHECK( ! e3.is_scalar() );
+ BOOST_CHECK( e3.is_vector() );
+ BOOST_CHECK( ! e3.is_matrix() );
+ BOOST_CHECK( ! e3.is_tensor() );
+
+ BOOST_CHECK( ! e4.empty () );
+ BOOST_CHECK( ! e4.is_scalar() );
+ BOOST_CHECK( ! e4.is_vector() );
+ BOOST_CHECK( e4.is_matrix() );
+ BOOST_CHECK( ! e4.is_tensor() );
+
+ BOOST_CHECK( ! e5.empty () );
+ BOOST_CHECK( ! e5.is_scalar() );
+ BOOST_CHECK( ! e5.is_vector() );
+ BOOST_CHECK( e5.is_matrix() );
+ BOOST_CHECK( ! e5.is_tensor() );
+
+ BOOST_CHECK( ! e6.empty () );
+ BOOST_CHECK( ! e6.is_scalar() );
+ BOOST_CHECK( ! e6.is_vector() );
+ BOOST_CHECK( ! e6.is_matrix() );
+ BOOST_CHECK( e6.is_tensor() );
+
+ BOOST_CHECK( ! e7.empty () );
+ BOOST_CHECK( ! e7.is_scalar() );
+ BOOST_CHECK( ! e7.is_vector() );
+ BOOST_CHECK( ! e7.is_matrix() );
+ BOOST_CHECK( e7.is_tensor() );
+
+ BOOST_CHECK( ! e8.empty () );
+ BOOST_CHECK( ! e8.is_scalar() );
+ BOOST_CHECK( ! e8.is_vector() );
+ BOOST_CHECK( ! e8.is_matrix() );
+ BOOST_CHECK( e8.is_tensor() );
+
+ BOOST_CHECK( ! e9.empty () );
+ BOOST_CHECK( ! e9.is_scalar() );
+ BOOST_CHECK( ! e9.is_vector() );
+ BOOST_CHECK( ! e9.is_matrix() );
+ BOOST_CHECK( e9.is_tensor() );
+
+ BOOST_CHECK( ! e10.empty () );
+ BOOST_CHECK( ! e10.is_scalar() );
+ BOOST_CHECK( ! e10.is_vector() );
+ BOOST_CHECK( ! e10.is_matrix() );
+ BOOST_CHECK( e10.is_tensor() );
+
+ BOOST_CHECK( ! e11.empty () );
+ BOOST_CHECK( ! e11.is_scalar() );
+ BOOST_CHECK( ! e11.is_vector() );
+ BOOST_CHECK( ! e11.is_matrix() );
+ BOOST_CHECK( e11.is_tensor() );
+
+ BOOST_CHECK( ! e12.empty () );
+ BOOST_CHECK( ! e12.is_scalar() );
+ BOOST_CHECK( ! e12.is_vector() );
+ BOOST_CHECK( ! e12.is_matrix() );
+ BOOST_CHECK( e12.is_tensor() );
+}
+
+
+BOOST_FIXTURE_TEST_CASE(test_extents_squeeze, fixture, *boost::unit_test::label("extents") *boost::unit_test::label("squeeze"))
+{
+ BOOST_REQUIRE_EQUAL(extents.size(),13);
+
+ auto e0 = extents[ 0].squeeze(); // {}
+ auto e1 = extents[ 1].squeeze(); // {1,1}
+ auto e2 = extents[ 2].squeeze(); // {1,2}
+ auto e3 = extents[ 3].squeeze(); // {2,1}
+
+ auto e4 = extents[ 4].squeeze(); // {2,3}
+ auto e5 = extents[ 5].squeeze(); // {2,3}
+ auto e6 = extents[ 6].squeeze(); // {2,3}
+ auto e7 = extents[ 7].squeeze(); // {2,3}
+ auto e8 = extents[ 8].squeeze(); // {2,3}
+
+ auto e9 = extents[ 9].squeeze(); // {4,2,3}
+ auto e10 = extents[10].squeeze(); // {4,2,3}
+ auto e11 = extents[11].squeeze(); // {4,2,3}
+ auto e12 = extents[12].squeeze(); // {4,2,3}
+
+ BOOST_CHECK( (e0 == extents_type{} ) );
+ BOOST_CHECK( (e1 == extents_type{1,1}) );
+ BOOST_CHECK( (e2 == extents_type{1,2}) );
+ BOOST_CHECK( (e3 == extents_type{2,1}) );
+
+ BOOST_CHECK( (e4 == extents_type{2,3}) );
+ BOOST_CHECK( (e5 == extents_type{2,3}) );
+ BOOST_CHECK( (e6 == extents_type{2,3}) );
+ BOOST_CHECK( (e7 == extents_type{2,3}) );
+ BOOST_CHECK( (e8 == extents_type{2,3}) );
+
+ BOOST_CHECK( (e9 == extents_type{4,2,3}) );
+ BOOST_CHECK( (e10 == extents_type{4,2,3}) );
+ BOOST_CHECK( (e11 == extents_type{4,2,3}) );
+ BOOST_CHECK( (e12 == extents_type{4,2,3}) );
+
+}
+
+
+BOOST_FIXTURE_TEST_CASE(test_extents_valid, fixture, *boost::unit_test::label("extents") *boost::unit_test::label("valid"))
+{
+
+ using namespace boost::numeric;
+
+ BOOST_REQUIRE_EQUAL(extents.size(),13);
+
+ for(auto const& e : extents){
+ if(e.empty())
+ BOOST_CHECK_EQUAL(e.valid(),false);
+ else
+ BOOST_CHECK_EQUAL(e.valid(), true );
+ }
+
+ BOOST_CHECK_EQUAL( extents_type{}.valid() , false );
+
+ BOOST_CHECK_THROW( ublas::basic_extents<unsigned>({0,1}), std::length_error );
+ BOOST_CHECK_THROW( ublas::basic_extents<unsigned>({1,0,1}), std::length_error );
+
+}
+
+
+BOOST_FIXTURE_TEST_CASE(test_extents_product, fixture, *boost::unit_test::label("extents") *boost::unit_test::label("product"))
+{
+
+ auto e0 = extents[ 0].product(); // {}
+ auto e1 = extents[ 1].product(); // {1,1}
+ auto e2 = extents[ 2].product(); // {1,2}
+ auto e3 = extents[ 3].product(); // {2,1}
+ auto e4 = extents[ 4].product(); // {2,3}
+ auto e5 = extents[ 5].product(); // {2,3,1}
+ auto e6 = extents[ 6].product(); // {1,2,3}
+ auto e7 = extents[ 7].product(); // {1,1,2,3}
+ auto e8 = extents[ 8].product(); // {1,2,3,1,1}
+ auto e9 = extents[ 9].product(); // {4,2,3}
+ auto e10 = extents[10].product(); // {4,2,1,3}
+ auto e11 = extents[11].product(); // {4,2,1,3,1}
+ auto e12 = extents[12].product(); // {1,4,2,1,3,1}
+
+ BOOST_CHECK_EQUAL( e0 , 0 );
+ BOOST_CHECK_EQUAL( e1 , 1 );
+ BOOST_CHECK_EQUAL( e2 , 2 );
+ BOOST_CHECK_EQUAL( e3 , 2 );
+ BOOST_CHECK_EQUAL( e4 , 6 );
+ BOOST_CHECK_EQUAL( e5 , 6 );
+ BOOST_CHECK_EQUAL( e6 , 6 );
+ BOOST_CHECK_EQUAL( e7 , 6 );
+ BOOST_CHECK_EQUAL( e8 , 6 );
+ BOOST_CHECK_EQUAL( e9 , 24 );
+ BOOST_CHECK_EQUAL( e10, 24 );
+ BOOST_CHECK_EQUAL( e11, 24 );
+ BOOST_CHECK_EQUAL( e12, 24 );
+
+
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_functions.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_functions.cpp
new file mode 100644
index 00000000..64ecda39
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_functions.cpp
@@ -0,0 +1,453 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+// And we acknowledge the support from all contributors.
+
+
+#include <iostream>
+#include <algorithm>
+#include <boost/numeric/ublas/tensor.hpp>
+#include <boost/numeric/ublas/matrix.hpp>
+#include <boost/numeric/ublas/vector.hpp>
+
+#include <boost/test/unit_test.hpp>
+
+#include "utility.hpp"
+
+BOOST_AUTO_TEST_SUITE ( test_tensor_functions, * boost::unit_test::depends_on("test_tensor_contraction") )
+
+
+using test_types = zip<int,long,float,double,std::complex<float>>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
+
+//using test_types = zip<int>::with_t<boost::numeric::ublas::first_order>;
+
+
+struct fixture
+{
+ using extents_type = boost::numeric::ublas::shape;
+ fixture()
+ : extents {
+ extents_type{1,1}, // 1
+ extents_type{1,2}, // 2
+ extents_type{2,1}, // 3
+ extents_type{2,3}, // 4
+ extents_type{2,3,1}, // 5
+ extents_type{4,1,3}, // 6
+ extents_type{1,2,3}, // 7
+ extents_type{4,2,3}, // 8
+ extents_type{4,2,3,5}} // 9
+ {
+ }
+ std::vector<extents_type> extents;
+};
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_prod_vector, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type,layout_type>;
+ using vector_type = typename tensor_type::vector_type;
+
+
+ for(auto const& n : extents){
+
+ auto a = tensor_type(n, value_type{2});
+
+ for(auto m = 0u; m < n.size(); ++m){
+
+ auto b = vector_type (n[m], value_type{1} );
+
+ auto c = ublas::prod(a, b, m+1);
+
+ for(auto i = 0u; i < c.size(); ++i)
+ BOOST_CHECK_EQUAL( c[i] , value_type(n[m]) * a[i] );
+
+ }
+ }
+}
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_prod_matrix, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type,layout_type>;
+ using matrix_type = typename tensor_type::matrix_type;
+
+
+ for(auto const& n : extents) {
+
+ auto a = tensor_type(n, value_type{2});
+
+ for(auto m = 0u; m < n.size(); ++m){
+
+ auto b = matrix_type ( n[m], n[m], value_type{1} );
+
+ auto c = ublas::prod(a, b, m+1);
+
+ for(auto i = 0u; i < c.size(); ++i)
+ BOOST_CHECK_EQUAL( c[i] , value_type(n[m]) * a[i] );
+
+ }
+ }
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_prod_tensor_1, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type,layout_type>;
+
+ // left-hand and right-hand side have the
+ // the same number of elements
+
+ for(auto const& na : extents) {
+
+ auto a = tensor_type( na, value_type{2} );
+ auto b = tensor_type( na, value_type{3} );
+
+ auto const pa = a.rank();
+
+ // the number of contractions is changed.
+ for( auto q = 0ul; q <= pa; ++q) { // pa
+
+ auto phi = std::vector<std::size_t> ( q );
+
+ std::iota(phi.begin(), phi.end(), 1ul);
+
+ auto c = ublas::prod(a, b, phi);
+
+ auto acc = value_type(1);
+ for(auto i = 0ul; i < q; ++i)
+ acc *= a.extents().at(phi.at(i)-1);
+
+ for(auto i = 0ul; i < c.size(); ++i)
+ BOOST_CHECK_EQUAL( c[i] , acc * a[0] * b[0] );
+
+ }
+ }
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_prod_tensor_2, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type,layout_type>;
+
+
+ auto compute_factorial = [](auto const& p){
+ auto f = 1ul;
+ for(auto i = 1u; i <= p; ++i)
+ f *= i;
+ return f;
+ };
+
+ auto permute_extents = [](auto const& pi, auto const& na){
+ auto nb = na;
+ assert(pi.size() == na.size());
+ for(auto j = 0u; j < pi.size(); ++j)
+ nb[pi[j]-1] = na[j];
+ return nb;
+ };
+
+
+ // left-hand and right-hand side have the
+ // the same number of elements
+
+ for(auto const& na : extents) {
+
+ auto a = tensor_type( na, value_type{2} );
+ auto const pa = a.rank();
+
+
+ auto pi = std::vector<std::size_t>(pa);
+ auto fac = compute_factorial(pa);
+ std::iota( pi.begin(), pi.end(), 1 );
+
+ for(auto f = 0ul; f < fac; ++f)
+ {
+ auto nb = permute_extents( pi, na );
+ auto b = tensor_type( nb, value_type{3} );
+
+ // the number of contractions is changed.
+ for( auto q = 0ul; q <= pa; ++q) { // pa
+
+ auto phia = std::vector<std::size_t> ( q ); // concatenation for a
+ auto phib = std::vector<std::size_t> ( q ); // concatenation for b
+
+ std::iota(phia.begin(), phia.end(), 1ul);
+ std::transform( phia.begin(), phia.end(), phib.begin(),
+ [&pi] ( std::size_t i ) { return pi.at(i-1); } );
+
+ auto c = ublas::prod(a, b, phia, phib);
+
+ auto acc = value_type(1);
+ for(auto i = 0ul; i < q; ++i)
+ acc *= a.extents().at(phia.at(i)-1);
+
+ for(auto i = 0ul; i < c.size(); ++i)
+ BOOST_CHECK_EQUAL( c[i] , acc * a[0] * b[0] );
+
+ }
+
+ std::next_permutation(pi.begin(), pi.end());
+ }
+ }
+}
+
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_inner_prod, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type,layout_type>;
+
+
+ for(auto const& n : extents) {
+
+ auto a = tensor_type(n, value_type(2));
+ auto b = tensor_type(n, value_type(1));
+
+ auto c = ublas::inner_prod(a, b);
+ auto r = std::inner_product(a.begin(),a.end(), b.begin(),value_type(0));
+
+ BOOST_CHECK_EQUAL( c , r );
+
+ }
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_norm, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type,layout_type>;
+
+
+ for(auto const& n : extents) {
+
+ auto a = tensor_type(n);
+
+ auto one = value_type(1);
+ auto v = one;
+ for(auto& aa: a)
+ aa = v, v += one;
+
+
+ auto c = ublas::inner_prod(a, a);
+ auto r = std::inner_product(a.begin(),a.end(), a.begin(),value_type(0));
+
+ auto r2 = ublas::norm( (a+a) / 2 );
+
+ BOOST_CHECK_EQUAL( c , r );
+ BOOST_CHECK_EQUAL( std::sqrt( c ) , r2 );
+
+ }
+}
+
+
+BOOST_FIXTURE_TEST_CASE( test_tensor_real_imag_conj, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = float;
+ using complex_type = std::complex<value_type>;
+ using layout_type = ublas::first_order;
+
+ using tensor_complex_type = ublas::tensor<complex_type,layout_type>;
+ using tensor_type = ublas::tensor<value_type,layout_type>;
+
+ for(auto const& n : extents) {
+
+ auto a = tensor_type(n);
+ auto r0 = tensor_type(n);
+ auto r00 = tensor_complex_type(n);
+
+
+ auto one = value_type(1);
+ auto v = one;
+ for(auto& aa: a)
+ aa = v, v += one;
+
+ tensor_type b = (a+a) / value_type( 2 );
+ tensor_type r1 = ublas::real( (a+a) / value_type( 2 ) );
+ std::transform( b.begin(), b.end(), r0.begin(), [](auto const& l){ return std::real( l ); } );
+ BOOST_CHECK( r0 == r1 );
+
+ tensor_type r2 = ublas::imag( (a+a) / value_type( 2 ) );
+ std::transform( b.begin(), b.end(), r0.begin(), [](auto const& l){ return std::imag( l ); } );
+ BOOST_CHECK( r0 == r2 );
+
+ tensor_complex_type r3 = ublas::conj( (a+a) / value_type( 2 ) );
+ std::transform( b.begin(), b.end(), r00.begin(), [](auto const& l){ return std::conj( l ); } );
+ BOOST_CHECK( r00 == r3 );
+
+ }
+
+ for(auto const& n : extents) {
+
+
+
+
+ auto a = tensor_complex_type(n);
+
+ auto r00 = tensor_complex_type(n);
+ auto r0 = tensor_type(n);
+
+
+ auto one = complex_type(1,1);
+ auto v = one;
+ for(auto& aa: a)
+ aa = v, v = v + one;
+
+ tensor_complex_type b = (a+a) / complex_type( 2,2 );
+
+
+ tensor_type r1 = ublas::real( (a+a) / complex_type( 2,2 ) );
+ std::transform( b.begin(), b.end(), r0.begin(), [](auto const& l){ return std::real( l ); } );
+ BOOST_CHECK( r0 == r1 );
+
+ tensor_type r2 = ublas::imag( (a+a) / complex_type( 2,2 ) );
+ std::transform( b.begin(), b.end(), r0.begin(), [](auto const& l){ return std::imag( l ); } );
+ BOOST_CHECK( r0 == r2 );
+
+ tensor_complex_type r3 = ublas::conj( (a+a) / complex_type( 2,2 ) );
+ std::transform( b.begin(), b.end(), r00.begin(), [](auto const& l){ return std::conj( l ); } );
+ BOOST_CHECK( r00 == r3 );
+
+
+
+ }
+
+
+
+}
+
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_outer_prod, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type,layout_type>;
+
+ for(auto const& n1 : extents) {
+ auto a = tensor_type(n1, value_type(2));
+ for(auto const& n2 : extents) {
+
+ auto b = tensor_type(n2, value_type(1));
+ auto c = ublas::outer_prod(a, b);
+
+ for(auto const& cc : c)
+ BOOST_CHECK_EQUAL( cc , a[0]*b[0] );
+ }
+ }
+}
+
+
+
+template<class V>
+void init(std::vector<V>& a)
+{
+ auto v = V(1);
+ for(auto i = 0u; i < a.size(); ++i, ++v){
+ a[i] = v;
+ }
+}
+
+template<class V>
+void init(std::vector<std::complex<V>>& a)
+{
+ auto v = std::complex<V>(1,1);
+ for(auto i = 0u; i < a.size(); ++i){
+ a[i] = v;
+ v.real(v.real()+1);
+ v.imag(v.imag()+1);
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_trans, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type,layout_type>;
+
+ auto fak = [](auto const& p){
+ auto f = 1ul;
+ for(auto i = 1u; i <= p; ++i)
+ f *= i;
+ return f;
+ };
+
+ auto inverse = [](auto const& pi){
+ auto pi_inv = pi;
+ for(auto j = 0u; j < pi.size(); ++j)
+ pi_inv[pi[j]-1] = j+1;
+ return pi_inv;
+ };
+
+ for(auto const& n : extents)
+ {
+ auto const p = n.size();
+ auto const s = n.product();
+ auto aref = tensor_type(n);
+ auto v = value_type{};
+ for(auto i = 0u; i < s; ++i, v+=1)
+ aref[i] = v;
+ auto a = aref;
+
+
+ auto pi = std::vector<std::size_t>(p);
+ std::iota(pi.begin(), pi.end(), 1);
+ a = ublas::trans( a, pi );
+ BOOST_CHECK( a == aref );
+
+
+ auto const pfak = fak(p);
+ auto i = 0u;
+ for(; i < pfak-1; ++i) {
+ std::next_permutation(pi.begin(), pi.end());
+ a = ublas::trans( a, pi );
+ }
+ std::next_permutation(pi.begin(), pi.end());
+ for(; i > 0; --i) {
+ std::prev_permutation(pi.begin(), pi.end());
+ auto pi_inv = inverse(pi);
+ a = ublas::trans( a, pi_inv );
+ }
+
+ BOOST_CHECK( a == aref );
+
+ }
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_multi_index.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_multi_index.cpp
new file mode 100644
index 00000000..d6448de2
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_multi_index.cpp
@@ -0,0 +1,146 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+
+#include <iostream>
+#include <algorithm>
+#include <complex>
+#include <boost/numeric/ublas/tensor.hpp>
+#include <boost/numeric/ublas/tensor/multi_index.hpp>
+
+
+#include <boost/test/unit_test.hpp>
+
+#include "utility.hpp"
+
+
+BOOST_AUTO_TEST_SUITE ( test_multi_index )
+
+
+using test_types = zip<int,long,float,double,std::complex<float>>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
+
+
+BOOST_AUTO_TEST_CASE ( test_index_classes )
+{
+ using namespace boost::numeric::ublas::index;
+
+
+ BOOST_CHECK_EQUAL ( _a.value , 1 ) ;
+ BOOST_CHECK_EQUAL ( _b.value , 2 ) ;
+ BOOST_CHECK_EQUAL ( _c.value , 3 ) ;
+ BOOST_CHECK_EQUAL ( _d.value , 4 ) ;
+ BOOST_CHECK_EQUAL ( _e.value , 5 ) ;
+ BOOST_CHECK_EQUAL ( _f.value , 6 ) ;
+ BOOST_CHECK_EQUAL ( _g.value , 7 ) ;
+ BOOST_CHECK_EQUAL ( _h.value , 8 ) ;
+ BOOST_CHECK_EQUAL ( _i.value , 9 ) ;
+ BOOST_CHECK_EQUAL ( _j.value , 10 ) ;
+ BOOST_CHECK_EQUAL ( _k.value , 11 ) ;
+ BOOST_CHECK_EQUAL ( _l.value , 12 ) ;
+ BOOST_CHECK_EQUAL ( _m.value , 13 ) ;
+ BOOST_CHECK_EQUAL ( _n.value , 14 ) ;
+ BOOST_CHECK_EQUAL ( _o.value , 15 ) ;
+ BOOST_CHECK_EQUAL ( _p.value , 16 ) ;
+ BOOST_CHECK_EQUAL ( _q.value , 17 ) ;
+ BOOST_CHECK_EQUAL ( _r.value , 18 ) ;
+ BOOST_CHECK_EQUAL ( _s.value , 19 ) ;
+ BOOST_CHECK_EQUAL ( _t.value , 20 ) ;
+ BOOST_CHECK_EQUAL ( _u.value , 21 ) ;
+ BOOST_CHECK_EQUAL ( _v.value , 22 ) ;
+ BOOST_CHECK_EQUAL ( _w.value , 23 ) ;
+ BOOST_CHECK_EQUAL ( _x.value , 24 ) ;
+ BOOST_CHECK_EQUAL ( _y.value , 25 ) ;
+ BOOST_CHECK_EQUAL ( _z.value , 26 ) ;
+
+}
+
+BOOST_AUTO_TEST_CASE ( test_multi_index_class_construction )
+{
+ using namespace boost::numeric::ublas;
+ using namespace boost::numeric::ublas::index;
+
+
+ {
+ multi_index<2> ind(_a, _b);
+
+ BOOST_CHECK_EQUAL ( get<0>( ind ), 1 ) ;
+ BOOST_CHECK_EQUAL ( get<1>( ind ), 2 ) ;
+ }
+
+
+ {
+ multi_index<2> ind(_d,_c);
+
+ BOOST_CHECK_EQUAL ( ind[0] , 4 ) ;
+ BOOST_CHECK_EQUAL ( ind[1] , 3 ) ;
+ }
+}
+
+
+BOOST_AUTO_TEST_CASE_TEMPLATE( test_tensor_multi_index_class_generation, value, test_types )
+{
+ using namespace boost::numeric::ublas;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = tensor<value_type,layout_type>;
+
+ auto t = std::make_tuple (
+ index::_a, // 0
+ index::_b, // 1
+ index::_c, // 2
+ index::_d, // 3
+ index::_e // 4
+ );
+
+ {
+ auto a = tensor_type(shape{2,3}, value_type{2});
+ auto a_ind = a( std::get<0>(t), std::get<2>(t) );
+
+ BOOST_CHECK_EQUAL ( std::addressof( a_ind.first ), std::addressof( a ) ) ;
+
+ BOOST_CHECK_EQUAL (std::get<0>(a_ind.second)(), index::_a() ) ;
+ BOOST_CHECK_EQUAL (std::get<1>(a_ind.second)(), index::_c() ) ;
+ }
+
+ {
+ auto a = tensor_type(shape{2,3}, value_type{2});
+ auto a_ind = a( std::get<2>(t), std::get<0>(t) );
+
+ BOOST_CHECK_EQUAL ( std::addressof( a_ind.first ), std::addressof( a ) ) ;
+
+ BOOST_CHECK_EQUAL (std::get<0>(a_ind.second)(), index::_c() ) ;
+ BOOST_CHECK_EQUAL (std::get<1>(a_ind.second)(), index::_a() ) ;
+ }
+
+ {
+ auto a = tensor_type(shape{2,3}, value_type{2});
+ auto a_ind = a( std::get<2>(t), std::get<3>(t) );
+
+ BOOST_CHECK_EQUAL (std::addressof( a_ind.first ), std::addressof( a ) ) ;
+
+ BOOST_CHECK_EQUAL (std::get<0>(a_ind.second)(), index::_c() ) ;
+ BOOST_CHECK_EQUAL (std::get<1>(a_ind.second)(), index::_d() ) ;
+ }
+
+ {
+ auto a = tensor_type(shape{2,3,4}, value_type{2});
+ auto a_ind = a( std::get<2>(t), std::get<3>(t), std::get<0>(t) );
+
+ BOOST_CHECK_EQUAL (std::addressof( a_ind.first ), std::addressof( a ) ) ;
+
+ BOOST_CHECK_EQUAL (std::get<0>(a_ind.second)(), index::_c() ) ;
+ BOOST_CHECK_EQUAL (std::get<1>(a_ind.second)(), index::_d() ) ;
+ BOOST_CHECK_EQUAL (std::get<2>(a_ind.second)(), index::_a() ) ;
+ }
+
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_multi_index_utility.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_multi_index_utility.cpp
new file mode 100644
index 00000000..2f31a58f
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_multi_index_utility.cpp
@@ -0,0 +1,564 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The author gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+
+
+#include <boost/numeric/ublas/tensor.hpp>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE ( test_multi_index_utility )
+
+
+BOOST_AUTO_TEST_CASE ( test_multi_index_has_index )
+{
+ using namespace boost::numeric::ublas;
+ using namespace boost::numeric::ublas::index;
+
+ {
+ constexpr auto tuple = std::tuple<>{};
+ constexpr auto has_a = has_index<decltype(_a),decltype(tuple)>::value;
+ constexpr auto has_b = has_index<decltype(_b),decltype(tuple)>::value;
+ BOOST_CHECK( !has_a );
+ BOOST_CHECK( !has_b );
+ }
+
+
+ {
+ constexpr auto tuple = std::make_tuple(_a);
+ constexpr auto has_a = has_index<decltype(_a),decltype(tuple)>::value;
+ constexpr auto has_b = has_index<decltype(_b),decltype(tuple)>::value;
+ BOOST_CHECK( has_a );
+ BOOST_CHECK( !has_b );
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_a,_b,_,_c,_d);
+ constexpr auto has_a = has_index<decltype(_a),decltype(tuple)>::value;
+ constexpr auto has_b = has_index<decltype(_b),decltype(tuple)>::value;
+ constexpr auto has_c = has_index<decltype(_c),decltype(tuple)>::value;
+ constexpr auto has_d = has_index<decltype(_d),decltype(tuple)>::value;
+ constexpr auto has_e = has_index<decltype(_e),decltype(tuple)>::value;
+ constexpr auto has__ = has_index<decltype( _),decltype(tuple)>::value;
+ BOOST_CHECK( has_a );
+ BOOST_CHECK( has_b );
+ BOOST_CHECK( has_c );
+ BOOST_CHECK( has_d );
+ BOOST_CHECK( !has_e );
+ BOOST_CHECK( has__ );
+ }
+}
+
+
+
+BOOST_AUTO_TEST_CASE ( test_multi_index_valid )
+{
+ using namespace boost::numeric::ublas;
+ using namespace boost::numeric::ublas::index;
+
+ {
+ constexpr auto tuple = std::tuple<>{};
+ constexpr auto valid = valid_multi_index<decltype(tuple)>::value;
+ BOOST_CHECK( valid );
+ }
+
+
+ {
+ constexpr auto tuple = std::make_tuple(_a);
+ constexpr auto valid = valid_multi_index<decltype(tuple)>::value;
+ BOOST_CHECK( valid );
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_a,_,_b);
+ constexpr auto valid = valid_multi_index<decltype(tuple)>::value;
+ BOOST_CHECK( valid );
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_a,_,_b,_b);
+ constexpr auto valid = valid_multi_index<decltype(tuple)>::value;
+ BOOST_CHECK( !valid );
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_c,_a,_,_b,_b);
+ constexpr auto valid = valid_multi_index<decltype(tuple)>::value;
+ BOOST_CHECK( !valid );
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_c,_a,_,_b);
+ constexpr auto valid = valid_multi_index<decltype(tuple)>::value;
+ BOOST_CHECK( valid );
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_,_c,_a,_,_b);
+ constexpr auto valid = valid_multi_index<decltype(tuple)>::value;
+ BOOST_CHECK( valid );
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_,_c,_a,_,_b,_);
+ constexpr auto valid = valid_multi_index<decltype(tuple)>::value;
+ BOOST_CHECK( valid );
+ }
+}
+
+
+
+
+
+BOOST_AUTO_TEST_CASE ( test_multi_index_number_equal_indices )
+{
+ using namespace boost::numeric::ublas;
+ using namespace boost::numeric::ublas::index;
+
+ {
+ constexpr auto lhs = std::tuple<>{};
+ constexpr auto rhs = std::tuple<>{};
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 0 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_a);
+ constexpr auto rhs = std::tuple<>{};
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 0 );
+ }
+
+ {
+ constexpr auto lhs = std::tuple<>{};
+ constexpr auto rhs = std::make_tuple(_a);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 0 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b);
+ constexpr auto rhs = std::make_tuple(_a);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 0 );
+ }
+
+
+
+ {
+ constexpr auto lhs = std::make_tuple(_a);
+ constexpr auto rhs = std::make_tuple(_a);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 1 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_a,_b);
+ constexpr auto rhs = std::make_tuple(_a);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 1 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b);
+ constexpr auto rhs = std::make_tuple(_a,_b);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 1 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_a);
+ constexpr auto rhs = std::make_tuple(_a);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 1 );
+ }
+
+
+
+ {
+ constexpr auto lhs = std::make_tuple(_a,_b);
+ constexpr auto rhs = std::make_tuple(_a,_b);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 2 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a);
+ constexpr auto rhs = std::make_tuple(_a,_b);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 2 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a,_c);
+ constexpr auto rhs = std::make_tuple(_a,_b);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 2 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a,_c);
+ constexpr auto rhs = std::make_tuple(_a,_b,_d);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 2 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a,_d);
+ constexpr auto rhs = std::make_tuple(_a,_b,_d);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 3 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a,_d);
+ constexpr auto rhs = std::make_tuple(_a,_b,_d,_);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 3 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a,_d,_);
+ constexpr auto rhs = std::make_tuple(_a,_b,_d,_);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 3 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a,_d,_);
+ constexpr auto rhs = std::make_tuple( _,_b,_d,_);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 2 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_,_a,_d,_);
+ constexpr auto rhs = std::make_tuple(_,_b,_d,_);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 1 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_,_a,_d,_);
+ constexpr auto rhs = std::make_tuple(_,_b,_d,_,_);
+ constexpr auto num = number_equal_indexes<decltype(lhs), decltype(rhs)>::value;
+ BOOST_CHECK_EQUAL( num, 1 );
+ }
+}
+
+
+
+
+
+
+
+BOOST_AUTO_TEST_CASE ( test_multi_index_index_position )
+{
+ using namespace boost::numeric::ublas;
+ using namespace boost::numeric::ublas::index;
+
+ {
+ constexpr auto tuple = std::tuple<>{};
+ constexpr auto ind = index_position<decltype(_),decltype(tuple)>::value;
+ BOOST_CHECK_EQUAL(ind,0);
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_);
+ constexpr auto ind = index_position<decltype(_),decltype(tuple)>::value;
+ BOOST_CHECK_EQUAL(ind,0);
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_);
+ constexpr auto ind = index_position<decltype(_a),decltype(tuple)>::value;
+ BOOST_CHECK_EQUAL(ind,1);
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_,_a);
+ constexpr auto ind = index_position<decltype(_),decltype(tuple)>::value;
+ BOOST_CHECK_EQUAL(ind,0);
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_,_a);
+ constexpr auto ind = index_position<decltype(_a),decltype(tuple)>::value;
+ BOOST_CHECK_EQUAL(ind,1);
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_,_a);
+ constexpr auto ind = index_position<decltype(_b),decltype(tuple)>::value;
+ BOOST_CHECK_EQUAL(ind,2);
+ }
+
+
+
+
+ {
+ constexpr auto tuple = std::make_tuple(_c,_,_a);
+ constexpr auto ind = index_position<decltype(_c),decltype(tuple)>::value;
+ BOOST_CHECK_EQUAL(ind,0);
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_c,_,_a,_);
+ constexpr auto ind = index_position<decltype(_),decltype(tuple)>::value;
+ BOOST_CHECK_EQUAL(ind,1);
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_c,_,_a);
+ constexpr auto ind = index_position<decltype(_a),decltype(tuple)>::value;
+ BOOST_CHECK_EQUAL(ind,2);
+ }
+
+ {
+ constexpr auto tuple = std::make_tuple(_c,_,_a);
+ constexpr auto ind = index_position<decltype(_d),decltype(tuple)>::value;
+ BOOST_CHECK_EQUAL(ind,3);
+ }
+
+}
+
+
+
+
+
+
+
+
+BOOST_AUTO_TEST_CASE ( test_multi_index_index_position_pairs )
+{
+ using namespace boost::numeric::ublas;
+ using namespace boost::numeric::ublas::index;
+
+ {
+ constexpr auto lhs = std::tuple<>{};
+ constexpr auto rhs = std::tuple<>{};
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_CHECK_EQUAL(array.size(), 0ul );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_a);
+ constexpr auto rhs = std::tuple<>{};
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_CHECK_EQUAL(array.size(), 0ul );
+ }
+
+ {
+ constexpr auto lhs = std::tuple<>{};
+ constexpr auto rhs = std::make_tuple(_a);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_CHECK_EQUAL(array.size(), 0ul );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b);
+ constexpr auto rhs = std::make_tuple(_a);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_CHECK_EQUAL(array.size(), 0ul );
+ }
+
+
+
+ {
+ constexpr auto lhs = std::make_tuple(_a);
+ constexpr auto rhs = std::make_tuple(_a);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 1ul );
+ BOOST_CHECK_EQUAL(array[0].first , 0 );
+ BOOST_CHECK_EQUAL(array[0].second, 0 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_a,_b);
+ constexpr auto rhs = std::make_tuple(_a);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 1ul );
+ BOOST_CHECK_EQUAL(array[0].first , 0 );
+ BOOST_CHECK_EQUAL(array[0].second, 0 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b);
+ constexpr auto rhs = std::make_tuple(_a,_b);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 1ul );
+ BOOST_CHECK_EQUAL(array[0].first , 0 );
+ BOOST_CHECK_EQUAL(array[0].second, 1 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_a);
+ constexpr auto rhs = std::make_tuple(_a);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 1ul );
+ BOOST_CHECK_EQUAL(array[0].first , 0 );
+ BOOST_CHECK_EQUAL(array[0].second, 0 );
+ }
+
+
+
+ {
+ constexpr auto lhs = std::make_tuple(_a,_b);
+ constexpr auto rhs = std::make_tuple(_a,_b);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 2ul );
+ BOOST_CHECK_EQUAL(array[0].first , 0 );
+ BOOST_CHECK_EQUAL(array[0].second, 0 );
+ BOOST_CHECK_EQUAL(array[1].first , 1 );
+ BOOST_CHECK_EQUAL(array[1].second, 1 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a);
+ constexpr auto rhs = std::make_tuple(_a,_b);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 2ul );
+ BOOST_CHECK_EQUAL(array[0].first , 0 );
+ BOOST_CHECK_EQUAL(array[0].second, 1 );
+ BOOST_CHECK_EQUAL(array[1].first , 1 );
+ BOOST_CHECK_EQUAL(array[1].second, 0 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a,_c);
+ constexpr auto rhs = std::make_tuple(_a,_b);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 2ul );
+ BOOST_CHECK_EQUAL(array[0].first , 0 );
+ BOOST_CHECK_EQUAL(array[0].second, 1 );
+ BOOST_CHECK_EQUAL(array[1].first , 1 );
+ BOOST_CHECK_EQUAL(array[1].second, 0 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a,_c);
+ constexpr auto rhs = std::make_tuple(_a,_b,_d);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 2ul );
+ BOOST_CHECK_EQUAL(array[0].first , 0 );
+ BOOST_CHECK_EQUAL(array[0].second, 1 );
+ BOOST_CHECK_EQUAL(array[1].first , 1 );
+ BOOST_CHECK_EQUAL(array[1].second, 0 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a,_d);
+ constexpr auto rhs = std::make_tuple(_a,_b,_d);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 3ul );
+ BOOST_CHECK_EQUAL(array[0].first , 0 );
+ BOOST_CHECK_EQUAL(array[0].second, 1 );
+ BOOST_CHECK_EQUAL(array[1].first , 1 );
+ BOOST_CHECK_EQUAL(array[1].second, 0 );
+ BOOST_CHECK_EQUAL(array[2].first , 2 );
+ BOOST_CHECK_EQUAL(array[2].second, 2 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a,_d);
+ constexpr auto rhs = std::make_tuple(_a,_b,_d,_);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 3ul );
+ BOOST_CHECK_EQUAL(array[0].first , 0 );
+ BOOST_CHECK_EQUAL(array[0].second, 1 );
+ BOOST_CHECK_EQUAL(array[1].first , 1 );
+ BOOST_CHECK_EQUAL(array[1].second, 0 );
+ BOOST_CHECK_EQUAL(array[2].first , 2 );
+ BOOST_CHECK_EQUAL(array[2].second, 2 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a,_d,_);
+ constexpr auto rhs = std::make_tuple(_a,_b,_d,_);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 3ul );
+ BOOST_CHECK_EQUAL(array[0].first , 0 );
+ BOOST_CHECK_EQUAL(array[0].second, 1 );
+ BOOST_CHECK_EQUAL(array[1].first , 1 );
+ BOOST_CHECK_EQUAL(array[1].second, 0 );
+ BOOST_CHECK_EQUAL(array[2].first , 2 );
+ BOOST_CHECK_EQUAL(array[2].second, 2 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_b,_a,_d,_);
+ constexpr auto rhs = std::make_tuple( _,_b,_d,_);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 2ul );
+ BOOST_CHECK_EQUAL(array[0].first , 0 );
+ BOOST_CHECK_EQUAL(array[0].second, 1 );
+ BOOST_CHECK_EQUAL(array[1].first , 2 );
+ BOOST_CHECK_EQUAL(array[1].second, 2 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_,_a,_d,_);
+ constexpr auto rhs = std::make_tuple(_,_b,_d,_);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 1ul );
+ BOOST_CHECK_EQUAL(array[0].first , 2 );
+ BOOST_CHECK_EQUAL(array[0].second, 2 );
+ }
+
+ {
+ constexpr auto lhs = std::make_tuple(_,_a,_d,_);
+ constexpr auto rhs = std::make_tuple(_,_b,_d,_,_);
+ auto array = index_position_pairs(lhs, rhs);
+ BOOST_ASSERT(array.size() == 1ul );
+ BOOST_CHECK_EQUAL(array[0].first , 2 );
+ BOOST_CHECK_EQUAL(array[0].second, 2 );
+ }
+}
+
+
+
+BOOST_AUTO_TEST_CASE ( test_multi_index_array_to_vector )
+{
+ using namespace boost::numeric::ublas;
+ using namespace boost::numeric::ublas::index;
+
+ auto check = [](auto const& lhs, auto const& rhs)
+ {
+ auto array = index_position_pairs(lhs, rhs);
+
+ auto vector_pair = array_to_vector( array );
+
+ BOOST_CHECK_EQUAL(vector_pair.first .size(), array.size() );
+ BOOST_CHECK_EQUAL(vector_pair.second.size(), array.size() );
+
+ for(auto i = 0ul; i < array.size(); ++i)
+ {
+ BOOST_CHECK_EQUAL(vector_pair.first [i], array[i].first +1 );
+ BOOST_CHECK_EQUAL(vector_pair.second[i], array[i].second+1 );
+ }
+
+ };
+
+ check(std::tuple<>{} , std::tuple<>{});
+ check(std::make_tuple(_a) , std::tuple<>{});
+ check(std::tuple<>{} , std::make_tuple(_a));
+ check(std::make_tuple(_a) , std::make_tuple(_b));
+ check(std::make_tuple(_a) , std::make_tuple(_a));
+ check(std::make_tuple(_a,_b), std::make_tuple(_a));
+ check(std::make_tuple(_a) , std::make_tuple(_a,_b));
+ check(std::make_tuple(_a,_b), std::make_tuple(_a,_b));
+ check(std::make_tuple(_b,_a), std::make_tuple(_a,_b));
+ check(std::make_tuple(_b,_a,_c), std::make_tuple(_a,_b,_d));
+}
+
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_multiplication.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_multiplication.cpp
new file mode 100644
index 00000000..2f1570d1
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_multiplication.cpp
@@ -0,0 +1,491 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+
+
+#include <iostream>
+#include <algorithm>
+#include <vector>
+
+#include <boost/numeric/ublas/tensor/multiplication.hpp>
+#include <boost/numeric/ublas/tensor/extents.hpp>
+#include <boost/numeric/ublas/tensor/strides.hpp>
+#include "utility.hpp"
+
+#include <boost/test/unit_test.hpp>
+
+
+BOOST_AUTO_TEST_SUITE (test_tensor_contraction)
+
+
+using test_types = zip<int,long,float,double,std::complex<float>>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
+
+//using test_types = zip<int>::with_t<boost::numeric::ublas::first_order>;
+
+
+struct fixture
+{
+ using extents_type = boost::numeric::ublas::shape;
+ fixture()
+ : extents {
+ extents_type{1,1}, // 1
+ extents_type{1,2}, // 2
+ extents_type{2,1}, // 3
+ extents_type{2,3}, // 4
+ extents_type{5,4}, // 5
+ extents_type{2,3,1}, // 6
+ extents_type{4,1,3}, // 7
+ extents_type{1,2,3}, // 8
+ extents_type{4,2,3}, // 9
+ extents_type{4,2,3,5}} // 10
+ {
+ }
+ std::vector<extents_type> extents;
+};
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_mtv, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using strides_type = ublas::strides<layout_type>;
+ using vector_type = std::vector<value_type>;
+ using extents_type = ublas::shape;
+ using extents_type_base = typename extents_type::base_type;
+ using size_type = typename extents_type_base::value_type;
+
+
+ for(auto const& na : extents) {
+
+ if(na.size() > 2)
+ continue;
+
+ auto a = vector_type(na.product(), value_type{2});
+ auto wa = strides_type(na);
+ for(auto m = 0u; m < na.size(); ++m){
+ auto nb = extents_type {na[m],1};
+ auto wb = strides_type (nb);
+ auto b = vector_type (nb.product(), value_type{1} );
+
+ auto nc_base = extents_type_base(std::max(na.size()-1, size_type{2}), 1);
+
+ for(auto i = 0u, j = 0u; i < na.size(); ++i)
+ if(i != m)
+ nc_base[j++] = na[i];
+
+ auto nc = extents_type (nc_base);
+ auto wc = strides_type (nc);
+ auto c = vector_type (nc.product(), value_type{0});
+
+ ublas::detail::recursive::mtv(
+ size_type(m),
+ c.data(), nc.data(), wc.data(),
+ a.data(), na.data(), wa.data(),
+ b.data());
+
+
+ for(auto i = 0u; i < c.size(); ++i)
+ BOOST_CHECK_EQUAL( c[i] , value_type(na[m]) * a[i] );
+
+ }
+ }
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_mtm, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using strides_type = ublas::strides<layout_type>;
+ using vector_type = std::vector<value_type>;
+ using extents_type = ublas::shape;
+ // using extents_type_base = typename extents_type::base_type;
+
+
+ for(auto const& na : extents) {
+
+ if(na.size() != 2)
+ continue;
+
+ auto a = vector_type (na.product(), value_type{2});
+ auto wa = strides_type (na);
+
+ auto nb = extents_type {na[1],na[0]};
+ auto wb = strides_type (nb);
+ auto b = vector_type (nb.product(), value_type{1} );
+
+ auto nc = extents_type {na[0],nb[1]};
+auto wc = strides_type (nc);
+auto c = vector_type (nc.product());
+
+
+ublas::detail::recursive::mtm(
+ c.data(), nc.data(), wc.data(),
+ a.data(), na.data(), wa.data(),
+ b.data(), nb.data(), wb.data());
+
+
+for(auto i = 0u; i < c.size(); ++i)
+BOOST_CHECK_EQUAL( c[i] , value_type(na[1]) * a[0] );
+
+
+}
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ttv, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using strides_type = ublas::strides<layout_type>;
+ using vector_type = std::vector<value_type>;
+ using extents_type = ublas::shape;
+ using extents_type_base = typename extents_type::base_type;
+ using size_type = typename extents_type_base::value_type;
+
+
+ for(auto const& na : extents) {
+
+ auto a = vector_type(na.product(), value_type{2});
+ auto wa = strides_type(na);
+ for(auto m = 0u; m < na.size(); ++m){
+ auto b = vector_type (na[m], value_type{1} );
+ auto nb = extents_type {na[m],1};
+ auto wb = strides_type (nb);
+
+ auto nc_base = extents_type_base(std::max(na.size()-1, size_type(2)),1);
+
+ for(auto i = 0ul, j = 0ul; i < na.size(); ++i)
+ if(i != m)
+ nc_base[j++] = na[i];
+
+ auto nc = extents_type (nc_base);
+ auto wc = strides_type (nc);
+ auto c = vector_type (nc.product(), value_type{0});
+
+ ublas::ttv(size_type(m+1), na.size(),
+ c.data(), nc.data(), wc.data(),
+ a.data(), na.data(), wa.data(),
+ b.data(), nb.data(), wb.data());
+
+
+ for(auto i = 0u; i < c.size(); ++i)
+ BOOST_CHECK_EQUAL( c[i] , value_type(na[m]) * a[i] );
+
+ }
+ }
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ttm, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using strides_type = ublas::strides<layout_type>;
+ using vector_type = std::vector<value_type>;
+ using extents_type = ublas::shape;
+ using size_type = typename extents_type::value_type;
+
+
+ for(auto const& na : extents) {
+
+ auto a = vector_type(na.product(), value_type{2});
+ auto wa = strides_type(na);
+ for(auto m = 0u; m < na.size(); ++m){
+ auto nb = extents_type {na[m], na[m] };
+ auto b = vector_type (nb.product(), value_type{1} );
+ auto wb = strides_type (nb);
+
+
+ auto nc = na;
+ auto wc = strides_type (nc);
+ auto c = vector_type (nc.product(), value_type{0});
+
+ ublas::ttm(size_type(m+1), na.size(),
+ c.data(), nc.data(), wc.data(),
+ a.data(), na.data(), wa.data(),
+ b.data(), nb.data(), wb.data());
+
+ for(auto i = 0u; i < c.size(); ++i)
+ BOOST_CHECK_EQUAL( c[i] , value_type(na[m]) * a[i] );
+
+ }
+ }
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ttt_permutation, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using strides_type = ublas::strides<layout_type>;
+ using vector_type = std::vector<value_type>;
+ using extents_type = ublas::shape;
+ using size_type = typename strides_type::value_type;
+
+
+ auto compute_factorial = [](auto const& p){
+ auto f = 1ul;
+ for(auto i = 1u; i <= p; ++i)
+ f *= i;
+ return f;
+ };
+
+
+ auto compute_inverse_permutation = [](auto const& pi){
+ auto pi_inv = pi;
+ for(auto j = 0u; j < pi.size(); ++j)
+ pi_inv[pi[j]-1] = j+1;
+ return pi_inv;
+ };
+
+ auto permute_extents = [](auto const& pi, auto const& na){
+ auto nb = na;
+ assert(pi.size() == na.size());
+ for(auto j = 0u; j < pi.size(); ++j)
+ nb[j] = na[pi[j]-1];
+ return nb;
+ };
+
+
+ // left-hand and right-hand side have the
+ // the same number of elements
+
+ // computing the inner product with
+ // different permutation tuples for
+ // right-hand side
+
+ for(auto const& na : extents) {
+
+ auto wa = strides_type(na);
+ auto a = vector_type(na.product(), value_type{2});
+ auto pa = na.size();
+ auto pia = std::vector<size_type>(pa);
+ std::iota( pia.begin(), pia.end(), 1 );
+
+ auto pib = pia;
+ auto pib_inv = compute_inverse_permutation(pib);
+
+ auto f = compute_factorial(pa);
+
+ // for the number of possible permutations
+ // only permutation tuple pib is changed.
+ for(auto i = 0u; i < f; ++i) {
+
+ auto nb = permute_extents( pib, na );
+ auto wb = strides_type(nb);
+ auto b = vector_type(nb.product(), value_type{3});
+ auto pb = nb.size();
+
+ // the number of contractions is changed.
+ for( auto q = size_type(0); q <= pa; ++q) {
+
+ auto r = pa - q;
+ auto s = pb - q;
+
+ auto pc = r+s > 0 ? std::max(r+s,size_type(2)) : size_type(2);
+
+ auto nc_base = std::vector<size_type>( pc , 1 );
+
+ for(auto i = 0u; i < r; ++i)
+ nc_base[ i ] = na[ pia[i]-1 ];
+
+ for(auto i = 0u; i < s; ++i)
+ nc_base[ r + i ] = nb[ pib_inv[i]-1 ];
+
+ auto nc = extents_type ( nc_base );
+ auto wc = strides_type ( nc );
+ auto c = vector_type ( nc.product(), value_type(0) );
+
+ ublas::ttt(pa,pb,q,
+ pia.data(), pib_inv.data(),
+ c.data(), nc.data(), wc.data(),
+ a.data(), na.data(), wa.data(),
+ b.data(), nb.data(), wb.data());
+
+
+ auto acc = value_type(1);
+ for(auto i = r; i < pa; ++i)
+ acc *= value_type(na[pia[i]-1]);
+
+ for(auto i = 0ul; i < c.size(); ++i)
+ BOOST_CHECK_EQUAL( c[i] , acc * a[0] * b[0] );
+
+ }
+
+ std::next_permutation(pib.begin(), pib.end());
+ pib_inv = compute_inverse_permutation(pib);
+ }
+ }
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ttt, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using strides_type = ublas::strides<layout_type>;
+ using vector_type = std::vector<value_type>;
+ using extents_type = ublas::shape;
+ using size_type = typename strides_type::value_type;
+
+ // left-hand and right-hand side have the
+ // the same number of elements
+
+ // computing the inner product with
+ // different permutation tuples for
+ // right-hand side
+
+ for(auto const& na : extents) {
+
+ auto wa = strides_type(na);
+ auto a = vector_type(na.product(), value_type{2});
+ auto pa = na.size();
+
+ auto nb = na;
+ auto wb = strides_type(nb);
+ auto b = vector_type(nb.product(), value_type{3});
+ auto pb = nb.size();
+
+ // std::cout << "na = ";
+ // std::copy(na.begin(), na.end(), std::ostream_iterator<size_type>(std::cout, " "));
+ // std::cout << std::endl;
+
+ // std::cout << "nb = ";
+ // std::copy(nb.begin(), nb.end(), std::ostream_iterator<size_type>(std::cout, " "));
+ // std::cout << std::endl;
+
+
+ // the number of contractions is changed.
+ for( auto q = size_type(0); q <= pa; ++q) { // pa
+
+ auto r = pa - q;
+ auto s = pb - q;
+
+ auto pc = r+s > 0 ? std::max(r+s, size_type(2)) : size_type(2);
+
+ auto nc_base = std::vector<size_type>( pc , 1 );
+
+ for(auto i = 0u; i < r; ++i)
+ nc_base[ i ] = na[ i ];
+
+ for(auto i = 0u; i < s; ++i)
+ nc_base[ r + i ] = nb[ i ];
+
+ auto nc = extents_type ( nc_base );
+ auto wc = strides_type ( nc );
+ auto c = vector_type ( nc.product(), value_type{0} );
+
+ // std::cout << "nc = ";
+ // std::copy(nc.begin(), nc.end(), std::ostream_iterator<size_type>(std::cout, " "));
+ // std::cout << std::endl;
+
+ ublas::ttt(pa,pb,q,
+ c.data(), nc.data(), wc.data(),
+ a.data(), na.data(), wa.data(),
+ b.data(), nb.data(), wb.data());
+
+
+ auto acc = value_type(1);
+ for(auto i = r; i < pa; ++i)
+ acc *= value_type(na[i]);
+
+ for(auto i = 0u; i < c.size(); ++i)
+ BOOST_CHECK_EQUAL( c[i] , acc * a[0] * b[0] );
+
+ }
+
+ }
+}
+
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_inner, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using strides_type = ublas::strides<layout_type>;
+ using vector_type = std::vector<value_type>;
+
+
+ for(auto const& n : extents) {
+
+ auto a = vector_type(n.product(), value_type{2});
+ auto b = vector_type(n.product(), value_type{3});
+ auto w = strides_type(n);
+
+ auto c = ublas::inner(n.size(), n.data(), a.data(), w.data(), b.data(), w.data(), value_type(0));
+ auto cref = std::inner_product(a.begin(), a.end(), b.begin(), value_type(0));
+
+
+ BOOST_CHECK_EQUAL( c , cref );
+
+ }
+
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_outer, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using extents_type = ublas::shape;
+ using strides_type = ublas::strides<layout_type>;
+ using vector_type = std::vector<value_type>;
+
+
+ for(auto const& na : extents) {
+
+ auto a = vector_type(na.product(), value_type{2});
+ auto wa = strides_type(na);
+
+ for(auto const& nb : extents) {
+
+ auto b = vector_type(nb.product(), value_type{3});
+ auto wb = strides_type(nb);
+
+ auto c = vector_type(nb.product()*na.product());
+ auto nc = typename extents_type::base_type(na.size()+nb.size());
+
+ for(auto i = 0u; i < na.size(); ++i)
+ nc[i] = na[i];
+ for(auto i = 0u; i < nb.size(); ++i)
+ nc[i+na.size()] = nb[i];
+
+ auto wc = strides_type(extents_type(nc));
+
+ ublas::outer(c.data(), nc.size(), nc.data(), wc.data(),
+ a.data(), na.size(), na.data(), wa.data(),
+ b.data(), nb.size(), nb.data(), wb.data());
+
+ for(auto const& cc : c)
+ BOOST_CHECK_EQUAL( cc , a[0]*b[0] );
+ }
+
+ }
+
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_operators_arithmetic.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_operators_arithmetic.cpp
new file mode 100644
index 00000000..8bab7619
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_operators_arithmetic.cpp
@@ -0,0 +1,267 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+
+
+
+#include <boost/numeric/ublas/tensor.hpp>
+
+#include <boost/test/unit_test.hpp>
+#include <boost/multiprecision/cpp_bin_float.hpp>
+#include "utility.hpp"
+
+
+using double_extended = boost::multiprecision::cpp_bin_float_double_extended;
+
+using test_types = zip<int,long,float,double,double_extended>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
+
+struct fixture
+{
+ using extents_type = boost::numeric::ublas::basic_extents<std::size_t>;
+ fixture()
+ : extents{
+ extents_type{}, // 0
+ extents_type{1,1}, // 1
+ extents_type{1,2}, // 2
+ extents_type{2,1}, // 3
+ extents_type{2,3}, // 4
+ extents_type{2,3,1}, // 5
+ extents_type{4,1,3}, // 6
+ extents_type{1,2,3}, // 7
+ extents_type{4,2,3}, // 8
+ extents_type{4,2,3,5}} // 9
+ {
+ }
+ std::vector<extents_type> extents;
+};
+
+BOOST_AUTO_TEST_SUITE(test_tensor_arithmetic_operations, * boost::unit_test::depends_on("test_tensor"))
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_binary_arithmetic_operations, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+
+ auto check = [](auto const& e)
+ {
+ auto t = tensor_type (e);
+ auto t2 = tensor_type (e);
+ auto r = tensor_type (e);
+ auto v = value_type {};
+
+ std::iota(t.begin(), t.end(), v);
+ std::iota(t2.begin(), t2.end(), v+2);
+
+ r = t + t + t + t2;
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), 3*t(i) + t2(i) );
+
+
+ r = t2 / (t+3) * (t+1) - t2; // r = ( t2/ ((t+3)*(t+1)) ) - t2
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), t2(i) / (t(i)+3)*(t(i)+1) - t2(i) );
+
+ r = 3+t2 / (t+3) * (t+1) * t - t2; // r = 3+( t2/ ((t+3)*(t+1)*t) ) - t2
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), 3+t2(i) / (t(i)+3)*(t(i)+1)*t(i) - t2(i) );
+
+ r = t2 - t + t2 - t;
+
+ for(auto i = 0ul; i < r.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), 4 );
+
+
+ r = tensor_type (e,1) + tensor_type (e,1);
+
+ for(auto i = 0ul; i < r.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), 2 );
+
+ r = t * t * t * t2;
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), t(i)*t(i)*t(i)*t2(i) );
+
+ r = (t2/t2) * (t2/t2);
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), 1 );
+ };
+
+ for(auto const& e : extents)
+ check(e);
+
+
+ BOOST_CHECK_NO_THROW ( tensor_type t = tensor_type(extents.at(0)) + tensor_type(extents.at(0)) );
+ BOOST_CHECK_THROW ( tensor_type t = tensor_type(extents.at(0)) + tensor_type(extents.at(2)), std::runtime_error );
+ BOOST_CHECK_THROW ( tensor_type t = tensor_type(extents.at(1)) + tensor_type(extents.at(2)), std::runtime_error );
+
+
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_unary_arithmetic_operations, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+
+ auto check = [](auto const& e)
+ {
+ auto t = tensor_type (e);
+ auto t2 = tensor_type (e);
+ auto v = value_type {};
+
+ std::iota(t.begin(), t.end(), v);
+ std::iota(t2.begin(), t2.end(), v+2);
+
+ tensor_type r1 = t + 2 + t + 2;
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r1(i), 2*t(i) + 4 );
+
+ tensor_type r2 = 2 + t + 2 + t;
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r2(i), 2*t(i) + 4 );
+
+ tensor_type r3 = (t-2) + (t-2);
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r3(i), 2*t(i) - 4 );
+
+ tensor_type r4 = (t*2) * (3*t);
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r4(i), 2*3*t(i)*t(i) );
+
+ tensor_type r5 = (t2*2) / (2*t2) * t2;
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r5(i), (t2(i)*2) / (2*t2(i)) * t2(i) );
+
+ tensor_type r6 = (t2/2+1) / (2/t2+1) / t2;
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r6(i), (t2(i)/2+1) / (2/t2(i)+1) / t2(i) );
+
+ };
+
+ for(auto const& e : extents)
+ check(e);
+
+ BOOST_CHECK_NO_THROW ( tensor_type t = tensor_type(extents.at(0)) + 2 + tensor_type(extents.at(0)) );
+ BOOST_CHECK_THROW ( tensor_type t = tensor_type(extents.at(0)) + 2 + tensor_type(extents.at(2)), std::runtime_error );
+ BOOST_CHECK_THROW ( tensor_type t = tensor_type(extents.at(1)) + 2 + tensor_type(extents.at(2)), std::runtime_error );
+ BOOST_CHECK_THROW ( tensor_type t = tensor_type(extents.at(2)) + 2 + tensor_type(extents.at(2)) + tensor_type(extents.at(1)), std::runtime_error );
+ BOOST_CHECK_THROW ( tensor_type t = tensor_type(extents.at(2)) + 2 + tensor_type(extents.at(2)) + 2 + tensor_type(extents.at(1)), std::runtime_error );
+}
+
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_assign_arithmetic_operations, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+
+ auto check = [](auto const& e)
+ {
+ auto t = tensor_type (e);
+ auto t2 = tensor_type (e);
+ auto r = tensor_type (e);
+ auto v = value_type {};
+
+ std::iota(t.begin(), t.end(), v);
+ std::iota(t2.begin(), t2.end(), v+2);
+
+ r = t + 2;
+ r += t;
+ r += 2;
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), 2*t(i) + 4 );
+
+ r = 2 + t;
+ r += t;
+ r += 2;
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), 2*t(i) + 4 );
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), 2*t(i) + 4 );
+
+ r = (t-2);
+ r += t;
+ r -= 2;
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), 2*t(i) - 4 );
+
+ r = (t*2);
+ r *= 3;
+ r *= t;
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), 2*3*t(i)*t(i) );
+
+ r = (t2*2);
+ r /= 2;
+ r /= t2;
+ r *= t2;
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), (t2(i)*2) / (2*t2(i)) * t2(i) );
+
+ r = (t2/2+1);
+ r /= (2/t2+1);
+ r /= t2;
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( r(i), (t2(i)/2+1) / (2/t2(i)+1) / t2(i) );
+
+ tensor_type q = -r;
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( q(i), -r(i) );
+
+ tensor_type p = +r;
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL ( p(i), r(i) );
+ };
+
+ for(auto const& e : extents)
+ check(e);
+
+ auto r = tensor_type (extents.at(0));
+
+ BOOST_CHECK_NO_THROW ( r += tensor_type(extents.at(0)) + 2 + tensor_type(extents.at(0)) );
+ BOOST_CHECK_THROW ( r += tensor_type(extents.at(0)) + 2 + tensor_type(extents.at(2)), std::runtime_error );
+ BOOST_CHECK_THROW ( r += tensor_type(extents.at(1)) + 2 + tensor_type(extents.at(2)), std::runtime_error );
+ BOOST_CHECK_THROW ( r += tensor_type(extents.at(2)) + 2 + tensor_type(extents.at(2)) + tensor_type(extents.at(1)), std::runtime_error );
+ BOOST_CHECK_THROW ( r += tensor_type(extents.at(2)) + 2 + tensor_type(extents.at(2)) + 2 + tensor_type(extents.at(1)), std::runtime_error );
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_operators_comparison.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_operators_comparison.cpp
new file mode 100644
index 00000000..f076e9c1
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_operators_comparison.cpp
@@ -0,0 +1,246 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+
+
+
+#include <boost/numeric/ublas/tensor/operators_comparison.hpp>
+#include <boost/numeric/ublas/tensor/operators_arithmetic.hpp>
+#include <boost/numeric/ublas/tensor/tensor.hpp>
+#include <boost/test/unit_test.hpp>
+#include <boost/multiprecision/cpp_bin_float.hpp>
+#include "utility.hpp"
+
+
+using double_extended = boost::multiprecision::cpp_bin_float_double_extended;
+
+using test_types = zip<int,long,float,double,double_extended>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
+
+struct fixture {
+ using extents_type = boost::numeric::ublas::basic_extents<std::size_t>;
+ fixture()
+ : extents{
+ extents_type{}, // 0
+ extents_type{1,1}, // 1
+ extents_type{1,2}, // 2
+ extents_type{2,1}, // 3
+ extents_type{2,3}, // 4
+ extents_type{2,3,1}, // 5
+ extents_type{4,1,3}, // 6
+ extents_type{1,2,3}, // 7
+ extents_type{4,2,3}, // 8
+ extents_type{4,2,3,5}} // 9
+ {
+ }
+ std::vector<extents_type> extents;
+};
+
+BOOST_AUTO_TEST_SUITE(test_tensor_comparison, * boost::unit_test::depends_on("test_tensor"))
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_comparison, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+
+ auto check = [](auto const& e)
+ {
+ auto t = tensor_type (e);
+ auto t2 = tensor_type (e);
+ auto v = value_type {};
+
+ std::iota(t.begin(), t.end(), v);
+ std::iota(t2.begin(), t2.end(), v+2);
+
+ BOOST_CHECK( t == t );
+ BOOST_CHECK( t != t2 );
+
+ if(t.empty())
+ return;
+
+ BOOST_CHECK(!(t < t));
+ BOOST_CHECK(!(t > t));
+ BOOST_CHECK( t < t2 );
+ BOOST_CHECK( t2 > t );
+ BOOST_CHECK( t <= t );
+ BOOST_CHECK( t >= t );
+ BOOST_CHECK( t <= t2 );
+ BOOST_CHECK( t2 >= t );
+ BOOST_CHECK( t2 >= t2 );
+ BOOST_CHECK( t2 >= t );
+ };
+
+ for(auto const& e : extents)
+ check(e);
+
+ auto e0 = extents.at(0);
+ auto e1 = extents.at(1);
+ auto e2 = extents.at(2);
+
+
+ auto b = false;
+ BOOST_CHECK_NO_THROW ( b = (tensor_type(e0) == tensor_type(e0)));
+ BOOST_CHECK_NO_THROW ( b = (tensor_type(e1) == tensor_type(e2)));
+ BOOST_CHECK_NO_THROW ( b = (tensor_type(e0) == tensor_type(e2)));
+ BOOST_CHECK_NO_THROW ( b = (tensor_type(e1) != tensor_type(e2)));
+
+ BOOST_CHECK_THROW ( b = (tensor_type(e1) >= tensor_type(e2)), std::runtime_error );
+ BOOST_CHECK_THROW ( b = (tensor_type(e1) <= tensor_type(e2)), std::runtime_error );
+ BOOST_CHECK_THROW ( b = (tensor_type(e1) < tensor_type(e2)), std::runtime_error );
+ BOOST_CHECK_THROW ( b = (tensor_type(e1) > tensor_type(e2)), std::runtime_error );
+
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_comparison_with_tensor_expressions, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+
+ auto check = [](auto const& e)
+ {
+ auto t = tensor_type (e);
+ auto t2 = tensor_type (e);
+ auto v = value_type {};
+
+ std::iota(t.begin(), t.end(), v);
+ std::iota(t2.begin(), t2.end(), v+2);
+
+ BOOST_CHECK( t == t );
+ BOOST_CHECK( t != t2 );
+
+ if(t.empty())
+ return;
+
+ BOOST_CHECK( !(t < t) );
+ BOOST_CHECK( !(t > t) );
+ BOOST_CHECK( t < (t2+t) );
+ BOOST_CHECK( (t2+t) > t );
+ BOOST_CHECK( t <= (t+t) );
+ BOOST_CHECK( (t+t2) >= t );
+ BOOST_CHECK( (t2+t2+2) >= t);
+ BOOST_CHECK( 2*t2 > t );
+ BOOST_CHECK( t < 2*t2 );
+ BOOST_CHECK( 2*t2 > t);
+ BOOST_CHECK( 2*t2 >= t2 );
+ BOOST_CHECK( t2 <= 2*t2);
+ BOOST_CHECK( 3*t2 >= t );
+
+ };
+
+ for(auto const& e : extents)
+ check(e);
+
+ auto e0 = extents.at(0);
+ auto e1 = extents.at(1);
+ auto e2 = extents.at(2);
+
+ auto b = false;
+ BOOST_CHECK_NO_THROW (b = tensor_type(e0) == (tensor_type(e0) + tensor_type(e0)) );
+ BOOST_CHECK_NO_THROW (b = tensor_type(e1) == (tensor_type(e2) + tensor_type(e2)) );
+ BOOST_CHECK_NO_THROW (b = tensor_type(e0) == (tensor_type(e2) + 2) );
+ BOOST_CHECK_NO_THROW (b = tensor_type(e1) != (2 + tensor_type(e2)) );
+
+ BOOST_CHECK_NO_THROW (b = (tensor_type(e0) + tensor_type(e0)) == tensor_type(e0) );
+ BOOST_CHECK_NO_THROW (b = (tensor_type(e2) + tensor_type(e2)) == tensor_type(e1) );
+ BOOST_CHECK_NO_THROW (b = (tensor_type(e2) + 2) == tensor_type(e0) );
+ BOOST_CHECK_NO_THROW (b = (2 + tensor_type(e2)) != tensor_type(e1) );
+
+ BOOST_CHECK_THROW (b = tensor_type(e1) >= (tensor_type(e2) + tensor_type(e2)), std::runtime_error );
+ BOOST_CHECK_THROW (b = tensor_type(e1) <= (tensor_type(e2) + tensor_type(e2)), std::runtime_error );
+ BOOST_CHECK_THROW (b = tensor_type(e1) < (tensor_type(e2) + tensor_type(e2)), std::runtime_error );
+ BOOST_CHECK_THROW (b = tensor_type(e1) > (tensor_type(e2) + tensor_type(e2)), std::runtime_error );
+
+ BOOST_CHECK_THROW (b = tensor_type(e1) >= (tensor_type(e2) + 2), std::runtime_error );
+ BOOST_CHECK_THROW (b = tensor_type(e1) <= (2 + tensor_type(e2)), std::runtime_error );
+ BOOST_CHECK_THROW (b = tensor_type(e1) < (tensor_type(e2) + 3), std::runtime_error );
+ BOOST_CHECK_THROW (b = tensor_type(e1) > (4 + tensor_type(e2)), std::runtime_error );
+
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_comparison_with_scalar, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+
+ auto check = [](auto const& e)
+ {
+
+ BOOST_CHECK( tensor_type(e,value_type{2}) == tensor_type(e,value_type{2}) );
+ BOOST_CHECK( tensor_type(e,value_type{2}) != tensor_type(e,value_type{1}) );
+
+ if(e.empty())
+ return;
+
+ BOOST_CHECK( !(tensor_type(e,2) < 2) );
+ BOOST_CHECK( !(tensor_type(e,2) > 2) );
+ BOOST_CHECK( (tensor_type(e,2) >= 2) );
+ BOOST_CHECK( (tensor_type(e,2) <= 2) );
+ BOOST_CHECK( (tensor_type(e,2) == 2) );
+ BOOST_CHECK( (tensor_type(e,2) != 3) );
+
+ BOOST_CHECK( !(2 > tensor_type(e,2)) );
+ BOOST_CHECK( !(2 < tensor_type(e,2)) );
+ BOOST_CHECK( (2 <= tensor_type(e,2)) );
+ BOOST_CHECK( (2 >= tensor_type(e,2)) );
+ BOOST_CHECK( (2 == tensor_type(e,2)) );
+ BOOST_CHECK( (3 != tensor_type(e,2)) );
+
+ BOOST_CHECK( !( tensor_type(e,2)+3 < 5) );
+ BOOST_CHECK( !( tensor_type(e,2)+3 > 5) );
+ BOOST_CHECK( ( tensor_type(e,2)+3 >= 5) );
+ BOOST_CHECK( ( tensor_type(e,2)+3 <= 5) );
+ BOOST_CHECK( ( tensor_type(e,2)+3 == 5) );
+ BOOST_CHECK( ( tensor_type(e,2)+3 != 6) );
+
+
+ BOOST_CHECK( !( 5 > tensor_type(e,2)+3) );
+ BOOST_CHECK( !( 5 < tensor_type(e,2)+3) );
+ BOOST_CHECK( ( 5 >= tensor_type(e,2)+3) );
+ BOOST_CHECK( ( 5 <= tensor_type(e,2)+3) );
+ BOOST_CHECK( ( 5 == tensor_type(e,2)+3) );
+ BOOST_CHECK( ( 6 != tensor_type(e,2)+3) );
+
+
+ BOOST_CHECK( !( tensor_type(e,2)+tensor_type(e,3) < 5) );
+ BOOST_CHECK( !( tensor_type(e,2)+tensor_type(e,3) > 5) );
+ BOOST_CHECK( ( tensor_type(e,2)+tensor_type(e,3) >= 5) );
+ BOOST_CHECK( ( tensor_type(e,2)+tensor_type(e,3) <= 5) );
+ BOOST_CHECK( ( tensor_type(e,2)+tensor_type(e,3) == 5) );
+ BOOST_CHECK( ( tensor_type(e,2)+tensor_type(e,3) != 6) );
+
+
+ BOOST_CHECK( !( 5 > tensor_type(e,2)+tensor_type(e,3)) );
+ BOOST_CHECK( !( 5 < tensor_type(e,2)+tensor_type(e,3)) );
+ BOOST_CHECK( ( 5 >= tensor_type(e,2)+tensor_type(e,3)) );
+ BOOST_CHECK( ( 5 <= tensor_type(e,2)+tensor_type(e,3)) );
+ BOOST_CHECK( ( 5 == tensor_type(e,2)+tensor_type(e,3)) );
+ BOOST_CHECK( ( 6 != tensor_type(e,2)+tensor_type(e,3)) );
+
+ };
+
+ for(auto const& e : extents)
+ check(e);
+
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_strides.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_strides.cpp
new file mode 100644
index 00000000..3a29e660
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_strides.cpp
@@ -0,0 +1,172 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+
+
+
+
+#include <boost/test/unit_test.hpp>
+#include <boost/numeric/ublas/tensor/strides.hpp>
+#include <boost/numeric/ublas/tensor/extents.hpp>
+
+//BOOST_AUTO_TEST_SUITE(test_strides, * boost::unit_test::depends_on("test_extents"));
+
+BOOST_AUTO_TEST_SUITE(test_strides)
+
+using test_types = std::tuple<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE( test_strides_ctor, value, test_types)
+{
+ using namespace boost::numeric;
+
+ using extents_type = ublas::basic_extents<unsigned>;
+ using strides_type = ublas::strides<value>;
+
+ strides_type s0{};
+ BOOST_CHECK ( s0.empty());
+ BOOST_CHECK_EQUAL ( s0.size(), 0);
+
+ strides_type s1{extents_type{1,1}};
+ BOOST_CHECK (!s1.empty());
+ BOOST_CHECK_EQUAL ( s1.size(), 2);
+
+ strides_type s2{extents_type{1,2}};
+ BOOST_CHECK (!s2.empty());
+ BOOST_CHECK_EQUAL ( s2.size(), 2);
+
+ strides_type s3{extents_type{2,1}};
+ BOOST_CHECK (!s3.empty());
+ BOOST_CHECK_EQUAL ( s3.size(), 2);
+
+ strides_type s4{extents_type{2,3}};
+ BOOST_CHECK (!s4.empty());
+ BOOST_CHECK_EQUAL ( s4.size(), 2);
+
+ strides_type s5{extents_type{2,3,1}};
+ BOOST_CHECK (!s5.empty());
+ BOOST_CHECK_EQUAL ( s5.size(), 3);
+
+ strides_type s6{extents_type{1,2,3}};
+ BOOST_CHECK (!s6.empty());
+ BOOST_CHECK_EQUAL ( s6.size(), 3);
+
+ strides_type s7{extents_type{4,2,3}};
+ BOOST_CHECK (!s7.empty());
+ BOOST_CHECK_EQUAL ( s7.size(), 3);
+}
+
+
+
+BOOST_AUTO_TEST_CASE( test_strides_ctor_access_first_order)
+{
+ using namespace boost::numeric;
+
+ using extents_type = ublas::basic_extents<unsigned>;
+ using strides_type = ublas::strides<ublas::first_order>;
+
+ strides_type s1{extents_type{1,1}};
+ BOOST_REQUIRE_EQUAL( s1.size(),2);
+ BOOST_CHECK_EQUAL ( s1[0], 1);
+ BOOST_CHECK_EQUAL ( s1[1], 1);
+
+ strides_type s2{extents_type{1,2}};
+ BOOST_REQUIRE_EQUAL ( s2.size(),2);
+ BOOST_CHECK_EQUAL ( s2[0], 1);
+ BOOST_CHECK_EQUAL ( s2[1], 1);
+
+ strides_type s3{extents_type{2,1}};
+ BOOST_REQUIRE_EQUAL ( s3.size(),2);
+ BOOST_CHECK_EQUAL ( s3[0], 1);
+ BOOST_CHECK_EQUAL ( s3[1], 1);
+
+ strides_type s4{extents_type{2,3}};
+ BOOST_REQUIRE_EQUAL ( s4.size(),2);
+ BOOST_CHECK_EQUAL ( s4[0], 1);
+ BOOST_CHECK_EQUAL ( s4[1], 2);
+
+ strides_type s5{extents_type{2,3,1}};
+ BOOST_REQUIRE_EQUAL ( s5.size(),3);
+ BOOST_CHECK_EQUAL ( s5[0], 1);
+ BOOST_CHECK_EQUAL ( s5[1], 2);
+ BOOST_CHECK_EQUAL ( s5[2], 6);
+
+ strides_type s6{extents_type{1,2,3}};
+ BOOST_REQUIRE_EQUAL ( s6.size(),3);
+ BOOST_CHECK_EQUAL ( s6[0], 1);
+ BOOST_CHECK_EQUAL ( s6[1], 1);
+ BOOST_CHECK_EQUAL ( s6[2], 2);
+
+ strides_type s7{extents_type{2,1,3}};
+ BOOST_REQUIRE_EQUAL ( s7.size(),3);
+ BOOST_CHECK_EQUAL ( s7[0], 1);
+ BOOST_CHECK_EQUAL ( s7[1], 2);
+ BOOST_CHECK_EQUAL ( s7[2], 2);
+
+ strides_type s8{extents_type{4,2,3}};
+ BOOST_REQUIRE_EQUAL ( s8.size(),3);
+ BOOST_CHECK_EQUAL ( s8[0], 1);
+ BOOST_CHECK_EQUAL ( s8[1], 4);
+ BOOST_CHECK_EQUAL ( s8[2], 8);
+}
+
+BOOST_AUTO_TEST_CASE( test_strides_ctor_access_last_order)
+{
+ using namespace boost::numeric;
+
+ using extents_type = ublas::basic_extents<unsigned>;
+ using strides_type = ublas::strides<ublas::last_order>;
+
+ strides_type s1{extents_type{1,1}};
+ BOOST_REQUIRE_EQUAL( s1.size(),2);
+ BOOST_CHECK_EQUAL ( s1[0], 1);
+ BOOST_CHECK_EQUAL ( s1[1], 1);
+
+ strides_type s2{extents_type{1,2}};
+ BOOST_REQUIRE_EQUAL ( s2.size(),2);
+ BOOST_CHECK_EQUAL ( s2[0], 1);
+ BOOST_CHECK_EQUAL ( s2[1], 1);
+
+ strides_type s3{extents_type{2,1}};
+ BOOST_REQUIRE_EQUAL ( s3.size(),2);
+ BOOST_CHECK_EQUAL ( s3[0], 1);
+ BOOST_CHECK_EQUAL ( s3[1], 1);
+
+ strides_type s4{extents_type{2,3}};
+ BOOST_REQUIRE_EQUAL ( s4.size(),2);
+ BOOST_CHECK_EQUAL ( s4[0], 3);
+ BOOST_CHECK_EQUAL ( s4[1], 1);
+
+ strides_type s5{extents_type{2,3,1}};
+ BOOST_REQUIRE_EQUAL ( s5.size(),3);
+ BOOST_CHECK_EQUAL ( s5[0], 3);
+ BOOST_CHECK_EQUAL ( s5[1], 1);
+ BOOST_CHECK_EQUAL ( s5[2], 1);
+
+ strides_type s6{extents_type{1,2,3}};
+ BOOST_REQUIRE_EQUAL ( s6.size(),3);
+ BOOST_CHECK_EQUAL ( s6[0], 6);
+ BOOST_CHECK_EQUAL ( s6[1], 3);
+ BOOST_CHECK_EQUAL ( s6[2], 1);
+
+ strides_type s7{extents_type{2,1,3}};
+ BOOST_REQUIRE_EQUAL ( s7.size(),3);
+ BOOST_CHECK_EQUAL ( s7[0], 3);
+ BOOST_CHECK_EQUAL ( s7[1], 3);
+ BOOST_CHECK_EQUAL ( s7[2], 1);
+
+ strides_type s8{extents_type{4,2,3}};
+ BOOST_REQUIRE_EQUAL ( s8.size(),3);
+ BOOST_CHECK_EQUAL ( s8[0], 6);
+ BOOST_CHECK_EQUAL ( s8[1], 3);
+ BOOST_CHECK_EQUAL ( s8[2], 1);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_tensor.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_tensor.cpp
new file mode 100644
index 00000000..400e8624
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_tensor.cpp
@@ -0,0 +1,473 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+
+
+
+#include <random>
+#include <boost/numeric/ublas/tensor/tensor.hpp>
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MODULE TestTensor
+
+#include <boost/test/unit_test.hpp>
+#include "utility.hpp"
+
+//BOOST_AUTO_TEST_SUITE ( test_tensor, * boost::unit_test::depends_on("test_extents") ) ;
+BOOST_AUTO_TEST_SUITE ( test_tensor )
+
+using test_types = zip<int,long,float,double,std::complex<float>>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
+
+
+BOOST_AUTO_TEST_CASE_TEMPLATE( test_tensor_ctor, value, test_types)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ auto a1 = tensor_type{};
+ BOOST_CHECK_EQUAL( a1.size() , 0ul );
+ BOOST_CHECK( a1.empty() );
+ BOOST_CHECK_EQUAL( a1.data() , nullptr);
+
+ auto a2 = tensor_type{1,1};
+ BOOST_CHECK_EQUAL( a2.size() , 1 );
+ BOOST_CHECK( !a2.empty() );
+ BOOST_CHECK_NE( a2.data() , nullptr);
+
+ auto a3 = tensor_type{2,1};
+ BOOST_CHECK_EQUAL( a3.size() , 2 );
+ BOOST_CHECK( !a3.empty() );
+ BOOST_CHECK_NE( a3.data() , nullptr);
+
+ auto a4 = tensor_type{1,2};
+ BOOST_CHECK_EQUAL( a4.size() , 2 );
+ BOOST_CHECK( !a4.empty() );
+ BOOST_CHECK_NE( a4.data() , nullptr);
+
+ auto a5 = tensor_type{2,1};
+ BOOST_CHECK_EQUAL( a5.size() , 2 );
+ BOOST_CHECK( !a5.empty() );
+ BOOST_CHECK_NE( a5.data() , nullptr);
+
+ auto a6 = tensor_type{4,3,2};
+ BOOST_CHECK_EQUAL( a6.size() , 4*3*2 );
+ BOOST_CHECK( !a6.empty() );
+ BOOST_CHECK_NE( a6.data() , nullptr);
+
+ auto a7 = tensor_type{4,1,2};
+ BOOST_CHECK_EQUAL( a7.size() , 4*1*2 );
+ BOOST_CHECK( !a7.empty() );
+ BOOST_CHECK_NE( a7.data() , nullptr);
+}
+
+
+struct fixture
+{
+ using extents_type = boost::numeric::ublas::basic_extents<std::size_t>;
+ fixture()
+ : extents {
+ extents_type{}, // 0
+ extents_type{1,1}, // 1
+ extents_type{1,2}, // 2
+ extents_type{2,1}, // 3
+ extents_type{2,3}, // 4
+ extents_type{2,3,1}, // 5
+ extents_type{4,1,3}, // 6
+ extents_type{1,2,3}, // 7
+ extents_type{4,2,3}, // 8
+ extents_type{4,2,3,5}} // 9
+ {
+ }
+ std::vector<extents_type> extents;
+};
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ctor_extents, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ auto check = [](auto const& e) {
+ auto t = tensor_type{e};
+ BOOST_CHECK_EQUAL ( t.size() , e.product() );
+ BOOST_CHECK_EQUAL ( t.rank() , e.size() );
+ if(e.empty()) {
+ BOOST_CHECK ( t.empty() );
+ BOOST_CHECK_EQUAL ( t.data() , nullptr);
+ }
+ else{
+ BOOST_CHECK ( !t.empty() );
+ BOOST_CHECK_NE ( t.data() , nullptr);
+ }
+ };
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_copy_ctor, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ auto check = [](auto const& e)
+ {
+ auto r = tensor_type{e};
+ auto t = r;
+ BOOST_CHECK_EQUAL ( t.size() , r.size() );
+ BOOST_CHECK_EQUAL ( t.rank() , r.rank() );
+ BOOST_CHECK ( t.strides() == r.strides() );
+ BOOST_CHECK ( t.extents() == r.extents() );
+
+ if(e.empty()) {
+ BOOST_CHECK ( t.empty() );
+ BOOST_CHECK_EQUAL ( t.data() , nullptr);
+ }
+ else{
+ BOOST_CHECK ( !t.empty() );
+ BOOST_CHECK_NE ( t.data() , nullptr);
+ }
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( t[i], r[i] );
+ };
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_copy_ctor_layout, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using other_layout_type = std::conditional_t<std::is_same<ublas::first_order,layout_type>::value, ublas::last_order, ublas::first_order>;
+ using other_tensor_type = ublas::tensor<value_type, other_layout_type>;
+
+
+ for(auto const& e : extents)
+ {
+ auto r = tensor_type{e};
+ other_tensor_type t = r;
+ tensor_type q = t;
+
+ BOOST_CHECK_EQUAL ( t.size() , r.size() );
+ BOOST_CHECK_EQUAL ( t.rank() , r.rank() );
+ BOOST_CHECK ( t.extents() == r.extents() );
+
+ BOOST_CHECK_EQUAL ( q.size() , r.size() );
+ BOOST_CHECK_EQUAL ( q.rank() , r.rank() );
+ BOOST_CHECK ( q.strides() == r.strides() );
+ BOOST_CHECK ( q.extents() == r.extents() );
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( q[i], r[i] );
+ }
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_copy_move_ctor, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ auto check = [](auto const& e)
+ {
+ auto r = tensor_type{e};
+ auto t = std::move(r);
+ BOOST_CHECK_EQUAL ( t.size() , e.product() );
+ BOOST_CHECK_EQUAL ( t.rank() , e.size() );
+
+ if(e.empty()) {
+ BOOST_CHECK ( t.empty() );
+ BOOST_CHECK_EQUAL ( t.data() , nullptr);
+ }
+ else{
+ BOOST_CHECK ( !t.empty() );
+ BOOST_CHECK_NE ( t.data() , nullptr);
+ }
+
+ };
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ctor_extents_init, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ std::random_device device{};
+ std::minstd_rand0 generator(device());
+
+ using distribution_type = std::conditional_t<std::is_integral_v<value_type>, std::uniform_int_distribution<>, std::uniform_real_distribution<> >;
+ auto distribution = distribution_type(1,6);
+
+ for(auto const& e : extents){
+ auto r = static_cast<value_type>(distribution(generator));
+ auto t = tensor_type{e,r};
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( t[i], r );
+ }
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ctor_extents_array, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using array_type = typename tensor_type::array_type;
+
+ for(auto const& e : extents) {
+ auto a = array_type(e.product());
+ auto v = value_type {};
+
+ for(auto& aa : a){
+ aa = v;
+ v += value_type{1};
+ }
+ auto t = tensor_type{e, a};
+ v = value_type{};
+
+ for(auto i = 0ul; i < t.size(); ++i, v+=value_type{1})
+ BOOST_CHECK_EQUAL( t[i], v);
+ }
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_read_write_single_index_access, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ for(auto const& e : extents) {
+ auto t = tensor_type{e};
+ auto v = value_type {};
+ for(auto i = 0ul; i < t.size(); ++i, v+=value_type{1}){
+ t[i] = v;
+ BOOST_CHECK_EQUAL( t[i], v );
+
+ t(i) = v;
+ BOOST_CHECK_EQUAL( t(i), v );
+ }
+ }
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_read_write_multi_index_access_at, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ auto check1 = [](const tensor_type& t)
+ {
+ auto v = value_type{};
+ for(auto k = 0ul; k < t.size(); ++k){
+ BOOST_CHECK_EQUAL(t[k], v);
+ v+=value_type{1};
+ }
+ };
+
+ auto check2 = [](const tensor_type& t)
+ {
+ std::array<unsigned,2> k;
+ auto r = std::is_same_v<layout_type,ublas::first_order> ? 1 : 0;
+ auto q = std::is_same_v<layout_type,ublas::last_order > ? 1 : 0;
+ auto v = value_type{};
+ for(k[r] = 0ul; k[r] < t.size(r); ++k[r]){
+ for(k[q] = 0ul; k[q] < t.size(q); ++k[q]){
+ BOOST_CHECK_EQUAL(t.at(k[0],k[1]), v);
+ v+=value_type{1};
+ }
+ }
+ };
+
+ auto check3 = [](const tensor_type& t)
+ {
+ std::array<unsigned,3> k;
+ using op_type = std::conditional_t<std::is_same_v<layout_type,ublas::first_order>, std::minus<>, std::plus<>>;
+ auto r = std::is_same_v<layout_type,ublas::first_order> ? 2 : 0;
+ auto o = op_type{};
+ auto v = value_type{};
+ for(k[r] = 0ul; k[r] < t.size(r); ++k[r]){
+ for(k[o(r,1)] = 0ul; k[o(r,1)] < t.size(o(r,1)); ++k[o(r,1)]){
+ for(k[o(r,2)] = 0ul; k[o(r,2)] < t.size(o(r,2)); ++k[o(r,2)]){
+ BOOST_CHECK_EQUAL(t.at(k[0],k[1],k[2]), v);
+ v+=value_type{1};
+ }
+ }
+ }
+ };
+
+ auto check4 = [](const tensor_type& t)
+ {
+ std::array<unsigned,4> k;
+ using op_type = std::conditional_t<std::is_same_v<layout_type,ublas::first_order>, std::minus<>, std::plus<>>;
+ auto r = std::is_same_v<layout_type,ublas::first_order> ? 3 : 0;
+ auto o = op_type{};
+ auto v = value_type{};
+ for(k[r] = 0ul; k[r] < t.size(r); ++k[r]){
+ for(k[o(r,1)] = 0ul; k[o(r,1)] < t.size(o(r,1)); ++k[o(r,1)]){
+ for(k[o(r,2)] = 0ul; k[o(r,2)] < t.size(o(r,2)); ++k[o(r,2)]){
+ for(k[o(r,3)] = 0ul; k[o(r,3)] < t.size(o(r,3)); ++k[o(r,3)]){
+ BOOST_CHECK_EQUAL(t.at(k[0],k[1],k[2],k[3]), v);
+ v+=value_type{1};
+ }
+ }
+ }
+ }
+ };
+
+ auto check = [check1,check2,check3,check4](auto const& e) {
+ auto t = tensor_type{e};
+ auto v = value_type {};
+ for(auto i = 0ul; i < t.size(); ++i){
+ t[i] = v;
+ v+=value_type{1};
+ }
+
+ if(t.rank() == 1) check1(t);
+ else if(t.rank() == 2) check2(t);
+ else if(t.rank() == 3) check3(t);
+ else if(t.rank() == 4) check4(t);
+
+ };
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_reshape, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+
+ for(auto const& efrom : extents){
+ for(auto const& eto : extents){
+
+ auto v = value_type {};
+ v+=value_type{1};
+ auto t = tensor_type{efrom, v};
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( t[i], v );
+
+ t.reshape(eto);
+ for(auto i = 0ul; i < std::min(efrom.product(),eto.product()); ++i)
+ BOOST_CHECK_EQUAL( t[i], v );
+
+ BOOST_CHECK_EQUAL ( t.size() , eto.product() );
+ BOOST_CHECK_EQUAL ( t.rank() , eto.size() );
+ BOOST_CHECK ( t.extents() == eto );
+
+ if(efrom != eto){
+ for(auto i = efrom.product(); i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( t[i], value_type{} );
+ }
+ }
+ }
+}
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_swap, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ for(auto const& e_t : extents){
+ for(auto const& e_r : extents) {
+
+ auto v = value_type {} + value_type{1};
+ auto w = value_type {} + value_type{2};
+ auto t = tensor_type{e_t, v};
+ auto r = tensor_type{e_r, w};
+
+ std::swap( r, t );
+
+ for(auto i = 0ul; i < t.size(); ++i)
+ BOOST_CHECK_EQUAL( t[i], w );
+
+ BOOST_CHECK_EQUAL ( t.size() , e_r.product() );
+ BOOST_CHECK_EQUAL ( t.rank() , e_r.size() );
+ BOOST_CHECK ( t.extents() == e_r );
+
+ for(auto i = 0ul; i < r.size(); ++i)
+ BOOST_CHECK_EQUAL( r[i], v );
+
+ BOOST_CHECK_EQUAL ( r.size() , e_t.product() );
+ BOOST_CHECK_EQUAL ( r.rank() , e_t.size() );
+ BOOST_CHECK ( r.extents() == e_t );
+
+
+ }
+ }
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_standard_iterator, value, test_types, fixture)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+
+ for(auto const& e : extents)
+ {
+ auto v = value_type {} + value_type{1};
+ auto t = tensor_type{e, v};
+
+ BOOST_CHECK_EQUAL( std::distance(t.begin(), t.end ()), t.size() );
+ BOOST_CHECK_EQUAL( std::distance(t.rbegin(), t.rend()), t.size() );
+
+ BOOST_CHECK_EQUAL( std::distance(t.cbegin(), t.cend ()), t.size() );
+ BOOST_CHECK_EQUAL( std::distance(t.crbegin(), t.crend()), t.size() );
+
+ if(t.size() > 0) {
+ BOOST_CHECK( t.data() == std::addressof( *t.begin () ) ) ;
+ BOOST_CHECK( t.data() == std::addressof( *t.cbegin() ) ) ;
+ }
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/boost/libs/numeric/ublas/test/tensor/test_tensor_matrix_vector.cpp b/src/boost/libs/numeric/ublas/test/tensor/test_tensor_matrix_vector.cpp
new file mode 100644
index 00000000..3e34047d
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/test_tensor_matrix_vector.cpp
@@ -0,0 +1,472 @@
+// Copyright (c) 2018-2019 Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+
+
+#include <iostream>
+#include <random>
+#include <boost/numeric/ublas/tensor.hpp>
+#include <boost/numeric/ublas/matrix.hpp>
+#include <boost/test/unit_test.hpp>
+
+#include "utility.hpp"
+
+// BOOST_AUTO_TEST_SUITE ( test_tensor_matrix_interoperability, * boost::unit_test::depends_on("test_tensor") ) ;
+
+BOOST_AUTO_TEST_SUITE ( test_tensor_matrix_interoperability )
+
+using test_types = zip<int,long,float,double>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
+
+
+BOOST_AUTO_TEST_CASE_TEMPLATE( test_tensor_matrix_copy_ctor, value, test_types)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using matrix_type = typename tensor_type::matrix_type;
+
+ tensor_type a1 = matrix_type();
+ BOOST_CHECK_EQUAL( a1.size() , 0ul );
+ BOOST_CHECK( a1.empty() );
+ BOOST_CHECK_EQUAL( a1.data() , nullptr);
+
+ tensor_type a2 = matrix_type(1,1);
+ BOOST_CHECK_EQUAL( a2.size() , 1 );
+ BOOST_CHECK( !a2.empty() );
+ BOOST_CHECK_NE( a2.data() , nullptr);
+
+ tensor_type a3 = matrix_type(2,1);
+ BOOST_CHECK_EQUAL( a3.size() , 2 );
+ BOOST_CHECK( !a3.empty() );
+ BOOST_CHECK_NE( a3.data() , nullptr);
+
+ tensor_type a4 = matrix_type(1,2);
+ BOOST_CHECK_EQUAL( a4.size() , 2 );
+ BOOST_CHECK( !a4.empty() );
+ BOOST_CHECK_NE( a4.data() , nullptr);
+
+ tensor_type a5 = matrix_type(2,3);
+ BOOST_CHECK_EQUAL( a5.size() , 6 );
+ BOOST_CHECK( !a5.empty() );
+ BOOST_CHECK_NE( a5.data() , nullptr);
+}
+
+
+BOOST_AUTO_TEST_CASE_TEMPLATE( test_tensor_vector_copy_ctor, value, test_types)
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using vector_type = typename tensor_type::vector_type;
+
+ tensor_type a1 = vector_type();
+ BOOST_CHECK_EQUAL( a1.size() , 0ul );
+ BOOST_CHECK( a1.empty() );
+ BOOST_CHECK_EQUAL( a1.data() , nullptr);
+
+ tensor_type a2 = vector_type(1);
+ BOOST_CHECK_EQUAL( a2.size() , 1 );
+ BOOST_CHECK( !a2.empty() );
+ BOOST_CHECK_NE( a2.data() , nullptr);
+
+ tensor_type a3 = vector_type(2);
+ BOOST_CHECK_EQUAL( a3.size() , 2 );
+ BOOST_CHECK( !a3.empty() );
+ BOOST_CHECK_NE( a3.data() , nullptr);
+
+ tensor_type a4 = vector_type(2);
+ BOOST_CHECK_EQUAL( a4.size() , 2 );
+ BOOST_CHECK( !a4.empty() );
+ BOOST_CHECK_NE( a4.data() , nullptr);
+
+ tensor_type a5 = vector_type(3);
+ BOOST_CHECK_EQUAL( a5.size() , 3 );
+ BOOST_CHECK( !a5.empty() );
+ BOOST_CHECK_NE( a5.data() , nullptr);
+}
+
+
+struct fixture
+{
+ using extents_type = boost::numeric::ublas::basic_extents<std::size_t>;
+ fixture()
+ : extents{
+ extents_type{1,1}, // 1
+ extents_type{1,2}, // 2
+ extents_type{2,1}, // 3
+ extents_type{2,3}, // 4
+ extents_type{9,7}, // 5
+ extents_type{9,11}, // 6
+ extents_type{12,12}, // 7
+ extents_type{15,17}} // 8
+ {
+ }
+ std::vector<extents_type> extents;
+};
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_matrix_copy_ctor_extents, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using matrix_type = typename tensor_type::matrix_type;
+
+ auto check = [](auto const& e) {
+ assert(e.size()==2);
+ tensor_type t = matrix_type{e[0],e[1]};
+ BOOST_CHECK_EQUAL ( t.size() , e.product() );
+ BOOST_CHECK_EQUAL ( t.rank() , e.size() );
+ BOOST_CHECK ( !t.empty() );
+ BOOST_CHECK_NE ( t.data() , nullptr);
+ };
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_vector_copy_ctor_extents, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using vector_type = typename tensor_type::vector_type;
+
+ auto check = [](auto const& e) {
+ assert(e.size()==2);
+ if(e.empty())
+ return;
+
+ tensor_type t = vector_type(e.product());
+ BOOST_CHECK_EQUAL ( t.size() , e.product() );
+ BOOST_CHECK_EQUAL ( t.rank() , e.size() );
+ BOOST_CHECK ( !t.empty() );
+ BOOST_CHECK_NE ( t.data() , nullptr);
+ };
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_matrix_copy_assignment, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using matrix_type = typename tensor_type::matrix_type;
+
+ auto check = [](auto const& e)
+ {
+ assert(e.size() == 2);
+ auto t = tensor_type{};
+ auto r = matrix_type(e[0],e[1]);
+ std::iota(r.data().begin(),r.data().end(), 1);
+ t = r;
+
+ BOOST_CHECK_EQUAL ( t.extents().at(0) , e.at(0) );
+ BOOST_CHECK_EQUAL ( t.extents().at(1) , e.at(1) );
+ BOOST_CHECK_EQUAL ( t.size() , e.product() );
+ BOOST_CHECK_EQUAL ( t.rank() , e.size() );
+ BOOST_CHECK ( !t.empty() );
+ BOOST_CHECK_NE ( t.data() , nullptr);
+
+ for(auto j = 0ul; j < t.size(1); ++j){
+ for(auto i = 0ul; i < t.size(0); ++i){
+ BOOST_CHECK_EQUAL( t.at(i,j), r(i,j) );
+ }
+ }
+ };
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_vector_copy_assignment, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using vector_type = typename tensor_type::vector_type;
+
+ auto check = [](auto const& e)
+ {
+ assert(e.size() == 2);
+ auto t = tensor_type{};
+ auto r = vector_type(e[0]*e[1]);
+ std::iota(r.data().begin(),r.data().end(), 1);
+ t = r;
+
+ BOOST_CHECK_EQUAL ( t.extents().at(0) , e.at(0)*e.at(1) );
+ BOOST_CHECK_EQUAL ( t.extents().at(1) , 1);
+ BOOST_CHECK_EQUAL ( t.size() , e.product() );
+ BOOST_CHECK_EQUAL ( t.rank() , e.size() );
+ BOOST_CHECK ( !t.empty() );
+ BOOST_CHECK_NE ( t.data() , nullptr);
+
+ for(auto i = 0ul; i < t.size(); ++i){
+ BOOST_CHECK_EQUAL( t[i], r(i) );
+ }
+ };
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_matrix_move_assignment, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using matrix_type = typename tensor_type::matrix_type;
+
+ auto check = [](auto const& e)
+ {
+ assert(e.size() == 2);
+ auto t = tensor_type{};
+ auto r = matrix_type(e[0],e[1]);
+ std::iota(r.data().begin(),r.data().end(), 1);
+ auto q = r;
+ t = std::move(r);
+
+ BOOST_CHECK_EQUAL ( t.extents().at(0) , e.at(0) );
+ BOOST_CHECK_EQUAL ( t.extents().at(1) , e.at(1) );
+ BOOST_CHECK_EQUAL ( t.size() , e.product() );
+ BOOST_CHECK_EQUAL ( t.rank() , e.size() );
+ BOOST_CHECK ( !t.empty() );
+ BOOST_CHECK_NE ( t.data() , nullptr);
+
+ for(auto j = 0ul; j < t.size(1); ++j){
+ for(auto i = 0ul; i < t.size(0); ++i){
+ BOOST_CHECK_EQUAL( t.at(i,j), q(i,j) );
+ }
+ }
+ };
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_vector_move_assignment, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using vector_type = typename tensor_type::vector_type;
+
+ auto check = [](auto const& e)
+ {
+ assert(e.size() == 2);
+ auto t = tensor_type{};
+ auto r = vector_type(e[0]*e[1]);
+ std::iota(r.data().begin(),r.data().end(), 1);
+ auto q = r;
+ t = std::move(r);
+
+ BOOST_CHECK_EQUAL ( t.extents().at(0) , e.at(0) * e.at(1));
+ BOOST_CHECK_EQUAL ( t.extents().at(1) , 1);
+ BOOST_CHECK_EQUAL ( t.size() , e.product() );
+ BOOST_CHECK_EQUAL ( t.rank() , e.size() );
+ BOOST_CHECK ( !t.empty() );
+ BOOST_CHECK_NE ( t.data() , nullptr);
+
+ for(auto i = 0ul; i < t.size(); ++i){
+ BOOST_CHECK_EQUAL( t[i], q(i) );
+ }
+ };
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_matrix_expressions, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using matrix_type = typename tensor_type::matrix_type;
+
+ auto check = [](auto const& e)
+ {
+ assert(e.size() == 2);
+ auto t = tensor_type{};
+ auto r = matrix_type(e[0],e[1]);
+ std::iota(r.data().begin(),r.data().end(), 1);
+ t = r + 3*r;
+ tensor_type s = r + 3*r;
+ tensor_type q = s + r + 3*r + s; // + 3*r
+
+
+ BOOST_CHECK_EQUAL ( t.extents().at(0) , e.at(0) );
+ BOOST_CHECK_EQUAL ( t.extents().at(1) , e.at(1) );
+ BOOST_CHECK_EQUAL ( t.size() , e.product() );
+ BOOST_CHECK_EQUAL ( t.rank() , e.size() );
+ BOOST_CHECK ( !t.empty() );
+ BOOST_CHECK_NE ( t.data() , nullptr);
+
+ BOOST_CHECK_EQUAL ( s.extents().at(0) , e.at(0) );
+ BOOST_CHECK_EQUAL ( s.extents().at(1) , e.at(1) );
+ BOOST_CHECK_EQUAL ( s.size() , e.product() );
+ BOOST_CHECK_EQUAL ( s.rank() , e.size() );
+ BOOST_CHECK ( !s.empty() );
+ BOOST_CHECK_NE ( s.data() , nullptr);
+
+ BOOST_CHECK_EQUAL ( q.extents().at(0) , e.at(0) );
+ BOOST_CHECK_EQUAL ( q.extents().at(1) , e.at(1) );
+ BOOST_CHECK_EQUAL ( q.size() , e.product() );
+ BOOST_CHECK_EQUAL ( q.rank() , e.size() );
+ BOOST_CHECK ( !q.empty() );
+ BOOST_CHECK_NE ( q.data() , nullptr);
+
+
+ for(auto j = 0ul; j < t.size(1); ++j){
+ for(auto i = 0ul; i < t.size(0); ++i){
+ BOOST_CHECK_EQUAL( t.at(i,j), 4*r(i,j) );
+ BOOST_CHECK_EQUAL( s.at(i,j), t.at(i,j) );
+ BOOST_CHECK_EQUAL( q.at(i,j), 3*s.at(i,j) );
+ }
+ }
+ };
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+
+
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_vector_expressions, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using vector_type = typename tensor_type::vector_type;
+
+ auto check = [](auto const& e)
+ {
+ assert(e.size() == 2);
+ auto t = tensor_type{};
+ auto r = vector_type(e[0]*e[1]);
+ std::iota(r.data().begin(),r.data().end(), 1);
+ t = r + 3*r;
+ tensor_type s = r + 3*r;
+ tensor_type q = s + r + 3*r + s; // + 3*r
+
+
+ BOOST_CHECK_EQUAL ( t.extents().at(0) , e.at(0)*e.at(1) );
+ BOOST_CHECK_EQUAL ( t.extents().at(1) , 1);
+ BOOST_CHECK_EQUAL ( t.size() , e.product() );
+ BOOST_CHECK_EQUAL ( t.rank() , e.size() );
+ BOOST_CHECK ( !t.empty() );
+ BOOST_CHECK_NE ( t.data() , nullptr);
+
+ BOOST_CHECK_EQUAL ( s.extents().at(0) , e.at(0)*e.at(1) );
+ BOOST_CHECK_EQUAL ( s.extents().at(1) , 1);
+ BOOST_CHECK_EQUAL ( s.size() , e.product() );
+ BOOST_CHECK_EQUAL ( s.rank() , e.size() );
+ BOOST_CHECK ( !s.empty() );
+ BOOST_CHECK_NE ( s.data() , nullptr);
+
+ BOOST_CHECK_EQUAL ( q.extents().at(0) , e.at(0)*e.at(1) );
+ BOOST_CHECK_EQUAL ( q.extents().at(1) , 1);
+ BOOST_CHECK_EQUAL ( q.size() , e.product() );
+ BOOST_CHECK_EQUAL ( q.rank() , e.size() );
+ BOOST_CHECK ( !q.empty() );
+ BOOST_CHECK_NE ( q.data() , nullptr);
+
+
+
+ for(auto i = 0ul; i < t.size(); ++i){
+ BOOST_CHECK_EQUAL( t.at(i), 4*r(i) );
+ BOOST_CHECK_EQUAL( s.at(i), t.at(i) );
+ BOOST_CHECK_EQUAL( q.at(i), 3*s.at(i) );
+ }
+ };
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_matrix_vector_expressions, value, test_types, fixture )
+{
+ using namespace boost::numeric;
+ using value_type = typename value::first_type;
+ using layout_type = typename value::second_type;
+ using tensor_type = ublas::tensor<value_type, layout_type>;
+ using matrix_type = typename tensor_type::matrix_type;
+ using vector_type = typename tensor_type::vector_type;
+
+ auto check = [](auto const& e)
+ {
+ if(e.product() <= 2)
+ return;
+ assert(e.size() == 2);
+ auto Q = tensor_type{e[0],1};
+ auto A = matrix_type(e[0],e[1]);
+ auto b = vector_type(e[1]);
+ auto c = vector_type(e[0]);
+ std::iota(b.data().begin(),b.data().end(), 1);
+ std::fill(A.data().begin(),A.data().end(), 1);
+ std::fill(c.data().begin(),c.data().end(), 2);
+ std::fill(Q.begin(),Q.end(), 2);
+
+ tensor_type T = Q + (ublas::prod(A , b) + 2*c) + 3*Q;
+
+ BOOST_CHECK_EQUAL ( T.extents().at(0) , Q.extents().at(0) );
+ BOOST_CHECK_EQUAL ( T.extents().at(1) , Q.extents().at(1));
+ BOOST_CHECK_EQUAL ( T.size() , Q.size() );
+ BOOST_CHECK_EQUAL ( T.size() , c.size() );
+ BOOST_CHECK_EQUAL ( T.rank() , Q.rank() );
+ BOOST_CHECK ( !T.empty() );
+ BOOST_CHECK_NE ( T.data() , nullptr);
+
+ for(auto i = 0ul; i < T.size(); ++i){
+ auto n = e[1];
+ auto ab = n * (n+1) / 2;
+ BOOST_CHECK_EQUAL( T(i), ab+4*Q(0)+2*c(0) );
+ }
+
+ };
+
+
+
+ for(auto const& e : extents)
+ check(e);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/src/boost/libs/numeric/ublas/test/tensor/utility.hpp b/src/boost/libs/numeric/ublas/test/tensor/utility.hpp
new file mode 100644
index 00000000..b2ef266e
--- /dev/null
+++ b/src/boost/libs/numeric/ublas/test/tensor/utility.hpp
@@ -0,0 +1,56 @@
+// Copyright (c) 2018-2019
+// Cem Bassoy
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The authors gratefully acknowledge the support of
+// Fraunhofer and Google in producing this work
+// which started as a Google Summer of Code project.
+//
+
+#ifndef _BOOST_UBLAS_TEST_TENSOR_UTILITY_
+#define _BOOST_UBLAS_TEST_TENSOR_UTILITY_
+
+template<class ... types>
+struct zip_helper;
+
+template<class type1, class ... types3>
+struct zip_helper<std::tuple<types3...>, type1>
+{
+ template<class ... types2>
+ struct with
+ {
+ using type = std::tuple<types3...,std::pair<type1,types2>...>;
+ };
+ template<class ... types2>
+ using with_t = typename with<types2...>::type;
+};
+
+
+template<class type1, class ... types3, class ... types1>
+struct zip_helper<std::tuple<types3...>, type1, types1...>
+{
+ template<class ... types2>
+ struct with
+ {
+ using next_tuple = std::tuple<types3...,std::pair<type1,types2>...>;
+ using type = typename zip_helper<next_tuple, types1...>::template with<types2...>::type;
+ };
+
+ template<class ... types2>
+ using with_t = typename with<types2...>::type;
+};
+
+template<class ... types>
+using zip = zip_helper<std::tuple<>,types...>;
+
+// creates e.g.
+// using test_types = zip<long,float>::with_t<first_order,last_order>; // equals
+// using test_types = std::tuple< std::pair<float, first_order>, std::pair<float, last_order >, std::pair<double,first_order>, std::pair<double,last_order >
+//>;
+//static_assert(std::is_same< std::tuple_element_t<0,std::tuple_element_t<0,test_types2>>, float>::value,"should be float ");
+//static_assert(std::is_same< std::tuple_element_t<1,std::tuple_element_t<0,test_types2>>, boost::numeric::ublas::first_order>::value,"should be boost::numeric::ublas::first_order ");
+
+#endif