1 // Copyright (c) 2018-2019 Cem Bassoy
3 // Distributed under the Boost Software License, Version 1.0. (See
4 // accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
7 // The authors gratefully acknowledge the support of
8 // Fraunhofer and Google in producing this work
9 // which started as a Google Summer of Code project.
11 // And we acknowledge the support from all contributors.
16 #include <boost/numeric/ublas/tensor.hpp>
17 #include <boost/numeric/ublas/matrix.hpp>
18 #include <boost/numeric/ublas/vector.hpp>
20 #include <boost/test/unit_test.hpp>
22 #include "utility.hpp"
24 BOOST_AUTO_TEST_SUITE ( test_tensor_functions
, * boost::unit_test::depends_on("test_tensor_contraction") )
27 using test_types
= zip
<int,long,float,double,std::complex<float>>::with_t
<boost::numeric::ublas::first_order
, boost::numeric::ublas::last_order
>;
29 //using test_types = zip<int>::with_t<boost::numeric::ublas::first_order>;
34 using extents_type
= boost::numeric::ublas::shape
;
37 extents_type
{1,1}, // 1
38 extents_type
{1,2}, // 2
39 extents_type
{2,1}, // 3
40 extents_type
{2,3}, // 4
41 extents_type
{2,3,1}, // 5
42 extents_type
{4,1,3}, // 6
43 extents_type
{1,2,3}, // 7
44 extents_type
{4,2,3}, // 8
45 extents_type
{4,2,3,5}} // 9
48 std::vector
<extents_type
> extents
;
54 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_prod_vector
, value
, test_types
, fixture
)
56 using namespace boost::numeric
;
57 using value_type
= typename
value::first_type
;
58 using layout_type
= typename
value::second_type
;
59 using tensor_type
= ublas::tensor
<value_type
,layout_type
>;
60 using vector_type
= typename
tensor_type::vector_type
;
63 for(auto const& n
: extents
){
65 auto a
= tensor_type(n
, value_type
{2});
67 for(auto m
= 0u; m
< n
.size(); ++m
){
69 auto b
= vector_type (n
[m
], value_type
{1} );
71 auto c
= ublas::prod(a
, b
, m
+1);
73 for(auto i
= 0u; i
< c
.size(); ++i
)
74 BOOST_CHECK_EQUAL( c
[i
] , value_type(n
[m
]) * a
[i
] );
83 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_prod_matrix
, value
, test_types
, fixture
)
85 using namespace boost::numeric
;
86 using value_type
= typename
value::first_type
;
87 using layout_type
= typename
value::second_type
;
88 using tensor_type
= ublas::tensor
<value_type
,layout_type
>;
89 using matrix_type
= typename
tensor_type::matrix_type
;
92 for(auto const& n
: extents
) {
94 auto a
= tensor_type(n
, value_type
{2});
96 for(auto m
= 0u; m
< n
.size(); ++m
){
98 auto b
= matrix_type ( n
[m
], n
[m
], value_type
{1} );
100 auto c
= ublas::prod(a
, b
, m
+1);
102 for(auto i
= 0u; i
< c
.size(); ++i
)
103 BOOST_CHECK_EQUAL( c
[i
] , value_type(n
[m
]) * a
[i
] );
111 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_prod_tensor_1
, value
, test_types
, fixture
)
113 using namespace boost::numeric
;
114 using value_type
= typename
value::first_type
;
115 using layout_type
= typename
value::second_type
;
116 using tensor_type
= ublas::tensor
<value_type
,layout_type
>;
118 // left-hand and right-hand side have the
119 // the same number of elements
121 for(auto const& na
: extents
) {
123 auto a
= tensor_type( na
, value_type
{2} );
124 auto b
= tensor_type( na
, value_type
{3} );
126 auto const pa
= a
.rank();
128 // the number of contractions is changed.
129 for( auto q
= 0ul; q
<= pa
; ++q
) { // pa
131 auto phi
= std::vector
<std::size_t> ( q
);
133 std::iota(phi
.begin(), phi
.end(), 1ul);
135 auto c
= ublas::prod(a
, b
, phi
);
137 auto acc
= value_type(1);
138 for(auto i
= 0ul; i
< q
; ++i
)
139 acc
*= a
.extents().at(phi
.at(i
)-1);
141 for(auto i
= 0ul; i
< c
.size(); ++i
)
142 BOOST_CHECK_EQUAL( c
[i
] , acc
* a
[0] * b
[0] );
149 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_prod_tensor_2
, value
, test_types
, fixture
)
151 using namespace boost::numeric
;
152 using value_type
= typename
value::first_type
;
153 using layout_type
= typename
value::second_type
;
154 using tensor_type
= ublas::tensor
<value_type
,layout_type
>;
157 auto compute_factorial
= [](auto const& p
){
159 for(auto i
= 1u; i
<= p
; ++i
)
164 auto permute_extents
= [](auto const& pi
, auto const& na
){
166 assert(pi
.size() == na
.size());
167 for(auto j
= 0u; j
< pi
.size(); ++j
)
173 // left-hand and right-hand side have the
174 // the same number of elements
176 for(auto const& na
: extents
) {
178 auto a
= tensor_type( na
, value_type
{2} );
179 auto const pa
= a
.rank();
182 auto pi
= std::vector
<std::size_t>(pa
);
183 auto fac
= compute_factorial(pa
);
184 std::iota( pi
.begin(), pi
.end(), 1 );
186 for(auto f
= 0ul; f
< fac
; ++f
)
188 auto nb
= permute_extents( pi
, na
);
189 auto b
= tensor_type( nb
, value_type
{3} );
191 // the number of contractions is changed.
192 for( auto q
= 0ul; q
<= pa
; ++q
) { // pa
194 auto phia
= std::vector
<std::size_t> ( q
); // concatenation for a
195 auto phib
= std::vector
<std::size_t> ( q
); // concatenation for b
197 std::iota(phia
.begin(), phia
.end(), 1ul);
198 std::transform( phia
.begin(), phia
.end(), phib
.begin(),
199 [&pi
] ( std::size_t i
) { return pi
.at(i
-1); } );
201 auto c
= ublas::prod(a
, b
, phia
, phib
);
203 auto acc
= value_type(1);
204 for(auto i
= 0ul; i
< q
; ++i
)
205 acc
*= a
.extents().at(phia
.at(i
)-1);
207 for(auto i
= 0ul; i
< c
.size(); ++i
)
208 BOOST_CHECK_EQUAL( c
[i
] , acc
* a
[0] * b
[0] );
212 std::next_permutation(pi
.begin(), pi
.end());
221 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_inner_prod
, value
, test_types
, fixture
)
223 using namespace boost::numeric
;
224 using value_type
= typename
value::first_type
;
225 using layout_type
= typename
value::second_type
;
226 using tensor_type
= ublas::tensor
<value_type
,layout_type
>;
229 for(auto const& n
: extents
) {
231 auto a
= tensor_type(n
, value_type(2));
232 auto b
= tensor_type(n
, value_type(1));
234 auto c
= ublas::inner_prod(a
, b
);
235 auto r
= std::inner_product(a
.begin(),a
.end(), b
.begin(),value_type(0));
237 BOOST_CHECK_EQUAL( c
, r
);
243 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_norm
, value
, test_types
, fixture
)
245 using namespace boost::numeric
;
246 using value_type
= typename
value::first_type
;
247 using layout_type
= typename
value::second_type
;
248 using tensor_type
= ublas::tensor
<value_type
,layout_type
>;
251 for(auto const& n
: extents
) {
253 auto a
= tensor_type(n
);
255 auto one
= value_type(1);
261 auto c
= ublas::inner_prod(a
, a
);
262 auto r
= std::inner_product(a
.begin(),a
.end(), a
.begin(),value_type(0));
264 auto r2
= ublas::norm( (a
+a
) / 2 );
266 BOOST_CHECK_EQUAL( c
, r
);
267 BOOST_CHECK_EQUAL( std::sqrt( c
) , r2
);
273 BOOST_FIXTURE_TEST_CASE( test_tensor_real_imag_conj
, fixture
)
275 using namespace boost::numeric
;
276 using value_type
= float;
277 using complex_type
= std::complex<value_type
>;
278 using layout_type
= ublas::first_order
;
280 using tensor_complex_type
= ublas::tensor
<complex_type
,layout_type
>;
281 using tensor_type
= ublas::tensor
<value_type
,layout_type
>;
283 for(auto const& n
: extents
) {
285 auto a
= tensor_type(n
);
286 auto r0
= tensor_type(n
);
287 auto r00
= tensor_complex_type(n
);
290 auto one
= value_type(1);
295 tensor_type b
= (a
+a
) / value_type( 2 );
296 tensor_type r1
= ublas::real( (a
+a
) / value_type( 2 ) );
297 std::transform( b
.begin(), b
.end(), r0
.begin(), [](auto const& l
){ return std::real( l
); } );
298 BOOST_CHECK( r0
== r1
);
300 tensor_type r2
= ublas::imag( (a
+a
) / value_type( 2 ) );
301 std::transform( b
.begin(), b
.end(), r0
.begin(), [](auto const& l
){ return std::imag( l
); } );
302 BOOST_CHECK( r0
== r2
);
304 tensor_complex_type r3
= ublas::conj( (a
+a
) / value_type( 2 ) );
305 std::transform( b
.begin(), b
.end(), r00
.begin(), [](auto const& l
){ return std::conj( l
); } );
306 BOOST_CHECK( r00
== r3
);
310 for(auto const& n
: extents
) {
315 auto a
= tensor_complex_type(n
);
317 auto r00
= tensor_complex_type(n
);
318 auto r0
= tensor_type(n
);
321 auto one
= complex_type(1,1);
326 tensor_complex_type b
= (a
+a
) / complex_type( 2,2 );
329 tensor_type r1
= ublas::real( (a
+a
) / complex_type( 2,2 ) );
330 std::transform( b
.begin(), b
.end(), r0
.begin(), [](auto const& l
){ return std::real( l
); } );
331 BOOST_CHECK( r0
== r1
);
333 tensor_type r2
= ublas::imag( (a
+a
) / complex_type( 2,2 ) );
334 std::transform( b
.begin(), b
.end(), r0
.begin(), [](auto const& l
){ return std::imag( l
); } );
335 BOOST_CHECK( r0
== r2
);
337 tensor_complex_type r3
= ublas::conj( (a
+a
) / complex_type( 2,2 ) );
338 std::transform( b
.begin(), b
.end(), r00
.begin(), [](auto const& l
){ return std::conj( l
); } );
339 BOOST_CHECK( r00
== r3
);
353 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_outer_prod
, value
, test_types
, fixture
)
355 using namespace boost::numeric
;
356 using value_type
= typename
value::first_type
;
357 using layout_type
= typename
value::second_type
;
358 using tensor_type
= ublas::tensor
<value_type
,layout_type
>;
360 for(auto const& n1
: extents
) {
361 auto a
= tensor_type(n1
, value_type(2));
362 for(auto const& n2
: extents
) {
364 auto b
= tensor_type(n2
, value_type(1));
365 auto c
= ublas::outer_prod(a
, b
);
367 for(auto const& cc
: c
)
368 BOOST_CHECK_EQUAL( cc
, a
[0]*b
[0] );
376 void init(std::vector
<V
>& a
)
379 for(auto i
= 0u; i
< a
.size(); ++i
, ++v
){
385 void init(std::vector
<std::complex<V
>>& a
)
387 auto v
= std::complex<V
>(1,1);
388 for(auto i
= 0u; i
< a
.size(); ++i
){
395 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_trans
, value
, test_types
, fixture
)
397 using namespace boost::numeric
;
398 using value_type
= typename
value::first_type
;
399 using layout_type
= typename
value::second_type
;
400 using tensor_type
= ublas::tensor
<value_type
,layout_type
>;
402 auto fak
= [](auto const& p
){
404 for(auto i
= 1u; i
<= p
; ++i
)
409 auto inverse
= [](auto const& pi
){
411 for(auto j
= 0u; j
< pi
.size(); ++j
)
412 pi_inv
[pi
[j
]-1] = j
+1;
416 for(auto const& n
: extents
)
418 auto const p
= n
.size();
419 auto const s
= n
.product();
420 auto aref
= tensor_type(n
);
421 auto v
= value_type
{};
422 for(auto i
= 0u; i
< s
; ++i
, v
+=1)
427 auto pi
= std::vector
<std::size_t>(p
);
428 std::iota(pi
.begin(), pi
.end(), 1);
429 a
= ublas::trans( a
, pi
);
430 BOOST_CHECK( a
== aref
);
433 auto const pfak
= fak(p
);
435 for(; i
< pfak
-1; ++i
) {
436 std::next_permutation(pi
.begin(), pi
.end());
437 a
= ublas::trans( a
, pi
);
439 std::next_permutation(pi
.begin(), pi
.end());
441 std::prev_permutation(pi
.begin(), pi
.end());
442 auto pi_inv
= inverse(pi
);
443 a
= ublas::trans( a
, pi_inv
);
446 BOOST_CHECK( a
== aref
);
452 BOOST_AUTO_TEST_SUITE_END()