test_functions.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. // Copyright (c) 2018-2019 Cem Bassoy
  2. //
  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)
  6. //
  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.
  10. //
  11. // And we acknowledge the support from all contributors.
  12. #include <iostream>
  13. #include <algorithm>
  14. #include <boost/numeric/ublas/tensor.hpp>
  15. #include <boost/numeric/ublas/matrix.hpp>
  16. #include <boost/numeric/ublas/vector.hpp>
  17. #include <boost/test/unit_test.hpp>
  18. #include "utility.hpp"
  19. BOOST_AUTO_TEST_SUITE ( test_tensor_functions, * boost::unit_test::depends_on("test_tensor_contraction") )
  20. using test_types = zip<int,long,float,double,std::complex<float>>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
  21. //using test_types = zip<int>::with_t<boost::numeric::ublas::first_order>;
  22. struct fixture
  23. {
  24. using extents_type = boost::numeric::ublas::shape;
  25. fixture()
  26. : extents {
  27. extents_type{1,1}, // 1
  28. extents_type{1,2}, // 2
  29. extents_type{2,1}, // 3
  30. extents_type{2,3}, // 4
  31. extents_type{2,3,1}, // 5
  32. extents_type{4,1,3}, // 6
  33. extents_type{1,2,3}, // 7
  34. extents_type{4,2,3}, // 8
  35. extents_type{4,2,3,5}} // 9
  36. {
  37. }
  38. std::vector<extents_type> extents;
  39. };
  40. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_prod_vector, value, test_types, fixture )
  41. {
  42. using namespace boost::numeric;
  43. using value_type = typename value::first_type;
  44. using layout_type = typename value::second_type;
  45. using tensor_type = ublas::tensor<value_type,layout_type>;
  46. using vector_type = typename tensor_type::vector_type;
  47. for(auto const& n : extents){
  48. auto a = tensor_type(n, value_type{2});
  49. for(auto m = 0u; m < n.size(); ++m){
  50. auto b = vector_type (n[m], value_type{1} );
  51. auto c = ublas::prod(a, b, m+1);
  52. for(auto i = 0u; i < c.size(); ++i)
  53. BOOST_CHECK_EQUAL( c[i] , value_type(n[m]) * a[i] );
  54. }
  55. }
  56. }
  57. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_prod_matrix, value, test_types, fixture )
  58. {
  59. using namespace boost::numeric;
  60. using value_type = typename value::first_type;
  61. using layout_type = typename value::second_type;
  62. using tensor_type = ublas::tensor<value_type,layout_type>;
  63. using matrix_type = typename tensor_type::matrix_type;
  64. for(auto const& n : extents) {
  65. auto a = tensor_type(n, value_type{2});
  66. for(auto m = 0u; m < n.size(); ++m){
  67. auto b = matrix_type ( n[m], n[m], value_type{1} );
  68. auto c = ublas::prod(a, b, m+1);
  69. for(auto i = 0u; i < c.size(); ++i)
  70. BOOST_CHECK_EQUAL( c[i] , value_type(n[m]) * a[i] );
  71. }
  72. }
  73. }
  74. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_prod_tensor_1, value, test_types, fixture )
  75. {
  76. using namespace boost::numeric;
  77. using value_type = typename value::first_type;
  78. using layout_type = typename value::second_type;
  79. using tensor_type = ublas::tensor<value_type,layout_type>;
  80. // left-hand and right-hand side have the
  81. // the same number of elements
  82. for(auto const& na : extents) {
  83. auto a = tensor_type( na, value_type{2} );
  84. auto b = tensor_type( na, value_type{3} );
  85. auto const pa = a.rank();
  86. // the number of contractions is changed.
  87. for( auto q = 0ul; q <= pa; ++q) { // pa
  88. auto phi = std::vector<std::size_t> ( q );
  89. std::iota(phi.begin(), phi.end(), 1ul);
  90. auto c = ublas::prod(a, b, phi);
  91. auto acc = value_type(1);
  92. for(auto i = 0ul; i < q; ++i)
  93. acc *= a.extents().at(phi.at(i)-1);
  94. for(auto i = 0ul; i < c.size(); ++i)
  95. BOOST_CHECK_EQUAL( c[i] , acc * a[0] * b[0] );
  96. }
  97. }
  98. }
  99. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_prod_tensor_2, value, test_types, fixture )
  100. {
  101. using namespace boost::numeric;
  102. using value_type = typename value::first_type;
  103. using layout_type = typename value::second_type;
  104. using tensor_type = ublas::tensor<value_type,layout_type>;
  105. auto compute_factorial = [](auto const& p){
  106. auto f = 1ul;
  107. for(auto i = 1u; i <= p; ++i)
  108. f *= i;
  109. return f;
  110. };
  111. auto permute_extents = [](auto const& pi, auto const& na){
  112. auto nb = na;
  113. assert(pi.size() == na.size());
  114. for(auto j = 0u; j < pi.size(); ++j)
  115. nb[pi[j]-1] = na[j];
  116. return nb;
  117. };
  118. // left-hand and right-hand side have the
  119. // the same number of elements
  120. for(auto const& na : extents) {
  121. auto a = tensor_type( na, value_type{2} );
  122. auto const pa = a.rank();
  123. auto pi = std::vector<std::size_t>(pa);
  124. auto fac = compute_factorial(pa);
  125. std::iota( pi.begin(), pi.end(), 1 );
  126. for(auto f = 0ul; f < fac; ++f)
  127. {
  128. auto nb = permute_extents( pi, na );
  129. auto b = tensor_type( nb, value_type{3} );
  130. // the number of contractions is changed.
  131. for( auto q = 0ul; q <= pa; ++q) { // pa
  132. auto phia = std::vector<std::size_t> ( q ); // concatenation for a
  133. auto phib = std::vector<std::size_t> ( q ); // concatenation for b
  134. std::iota(phia.begin(), phia.end(), 1ul);
  135. std::transform( phia.begin(), phia.end(), phib.begin(),
  136. [&pi] ( std::size_t i ) { return pi.at(i-1); } );
  137. auto c = ublas::prod(a, b, phia, phib);
  138. auto acc = value_type(1);
  139. for(auto i = 0ul; i < q; ++i)
  140. acc *= a.extents().at(phia.at(i)-1);
  141. for(auto i = 0ul; i < c.size(); ++i)
  142. BOOST_CHECK_EQUAL( c[i] , acc * a[0] * b[0] );
  143. }
  144. std::next_permutation(pi.begin(), pi.end());
  145. }
  146. }
  147. }
  148. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_inner_prod, value, test_types, fixture )
  149. {
  150. using namespace boost::numeric;
  151. using value_type = typename value::first_type;
  152. using layout_type = typename value::second_type;
  153. using tensor_type = ublas::tensor<value_type,layout_type>;
  154. for(auto const& n : extents) {
  155. auto a = tensor_type(n, value_type(2));
  156. auto b = tensor_type(n, value_type(1));
  157. auto c = ublas::inner_prod(a, b);
  158. auto r = std::inner_product(a.begin(),a.end(), b.begin(),value_type(0));
  159. BOOST_CHECK_EQUAL( c , r );
  160. }
  161. }
  162. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_norm, value, test_types, fixture )
  163. {
  164. using namespace boost::numeric;
  165. using value_type = typename value::first_type;
  166. using layout_type = typename value::second_type;
  167. using tensor_type = ublas::tensor<value_type,layout_type>;
  168. for(auto const& n : extents) {
  169. auto a = tensor_type(n);
  170. auto one = value_type(1);
  171. auto v = one;
  172. for(auto& aa: a)
  173. aa = v, v += one;
  174. auto c = ublas::inner_prod(a, a);
  175. auto r = std::inner_product(a.begin(),a.end(), a.begin(),value_type(0));
  176. auto r2 = ublas::norm( (a+a) / 2 );
  177. BOOST_CHECK_EQUAL( c , r );
  178. BOOST_CHECK_EQUAL( std::sqrt( c ) , r2 );
  179. }
  180. }
  181. BOOST_FIXTURE_TEST_CASE( test_tensor_real_imag_conj, fixture )
  182. {
  183. using namespace boost::numeric;
  184. using value_type = float;
  185. using complex_type = std::complex<value_type>;
  186. using layout_type = ublas::first_order;
  187. using tensor_complex_type = ublas::tensor<complex_type,layout_type>;
  188. using tensor_type = ublas::tensor<value_type,layout_type>;
  189. for(auto const& n : extents) {
  190. auto a = tensor_type(n);
  191. auto r0 = tensor_type(n);
  192. auto r00 = tensor_complex_type(n);
  193. auto one = value_type(1);
  194. auto v = one;
  195. for(auto& aa: a)
  196. aa = v, v += one;
  197. tensor_type b = (a+a) / value_type( 2 );
  198. tensor_type r1 = ublas::real( (a+a) / value_type( 2 ) );
  199. std::transform( b.begin(), b.end(), r0.begin(), [](auto const& l){ return std::real( l ); } );
  200. BOOST_CHECK( r0 == r1 );
  201. tensor_type r2 = ublas::imag( (a+a) / value_type( 2 ) );
  202. std::transform( b.begin(), b.end(), r0.begin(), [](auto const& l){ return std::imag( l ); } );
  203. BOOST_CHECK( r0 == r2 );
  204. tensor_complex_type r3 = ublas::conj( (a+a) / value_type( 2 ) );
  205. std::transform( b.begin(), b.end(), r00.begin(), [](auto const& l){ return std::conj( l ); } );
  206. BOOST_CHECK( r00 == r3 );
  207. }
  208. for(auto const& n : extents) {
  209. auto a = tensor_complex_type(n);
  210. auto r00 = tensor_complex_type(n);
  211. auto r0 = tensor_type(n);
  212. auto one = complex_type(1,1);
  213. auto v = one;
  214. for(auto& aa: a)
  215. aa = v, v = v + one;
  216. tensor_complex_type b = (a+a) / complex_type( 2,2 );
  217. tensor_type r1 = ublas::real( (a+a) / complex_type( 2,2 ) );
  218. std::transform( b.begin(), b.end(), r0.begin(), [](auto const& l){ return std::real( l ); } );
  219. BOOST_CHECK( r0 == r1 );
  220. tensor_type r2 = ublas::imag( (a+a) / complex_type( 2,2 ) );
  221. std::transform( b.begin(), b.end(), r0.begin(), [](auto const& l){ return std::imag( l ); } );
  222. BOOST_CHECK( r0 == r2 );
  223. tensor_complex_type r3 = ublas::conj( (a+a) / complex_type( 2,2 ) );
  224. std::transform( b.begin(), b.end(), r00.begin(), [](auto const& l){ return std::conj( l ); } );
  225. BOOST_CHECK( r00 == r3 );
  226. }
  227. }
  228. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_outer_prod, value, test_types, fixture )
  229. {
  230. using namespace boost::numeric;
  231. using value_type = typename value::first_type;
  232. using layout_type = typename value::second_type;
  233. using tensor_type = ublas::tensor<value_type,layout_type>;
  234. for(auto const& n1 : extents) {
  235. auto a = tensor_type(n1, value_type(2));
  236. for(auto const& n2 : extents) {
  237. auto b = tensor_type(n2, value_type(1));
  238. auto c = ublas::outer_prod(a, b);
  239. for(auto const& cc : c)
  240. BOOST_CHECK_EQUAL( cc , a[0]*b[0] );
  241. }
  242. }
  243. }
  244. template<class V>
  245. void init(std::vector<V>& a)
  246. {
  247. auto v = V(1);
  248. for(auto i = 0u; i < a.size(); ++i, ++v){
  249. a[i] = v;
  250. }
  251. }
  252. template<class V>
  253. void init(std::vector<std::complex<V>>& a)
  254. {
  255. auto v = std::complex<V>(1,1);
  256. for(auto i = 0u; i < a.size(); ++i){
  257. a[i] = v;
  258. v.real(v.real()+1);
  259. v.imag(v.imag()+1);
  260. }
  261. }
  262. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_trans, value, test_types, fixture )
  263. {
  264. using namespace boost::numeric;
  265. using value_type = typename value::first_type;
  266. using layout_type = typename value::second_type;
  267. using tensor_type = ublas::tensor<value_type,layout_type>;
  268. auto fak = [](auto const& p){
  269. auto f = 1ul;
  270. for(auto i = 1u; i <= p; ++i)
  271. f *= i;
  272. return f;
  273. };
  274. auto inverse = [](auto const& pi){
  275. auto pi_inv = pi;
  276. for(auto j = 0u; j < pi.size(); ++j)
  277. pi_inv[pi[j]-1] = j+1;
  278. return pi_inv;
  279. };
  280. for(auto const& n : extents)
  281. {
  282. auto const p = n.size();
  283. auto const s = n.product();
  284. auto aref = tensor_type(n);
  285. auto v = value_type{};
  286. for(auto i = 0u; i < s; ++i, v+=1)
  287. aref[i] = v;
  288. auto a = aref;
  289. auto pi = std::vector<std::size_t>(p);
  290. std::iota(pi.begin(), pi.end(), 1);
  291. a = ublas::trans( a, pi );
  292. BOOST_CHECK( a == aref );
  293. auto const pfak = fak(p);
  294. auto i = 0u;
  295. for(; i < pfak-1; ++i) {
  296. std::next_permutation(pi.begin(), pi.end());
  297. a = ublas::trans( a, pi );
  298. }
  299. std::next_permutation(pi.begin(), pi.end());
  300. for(; i > 0; --i) {
  301. std::prev_permutation(pi.begin(), pi.end());
  302. auto pi_inv = inverse(pi);
  303. a = ublas::trans( a, pi_inv );
  304. }
  305. BOOST_CHECK( a == aref );
  306. }
  307. }
  308. BOOST_AUTO_TEST_SUITE_END()