test-reduction.C 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. // (c) Daniel Llorens - 2014
  2. // This library is free software; you can redistribute it and/or modify it under
  3. // the terms of the GNU Lesser General Public License as published by the Free
  4. // Software Foundation; either version 3 of the License, or (at your option) any
  5. // later version.
  6. /// @file test-reduction.C
  7. /// @brief Test array reductions.
  8. #include <iostream>
  9. #include <iterator>
  10. #include "ra/mpdebug.H"
  11. #include "ra/complex.H"
  12. #include "ra/format.H"
  13. #include "ra/test.H"
  14. #include "ra/big.H"
  15. #include "ra/operators.H"
  16. #include "ra/io.H"
  17. using std::cout; using std::endl; using std::flush; using std::tuple;
  18. using real = double;
  19. using complex = std::complex<double>;
  20. using ra::QNAN;
  21. int main()
  22. {
  23. TestRecorder tr(std::cout);
  24. tr.section("amax with different expr types");
  25. {
  26. auto test_amax_expr = [&tr](auto && a, auto && b)
  27. {
  28. a = ra::Small<real, 2, 2> {1, 2, 9, -10};
  29. tr.test_eq(amax(a), 9);
  30. b = ra::Small<real, 2, 2> {1, 1, 1, 1};
  31. tr.test_eq(amax(a+b), 10);
  32. };
  33. test_amax_expr(ra::Unique<real, 2>({2, 2}, 0.), ra::Unique<real, 2>({2, 2}, 0.));
  34. test_amax_expr(ra::Small<real, 2, 2>(), ra::Small<real, 2, 2>());
  35. // failed in gcc 5.1 when amax() took its args by plain auto (now auto &&).
  36. test_amax_expr(ra::Unique<real, 2>({2, 2}, 0.), ra::Small<real, 2, 2>());
  37. }
  38. tr.section("every / any");
  39. {
  40. tr.test(every(ra::Unique<real, 2>({4, 4}, 10+ra::_0-ra::_1)));
  41. tr.test(any(ra::Unique<real, 2>({4, 4}, ra::_0-ra::_1)));
  42. tr.test(every(true));
  43. tr.test(!every(false));
  44. tr.test(any(true));
  45. tr.test(!any(false));
  46. tr.test(every(ra::Unique<int, 1> {5, 5}==5));
  47. tr.test(!every(ra::Unique<int, 1> {2, 5}==5));
  48. tr.test(!every(ra::Unique<int, 1> {5, 2}==5));
  49. tr.test(!every(ra::Unique<int, 1> {2, 3}==5));
  50. tr.test(any(ra::Unique<int, 1> {5, 5}==5));
  51. tr.test(any(ra::Unique<int, 1> {2, 5}==5));
  52. tr.test(any(ra::Unique<int, 1> {5, 2}==5));
  53. tr.test(!any(ra::Unique<int, 1> {2, 3}==5));
  54. }
  55. tr.section("norm2");
  56. {
  57. ra::Small<real, 2> a {1, 2};
  58. tr.test_abs_error(std::sqrt(5.), norm2(a), 1e-15);
  59. }
  60. tr.section("normv");
  61. {
  62. ra::Small<real, 2> a {1, 2};
  63. ra::Small<real, 2> b;
  64. b = normv(a);
  65. cout << "normv of lvalue: " << b << endl;
  66. tr.test_eq(b[0], 1./sqrt(5));
  67. tr.test_eq(b[1], 2./sqrt(5));
  68. b = normv(ra::Small<real, 2> {2, 1});
  69. cout << "normv of rvalue: "<< b << endl;
  70. tr.test_eq(b[0], 2./sqrt(5));
  71. tr.test_eq(b[1], 1./sqrt(5));
  72. }
  73. tr.section("reductions");
  74. {
  75. auto test_dot = [&tr](auto && test) // TODO Use this for other real reductions.
  76. {
  77. test(ra::Small<complex, 2>{1, 2}, ra::Small<real, 2>{3, 4});
  78. test(ra::Small<real, 2>{1, 2}, ra::Small<complex, 2>{3, 4});
  79. test(ra::Small<real, 2>{1, 2}, ra::Small<real, 2>{3, 4});
  80. test(ra::Small<complex, 2>{1, 2}, ra::Small<complex, 2>{3, 4});
  81. test(ra::Big<complex, 1>{1, 2}, ra::Big<real, 1>{3, 4});
  82. test(ra::Big<real, 1>{1, 2}, ra::Big<complex, 1>{3, 4});
  83. test(ra::Big<real, 1>{1, 2}, ra::Big<real, 1>{3, 4});
  84. test(ra::Big<complex, 1>{1, 2}, ra::Big<complex, 1>{3, 4});
  85. test(ra::Small<complex, 2>{1, 2}, ra::Big<real, 1>{3, 4});
  86. test(ra::Small<real, 2>{1, 2}, ra::Big<complex, 1>{3, 4});
  87. test(ra::Small<real, 2>{1, 2}, ra::Big<real, 1>{3, 4});
  88. test(ra::Small<complex, 2>{1, 2}, ra::Big<complex, 1>{3, 4});
  89. test(ra::Big<complex, 1>{1, 2}, ra::Small<real, 2>{3, 4});
  90. test(ra::Big<real, 1>{1, 2}, ra::Small<complex, 2>{3, 4});
  91. test(ra::Big<real, 1>{1, 2}, ra::Small<real, 2>{3, 4});
  92. test(ra::Big<complex, 1>{1, 2}, ra::Small<complex, 2>{3, 4});
  93. };
  94. test_dot([&tr](auto && a, auto && b) { tr.test_eq(11., dot(a, b)); });
  95. test_dot([&tr](auto && a, auto && b) { tr.test_eq(11., cdot(a, b)); });
  96. test_dot([&tr](auto && a, auto && b) { tr.test_eq(sqrt(8.), norm2(a-b)); });
  97. test_dot([&tr](auto && a, auto && b) { tr.test_eq(8., reduce_sqrm(a-b)); });
  98. auto test_cdot = [&tr](auto && test)
  99. {
  100. test(ra::Small<complex, 2>{1, complex(2, 3)}, ra::Small<complex, 2>{complex(4, 5), 6});
  101. test(ra::Big<complex, 1>{1, complex(2, 3)}, ra::Small<complex, 2>{complex(4, 5), 6});
  102. test(ra::Small<complex, 2>{1, complex(2, 3)}, ra::Big<complex, 1>{complex(4, 5), 6});
  103. test(ra::Big<complex, 1>{1, complex(2, 3)}, ra::Big<complex, 1>{complex(4, 5), 6});
  104. };
  105. complex value = conj(1.)*complex(4., 5.) + conj(complex(2., 3.))*6.;
  106. tr.test_eq(value, complex(16, -13));
  107. test_cdot([&tr](auto && a, auto && b) { tr.test_eq(complex(16., -13.), cdot(a, b)); });
  108. test_cdot([&tr](auto && a, auto && b) { tr.test_eq(sqrt(59.), norm2(a-b)); });
  109. test_cdot([&tr](auto && a, auto && b) { tr.test_eq(59., reduce_sqrm(a-b)); });
  110. auto test_sum = [&tr](auto && test)
  111. {
  112. test(ra::Small<complex, 2>{complex(4, 5), 6});
  113. test(ra::Big<complex, 1>{complex(4, 5), 6});
  114. };
  115. test_sum([&tr](auto && a) { tr.test_eq(complex(10, 5), sum(a)); });
  116. test_sum([&tr](auto && a) { tr.test_eq(complex(24, 30), prod(a)); });
  117. test_sum([&tr](auto && a) { tr.test_eq(sqrt(41.), amax(abs(a))); });
  118. test_sum([&tr](auto && a) { tr.test_eq(6., amin(abs(a))); });
  119. }
  120. tr.section("amax/amin ignore NaN");
  121. {
  122. tr.test_eq(std::numeric_limits<real>::lowest(), std::max(std::numeric_limits<real>::lowest(), QNAN));
  123. tr.test_eq(-std::numeric_limits<real>::infinity(), amax(ra::Small<real, 3>(QNAN)));
  124. tr.test_eq(std::numeric_limits<real>::infinity(), amin(ra::Small<real, 3>(QNAN)));
  125. }
  126. // TODO these reductions require a destination argument; there are no exprs really.
  127. tr.section("to sum columns in crude ways");
  128. {
  129. ra::Unique<real, 2> A({100, 111}, ra::_0 - ra::_1);
  130. ra::Unique<real, 1> B({100}, 0.);
  131. for (int i=0, iend=A.size(0); i<iend; ++i) {
  132. B(i) = sum(A(i));
  133. }
  134. {
  135. ra::Unique<real, 1> C({100}, 0.);
  136. for_each([](auto & c, auto a) { c += a; }, C, A);
  137. tr.test_eq(B, C);
  138. }
  139. // This depends on matching frames for += just as for any other op, which is at odds with e.g. amend.
  140. {
  141. ra::Unique<real, 1> C({100}, 0.);
  142. C += A;
  143. tr.test_eq(B, C);
  144. }
  145. // Same as above.
  146. {
  147. ra::Unique<real, 1> C({100}, 0.);
  148. C = C + A;
  149. tr.test_eq(B, C);
  150. }
  151. // It cannot work with a lhs scalar value since += must be a class member, but it will work with a rank 0 array or with ra::Scalar.
  152. {
  153. ra::Unique<real, 0> C({}, 0.);
  154. C += A(0);
  155. tr.test_eq(B(0), C);
  156. real c(0.);
  157. ra::scalar(c) += A(0);
  158. tr.test_eq(B(0), c);
  159. }
  160. // This will fail because the assumed driver (RANK_ANY) has lower actual rank than the other argument. TODO check that it fails.
  161. // {
  162. // ra::Unique<real, 2> A({2, 3}, {1, 2, 3, 4 ,5, 6});
  163. // ra::Unique<real> C({}, 0.);
  164. // C += A(0);
  165. // }
  166. }
  167. tr.section("to sum rows in crude ways");
  168. {
  169. ra::Unique<real, 2> A({100, 111}, ra::_0 - ra::_1);
  170. ra::Unique<real, 1> B({111}, 0.);
  171. for (int j=0, jend=A.size(1); j<jend; ++j) {
  172. B(j) = sum(A(ra::all, j));
  173. }
  174. {
  175. ra::Unique<real, 1> C({111}, 0.);
  176. for_each([&C](auto && a) { C += a; }, A.iter<1>());
  177. tr.info("rhs iterator of rank > 0").test_eq(B, C);
  178. }
  179. {
  180. ra::Unique<real, 1> C({111}, 0.);
  181. for_each(ra::wrank<1, 1>([](auto & c, auto && a) { c += a; }), C, A);
  182. tr.info("rank conjuction").test_eq(B, C);
  183. }
  184. {
  185. ra::Unique<real, 1> C({111}, 0.);
  186. for_each(ra::wrank<1, 1>(ra::wrank<0, 0>([](auto & c, auto a) { c += a; })), C, A);
  187. tr.info("double rank conjunction").test_eq(B, C);
  188. }
  189. {
  190. ra::Unique<real, 1> C({111}, 0.);
  191. ra::scalar(C) += A.iter<1>();
  192. tr.info("scalar() and iterators of rank > 0").test_eq(B, C);
  193. }
  194. {
  195. ra::Unique<real, 1> C({111}, 0.);
  196. C.iter<1>() += A.iter<1>();
  197. tr.info("assign to iterators of rank > 0").test_eq(B, C);
  198. }
  199. }
  200. tr.section("reductions with amax");
  201. {
  202. ra::Big<int, 2> c({2, 3}, {1, 3, 2, 7, 1, 3});
  203. tr.info("max of rows").test_eq(ra::Big<int, 1> {3, 7}, map([](auto && a) { return amax(a); }, iter<1>(c)));
  204. ra::Big<int, 1> m({3}, 0);
  205. scalar(m) = max(scalar(m), iter<1>(c));
  206. tr.info("max of columns I").test_eq(ra::Big<int, 1> {7, 3, 3}, m);
  207. m = 0;
  208. iter<1>(m) = max(iter<1>(m), iter<1>(c));
  209. tr.info("max of columns III").test_eq(ra::Big<int, 1> {7, 3, 3}, m);
  210. m = 0;
  211. for_each([&m](auto && a) { m = max(m, a); }, iter<1>(c));
  212. tr.info("max of columns II").test_eq(ra::Big<int, 1> {7, 3, 3}, m);
  213. ra::Big<double, 1> q({0}, {});
  214. tr.info("amax default").test_eq(std::numeric_limits<double>::infinity(), amin(q));
  215. tr.info("amin default").test_eq(-std::numeric_limits<double>::infinity(), amax(q));
  216. }
  217. tr.section("vector-matrix reductions");
  218. {
  219. auto test = [&tr](auto t, auto s, auto r)
  220. {
  221. using T = decltype(t);
  222. using S = decltype(s);
  223. using R = decltype(r);
  224. S x[4] = {1, 2, 3, 4};
  225. ra::Small<T, 3, 4> a = ra::_0 - ra::_1;
  226. R y[3] = {99, 99, 99};
  227. ra::start(y) = ra::gemv(a, x);
  228. auto z = ra::gemv(a, x);
  229. tr.test_eq(ra::Small<R, 3> {-20, -10, 0}, y);
  230. tr.test_eq(ra::Small<R, 3> {-20, -10, 0}, z);
  231. };
  232. test(double(0), double(0), double(0));
  233. test(std::complex<double>(0), std::complex<double>(0), std::complex<double>(0));
  234. test(int(0), int(0), int(0));
  235. test(int(0), double(0), double(0));
  236. test(double(0), int(0), double(0));
  237. }
  238. {
  239. auto test = [&tr](auto t, auto s, auto r)
  240. {
  241. using T = decltype(t);
  242. using S = decltype(s);
  243. using R = decltype(r);
  244. S x[4] = {1, 2, 3, 4};
  245. ra::Small<T, 4, 3> a = ra::_1 - ra::_0;
  246. R y[3] = {99, 99, 99};
  247. ra::start(y) = ra::gevm(x, a);
  248. auto z = ra::gevm(x, a);
  249. tr.test_eq(ra::Small<R, 3> {-20, -10, 0}, y);
  250. tr.test_eq(ra::Small<R, 3> {-20, -10, 0}, z);
  251. };
  252. test(double(0), double(0), double(0));
  253. test(std::complex<double>(0), std::complex<double>(0), std::complex<double>(0));
  254. test(int(0), int(0), int(0));
  255. test(int(0), double(0), double(0));
  256. test(double(0), int(0), double(0));
  257. }
  258. tr.section("matrix-matrix reductions");
  259. {
  260. ra::Big<double, 2> A({0, 0}, 0.);
  261. ra::Big<double, 2> B({0, 0}, 0.);
  262. auto C = gemm(A, B);
  263. tr.test_eq(0, C.size(0));
  264. tr.test_eq(0, C.size(1));
  265. }
  266. return tr.summary();
  267. }