474 lines
13 KiB
C++
474 lines
13 KiB
C++
// 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()
|