test_multiplication.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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. #include <iostream>
  12. #include <algorithm>
  13. #include <vector>
  14. #include <boost/numeric/ublas/tensor/multiplication.hpp>
  15. #include <boost/numeric/ublas/tensor/extents.hpp>
  16. #include <boost/numeric/ublas/tensor/strides.hpp>
  17. #include "utility.hpp"
  18. #include <boost/test/unit_test.hpp>
  19. BOOST_AUTO_TEST_SUITE (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{5,4}, // 5
  32. extents_type{2,3,1}, // 6
  33. extents_type{4,1,3}, // 7
  34. extents_type{1,2,3}, // 8
  35. extents_type{4,2,3}, // 9
  36. extents_type{4,2,3,5}} // 10
  37. {
  38. }
  39. std::vector<extents_type> extents;
  40. };
  41. BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_mtv, value, test_types, fixture )
  42. {
  43. using namespace boost::numeric;
  44. using value_type = typename value::first_type;
  45. using layout_type = typename value::second_type;
  46. using strides_type = ublas::strides<layout_type>;
  47. using vector_type = std::vector<value_type>;
  48. using extents_type = ublas::shape;
  49. using extents_type_base = typename extents_type::base_type;
  50. using size_type = typename extents_type_base::value_type;
  51. for(auto const& na : extents) {
  52. if(na.size() > 2)
  53. continue;
  54. auto a = vector_type(na.product(), value_type{2});
  55. auto wa = strides_type(na);
  56. for(auto m = 0u; m < na.size(); ++m){
  57. auto nb = extents_type {na[m],1};
  58. auto wb = strides_type (nb);
  59. auto b = vector_type (nb.product(), value_type{1} );
  60. auto nc_base = extents_type_base(std::max(na.size()-1, size_type{2}), 1);
  61. for(auto i = 0u, j = 0u; i < na.size(); ++i)
  62. if(i != m)
  63. nc_base[j++] = na[i];
  64. auto nc = extents_type (nc_base);
  65. auto wc = strides_type (nc);
  66. auto c = vector_type (nc.product(), value_type{0});
  67. ublas::detail::recursive::mtv(
  68. size_type(m),
  69. c.data(), nc.data(), wc.data(),
  70. a.data(), na.data(), wa.data(),
  71. b.data());
  72. for(auto i = 0u; i < c.size(); ++i)
  73. BOOST_CHECK_EQUAL( c[i] , value_type(na[m]) * a[i] );
  74. }
  75. }
  76. }
  77. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_mtm, value, test_types, fixture )
  78. {
  79. using namespace boost::numeric;
  80. using value_type = typename value::first_type;
  81. using layout_type = typename value::second_type;
  82. using strides_type = ublas::strides<layout_type>;
  83. using vector_type = std::vector<value_type>;
  84. using extents_type = ublas::shape;
  85. // using extents_type_base = typename extents_type::base_type;
  86. for(auto const& na : extents) {
  87. if(na.size() != 2)
  88. continue;
  89. auto a = vector_type (na.product(), value_type{2});
  90. auto wa = strides_type (na);
  91. auto nb = extents_type {na[1],na[0]};
  92. auto wb = strides_type (nb);
  93. auto b = vector_type (nb.product(), value_type{1} );
  94. auto nc = extents_type {na[0],nb[1]};
  95. auto wc = strides_type (nc);
  96. auto c = vector_type (nc.product());
  97. ublas::detail::recursive::mtm(
  98. c.data(), nc.data(), wc.data(),
  99. a.data(), na.data(), wa.data(),
  100. b.data(), nb.data(), wb.data());
  101. for(auto i = 0u; i < c.size(); ++i)
  102. BOOST_CHECK_EQUAL( c[i] , value_type(na[1]) * a[0] );
  103. }
  104. }
  105. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ttv, value, test_types, fixture )
  106. {
  107. using namespace boost::numeric;
  108. using value_type = typename value::first_type;
  109. using layout_type = typename value::second_type;
  110. using strides_type = ublas::strides<layout_type>;
  111. using vector_type = std::vector<value_type>;
  112. using extents_type = ublas::shape;
  113. using extents_type_base = typename extents_type::base_type;
  114. using size_type = typename extents_type_base::value_type;
  115. for(auto const& na : extents) {
  116. auto a = vector_type(na.product(), value_type{2});
  117. auto wa = strides_type(na);
  118. for(auto m = 0u; m < na.size(); ++m){
  119. auto b = vector_type (na[m], value_type{1} );
  120. auto nb = extents_type {na[m],1};
  121. auto wb = strides_type (nb);
  122. auto nc_base = extents_type_base(std::max(na.size()-1, size_type(2)),1);
  123. for(auto i = 0ul, j = 0ul; i < na.size(); ++i)
  124. if(i != m)
  125. nc_base[j++] = na[i];
  126. auto nc = extents_type (nc_base);
  127. auto wc = strides_type (nc);
  128. auto c = vector_type (nc.product(), value_type{0});
  129. ublas::ttv(size_type(m+1), na.size(),
  130. c.data(), nc.data(), wc.data(),
  131. a.data(), na.data(), wa.data(),
  132. b.data(), nb.data(), wb.data());
  133. for(auto i = 0u; i < c.size(); ++i)
  134. BOOST_CHECK_EQUAL( c[i] , value_type(na[m]) * a[i] );
  135. }
  136. }
  137. }
  138. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ttm, value, test_types, fixture )
  139. {
  140. using namespace boost::numeric;
  141. using value_type = typename value::first_type;
  142. using layout_type = typename value::second_type;
  143. using strides_type = ublas::strides<layout_type>;
  144. using vector_type = std::vector<value_type>;
  145. using extents_type = ublas::shape;
  146. using size_type = typename extents_type::value_type;
  147. for(auto const& na : extents) {
  148. auto a = vector_type(na.product(), value_type{2});
  149. auto wa = strides_type(na);
  150. for(auto m = 0u; m < na.size(); ++m){
  151. auto nb = extents_type {na[m], na[m] };
  152. auto b = vector_type (nb.product(), value_type{1} );
  153. auto wb = strides_type (nb);
  154. auto nc = na;
  155. auto wc = strides_type (nc);
  156. auto c = vector_type (nc.product(), value_type{0});
  157. ublas::ttm(size_type(m+1), na.size(),
  158. c.data(), nc.data(), wc.data(),
  159. a.data(), na.data(), wa.data(),
  160. b.data(), nb.data(), wb.data());
  161. for(auto i = 0u; i < c.size(); ++i)
  162. BOOST_CHECK_EQUAL( c[i] , value_type(na[m]) * a[i] );
  163. }
  164. }
  165. }
  166. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ttt_permutation, value, test_types, fixture )
  167. {
  168. using namespace boost::numeric;
  169. using value_type = typename value::first_type;
  170. using layout_type = typename value::second_type;
  171. using strides_type = ublas::strides<layout_type>;
  172. using vector_type = std::vector<value_type>;
  173. using extents_type = ublas::shape;
  174. using size_type = typename strides_type::value_type;
  175. auto compute_factorial = [](auto const& p){
  176. auto f = 1ul;
  177. for(auto i = 1u; i <= p; ++i)
  178. f *= i;
  179. return f;
  180. };
  181. auto compute_inverse_permutation = [](auto const& pi){
  182. auto pi_inv = pi;
  183. for(auto j = 0u; j < pi.size(); ++j)
  184. pi_inv[pi[j]-1] = j+1;
  185. return pi_inv;
  186. };
  187. auto permute_extents = [](auto const& pi, auto const& na){
  188. auto nb = na;
  189. assert(pi.size() == na.size());
  190. for(auto j = 0u; j < pi.size(); ++j)
  191. nb[j] = na[pi[j]-1];
  192. return nb;
  193. };
  194. // left-hand and right-hand side have the
  195. // the same number of elements
  196. // computing the inner product with
  197. // different permutation tuples for
  198. // right-hand side
  199. for(auto const& na : extents) {
  200. auto wa = strides_type(na);
  201. auto a = vector_type(na.product(), value_type{2});
  202. auto pa = na.size();
  203. auto pia = std::vector<size_type>(pa);
  204. std::iota( pia.begin(), pia.end(), 1 );
  205. auto pib = pia;
  206. auto pib_inv = compute_inverse_permutation(pib);
  207. auto f = compute_factorial(pa);
  208. // for the number of possible permutations
  209. // only permutation tuple pib is changed.
  210. for(auto i = 0u; i < f; ++i) {
  211. auto nb = permute_extents( pib, na );
  212. auto wb = strides_type(nb);
  213. auto b = vector_type(nb.product(), value_type{3});
  214. auto pb = nb.size();
  215. // the number of contractions is changed.
  216. for( auto q = size_type(0); q <= pa; ++q) {
  217. auto r = pa - q;
  218. auto s = pb - q;
  219. auto pc = r+s > 0 ? std::max(r+s,size_type(2)) : size_type(2);
  220. auto nc_base = std::vector<size_type>( pc , 1 );
  221. for(auto i = 0u; i < r; ++i)
  222. nc_base[ i ] = na[ pia[i]-1 ];
  223. for(auto i = 0u; i < s; ++i)
  224. nc_base[ r + i ] = nb[ pib_inv[i]-1 ];
  225. auto nc = extents_type ( nc_base );
  226. auto wc = strides_type ( nc );
  227. auto c = vector_type ( nc.product(), value_type(0) );
  228. ublas::ttt(pa,pb,q,
  229. pia.data(), pib_inv.data(),
  230. c.data(), nc.data(), wc.data(),
  231. a.data(), na.data(), wa.data(),
  232. b.data(), nb.data(), wb.data());
  233. auto acc = value_type(1);
  234. for(auto i = r; i < pa; ++i)
  235. acc *= value_type(na[pia[i]-1]);
  236. for(auto i = 0ul; i < c.size(); ++i)
  237. BOOST_CHECK_EQUAL( c[i] , acc * a[0] * b[0] );
  238. }
  239. std::next_permutation(pib.begin(), pib.end());
  240. pib_inv = compute_inverse_permutation(pib);
  241. }
  242. }
  243. }
  244. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ttt, value, test_types, fixture )
  245. {
  246. using namespace boost::numeric;
  247. using value_type = typename value::first_type;
  248. using layout_type = typename value::second_type;
  249. using strides_type = ublas::strides<layout_type>;
  250. using vector_type = std::vector<value_type>;
  251. using extents_type = ublas::shape;
  252. using size_type = typename strides_type::value_type;
  253. // left-hand and right-hand side have the
  254. // the same number of elements
  255. // computing the inner product with
  256. // different permutation tuples for
  257. // right-hand side
  258. for(auto const& na : extents) {
  259. auto wa = strides_type(na);
  260. auto a = vector_type(na.product(), value_type{2});
  261. auto pa = na.size();
  262. auto nb = na;
  263. auto wb = strides_type(nb);
  264. auto b = vector_type(nb.product(), value_type{3});
  265. auto pb = nb.size();
  266. // std::cout << "na = ";
  267. // std::copy(na.begin(), na.end(), std::ostream_iterator<size_type>(std::cout, " "));
  268. // std::cout << std::endl;
  269. // std::cout << "nb = ";
  270. // std::copy(nb.begin(), nb.end(), std::ostream_iterator<size_type>(std::cout, " "));
  271. // std::cout << std::endl;
  272. // the number of contractions is changed.
  273. for( auto q = size_type(0); q <= pa; ++q) { // pa
  274. auto r = pa - q;
  275. auto s = pb - q;
  276. auto pc = r+s > 0 ? std::max(r+s, size_type(2)) : size_type(2);
  277. auto nc_base = std::vector<size_type>( pc , 1 );
  278. for(auto i = 0u; i < r; ++i)
  279. nc_base[ i ] = na[ i ];
  280. for(auto i = 0u; i < s; ++i)
  281. nc_base[ r + i ] = nb[ i ];
  282. auto nc = extents_type ( nc_base );
  283. auto wc = strides_type ( nc );
  284. auto c = vector_type ( nc.product(), value_type{0} );
  285. // std::cout << "nc = ";
  286. // std::copy(nc.begin(), nc.end(), std::ostream_iterator<size_type>(std::cout, " "));
  287. // std::cout << std::endl;
  288. ublas::ttt(pa,pb,q,
  289. c.data(), nc.data(), wc.data(),
  290. a.data(), na.data(), wa.data(),
  291. b.data(), nb.data(), wb.data());
  292. auto acc = value_type(1);
  293. for(auto i = r; i < pa; ++i)
  294. acc *= value_type(na[i]);
  295. for(auto i = 0u; i < c.size(); ++i)
  296. BOOST_CHECK_EQUAL( c[i] , acc * a[0] * b[0] );
  297. }
  298. }
  299. }
  300. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_inner, value, test_types, fixture )
  301. {
  302. using namespace boost::numeric;
  303. using value_type = typename value::first_type;
  304. using layout_type = typename value::second_type;
  305. using strides_type = ublas::strides<layout_type>;
  306. using vector_type = std::vector<value_type>;
  307. for(auto const& n : extents) {
  308. auto a = vector_type(n.product(), value_type{2});
  309. auto b = vector_type(n.product(), value_type{3});
  310. auto w = strides_type(n);
  311. auto c = ublas::inner(n.size(), n.data(), a.data(), w.data(), b.data(), w.data(), value_type(0));
  312. auto cref = std::inner_product(a.begin(), a.end(), b.begin(), value_type(0));
  313. BOOST_CHECK_EQUAL( c , cref );
  314. }
  315. }
  316. BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_outer, value, test_types, fixture )
  317. {
  318. using namespace boost::numeric;
  319. using value_type = typename value::first_type;
  320. using layout_type = typename value::second_type;
  321. using extents_type = ublas::shape;
  322. using strides_type = ublas::strides<layout_type>;
  323. using vector_type = std::vector<value_type>;
  324. for(auto const& na : extents) {
  325. auto a = vector_type(na.product(), value_type{2});
  326. auto wa = strides_type(na);
  327. for(auto const& nb : extents) {
  328. auto b = vector_type(nb.product(), value_type{3});
  329. auto wb = strides_type(nb);
  330. auto c = vector_type(nb.product()*na.product());
  331. auto nc = typename extents_type::base_type(na.size()+nb.size());
  332. for(auto i = 0u; i < na.size(); ++i)
  333. nc[i] = na[i];
  334. for(auto i = 0u; i < nb.size(); ++i)
  335. nc[i+na.size()] = nb[i];
  336. auto wc = strides_type(extents_type(nc));
  337. ublas::outer(c.data(), nc.size(), nc.data(), wc.data(),
  338. a.data(), na.size(), na.data(), wa.data(),
  339. b.data(), nb.size(), nb.data(), wb.data());
  340. for(auto const& cc : c)
  341. BOOST_CHECK_EQUAL( cc , a[0]*b[0] );
  342. }
  343. }
  344. }
  345. BOOST_AUTO_TEST_SUITE_END()