read-me.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. // -*- mode: c++; coding: utf-8 -*-
  2. // ra-ra/examples - Examples used in top-level README.md
  3. // (c) Daniel Llorens - 2016
  4. // This library is free software; you can redistribute it and/or modify it under
  5. // the terms of the GNU Lesser General Public License as published by the Free
  6. // Software Foundation; either version 3 of the License, or (at your option) any
  7. // later version.
  8. // TODO Generate README.md and/or these examples.
  9. #include <iostream>
  10. #include <print>
  11. #include <numeric>
  12. #include "ra/test.hh" // ra/ra.hh without TestRecorder
  13. using std::cout, std::endl, ra::TestRecorder;
  14. using ra::ilist_t;
  15. int main()
  16. {
  17. TestRecorder tr(std::cout);
  18. tr.section("First example");
  19. {
  20. // run time rank
  21. ra::Big<float> A = { {1, 2, 3, 4}, {5, 6, 7, 8} };
  22. // static rank, run time dimensions
  23. ra::Big<float, 2> B = { {1, 2, 3, 4}, {5, 6, 7, 8} };
  24. // static dimensions
  25. ra::Small<float, 2, 4> C = { {1, 2, 3, 4}, {5, 6, 7, 8} };
  26. // rank-extending op with STL object
  27. B += A + C + std::vector {100., 200.};
  28. // negate right half
  29. B(ra::all, ra::iota(ra::len/2, ra::len/2)) *= -1;
  30. // c style
  31. std::println(stdout, "B:\n{:c:4.2f}", B);
  32. tr.test_eq(ra::Small<float, 2, 4> { {103, 106, -109, -112}, {215, 218, -221, -224} }, B);
  33. }
  34. tr.section("Most things are constexpr");
  35. {
  36. constexpr ra::Small<int, 3> a = { 1, 2, 3 };
  37. static_assert(6==ra::sum(a));
  38. }
  39. tr.section("Dynamic or static array rank. Dynamic or static array shape (all dimensions or none)");
  40. {
  41. ra::Big<char> A({2, 3}, 'a'); // dynamic rank = 2, dynamic shape = {2, 3}
  42. ra::Big<char, 2> B({2, 3}, 'b'); // static rank = 2, dynamic shape = {2, 3}
  43. ra::Small<char, 2, 3> C('c'); // static rank = 2, static shape = {2, 3}
  44. cout << "A: " << A << "\n\n";
  45. cout << "B: " << B << "\n\n";
  46. cout << "C: " << C << "\n\n";
  47. }
  48. tr.section("Memory-owning types and views. You can make array views over any piece of memory");
  49. {
  50. // memory-owning types
  51. ra::Big<char, 2> A({2, 3}, 'a'); // storage is std::vector inside A
  52. ra::Unique<char, 2> B({2, 3}, 'b'); // storage is owned by std::unique_ptr inside B
  53. ra::Small<char, 2, 3> C('c'); // storage is owned by C itself, on the stack
  54. cout << "A: " << A << "\n\n";
  55. cout << "B: " << B << "\n\n";
  56. cout << "C: " << C << "\n\n";
  57. // view types
  58. char cs[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
  59. ra::ViewBig<char, 2> D1({2, 3}, cs); // dynamic sizes and steps, C order
  60. ra::ViewBig<char, 2> D2({{2, 1}, {3, 2}}, cs); // dynamic sizes and steps, Fortran order.
  61. ra::ViewSmall<char, ra::ic_t<std::array {ra::Dim {2, 3}, ra::Dim {3, 1}}>> D3(cs); // static sizes & steps, C order.
  62. ra::ViewSmall<char, ra::ic_t<std::array {ra::Dim {2, 1}, ra::Dim {3, 2}}>> D4(cs); // static sizes & steps, Fortran order.
  63. cout << "D1: " << D1 << "\n\n";
  64. cout << "D2: " << D2 << "\n\n";
  65. cout << "D3: " << D3 << "\n\n";
  66. cout << "D4: " << D4 << "\n\n";
  67. }
  68. tr.section("Shape agreement");
  69. // Shape agreement rules and rank extension (broadcasting) for rank-0 operations of any arity
  70. // and operands of any rank, any of which can a reference (so you can write on them). These
  71. // rules are taken from the array language, J.
  72. // (See examples/agreement.cc for more examples.)
  73. {
  74. ra::Big<float, 2> A {{1, 2, 3}, {1, 2, 3}};
  75. ra::Big<float, 1> B {-1, +1};
  76. ra::Big<float, 2> C({2, 3}, 99.);
  77. C = A * B; // C(i, j) = A(i, j) * C(i)
  78. cout << "C: " << C << "\n\n";
  79. ra::Big<float, 1> D({2}, 0.);
  80. D += A * B; // D(i) += A(i, j) * C(i)
  81. cout << "D: " << D << "\n\n";
  82. }
  83. tr.section("Iterators over cells of arbitrary rank");
  84. {
  85. constexpr auto i = ra::iota<0>();
  86. constexpr auto j = ra::iota<1>();
  87. constexpr auto k = ra::iota<2>();
  88. ra::Big<float, 3> A({2, 3, 4}, i+j+k);
  89. ra::Big<float, 2> B({2, 3}, 0);
  90. cout << "A: " << A << "\n\n";
  91. // store the sum of A(i, j, ...) in B(i, j). All these are equivalent.
  92. B = 0; B += A; // default agreement matches prefixes
  93. for_each([](auto && b, auto && a) { b = ra::sum(a); }, B, A); // default agreement matches prefixes
  94. for_each([](auto && b, auto && a) { b = ra::sum(a); }, B, A.iter<1>()); // give cell rank
  95. for_each([](auto && b, auto && a) { b = ra::sum(a); }, B, A.iter<-2>()); // give frame rank
  96. cout << "B: " << B << "\n\n";
  97. // store the sum of A(i, ...) in B(i, j). The op is re-executed for each j, so don't do it this way.
  98. for_each([](auto && b, auto && a) { b = ra::sum(a); }, B, A.iter<2>()); // give cell rank
  99. cout << "B: " << B << "\n\n";
  100. }
  101. tr.section("A rank conjunction (only for static rank and somewhat fragile)");
  102. {
  103. // This is a translation of J: A = (i.3) -"(0 1) i.4, that is: A(i, j) = b(i)-c(j).
  104. ra::Big<float, 2> A = map(ra::wrank<0, 1>(std::minus<float>()), ra::iota(3), ra::iota(4));
  105. cout << "A: " << A << "\n\n";
  106. }
  107. // See examples/slicing.cc for more examples.
  108. tr.section("A proper selection operator with 'beating' of range or scalar subscripts.");
  109. {
  110. // TODO do implicit reshape in constructors?? so I can accept any 1-array and not only an initializer_list.
  111. ra::Big<char, 3> A({2, 2, 2}, {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'});
  112. cout << "A: " << A << "\n\n";
  113. // these are all equivalent to e.g. A(:, 0, :) in Octave.
  114. cout << "A1: " << A(ra::all, 0) << "\n\n";
  115. cout << "A2: " << A(ra::all, 0, ra::all) << "\n\n";
  116. cout << "A3: " << A(ra::all, 0, ra::dots<1>) << "\n\n";
  117. // an inverted range.
  118. cout << "A4: " << A(ra::iota(2, 1, -1)) << "\n\n";
  119. // indices can be arrays of any rank.
  120. ra::Big<int, 2> I {{0, 3}, {1, 2}};
  121. ra::Big<char, 1> B {'a', 'b', 'c', 'd'};
  122. cout << "B(I): " << B(I) << "\n\n";
  123. // multiple indexing performs an implicit outer product. this results in a rank
  124. // 4 array X = A(J, 1, J) -> X(i, j, k, l) = A(J(i, j), 1, J(k, l))
  125. ra::Big<int, 2> J {{1, 0}, {0, 1}};
  126. cout << "A(J, 1, J): " << A(J, 1, J) << "\n\n";
  127. // explicit indices do not result in a view (= pointer + steps), but the
  128. // resulting expression can still be written on.
  129. B(I) = ra::Big<char, 2> {{'x', 'y'}, {'z', 'w'}};
  130. cout << "B: " << B << endl;
  131. }
  132. // FIXME bring in some examples from test/stl-compat.cc. Show examples both ways.
  133. tr.section("STL compatibility");
  134. {
  135. ra::Big<char, 1> A = {'x', 'z', 'y'};
  136. std::sort(A.begin(), A.end());
  137. cout << "A: " << A << "\n\n";
  138. tr.test_eq(ra::start({'x', 'y', 'z'}), A);
  139. A = {'x', 'z', 'y'};
  140. std::sort(begin(A), end(A));
  141. cout << "A: " << A << "\n\n";
  142. tr.test_eq(ra::start({'x', 'y', 'z'}), A);
  143. }
  144. {
  145. ra::Big<float, 2> B {{1, 2}, {3, 4}};
  146. B += std::vector<float> {10, 20};
  147. cout << "B: " << B << "\n\n";
  148. tr.test_eq(ra::Big<float, 2> {{11, 12}, {23, 24}}, B);
  149. }
  150. tr.section("Example from the manual [ma100]");
  151. {
  152. ra::Small<int, 3> s {2, 1, 0};
  153. ra::Small<double, 3> z = pick(s, s*s, s+s, sqrt(s));
  154. cout << "z: " << z << endl;
  155. }
  156. tr.section("Example from the manual [ma101]");
  157. {
  158. ra::Big<char, 2> A({2, 5}, "helloworld");
  159. std::cout << fmt({ .shape=ra::noshape, .sep0="|" }, transpose(A)) << std::endl;
  160. }
  161. {
  162. ra::Big<char const *, 1> A = {"hello", "array", "world"};
  163. std::cout << fmt({ .shape=ra::noshape, .sep0="|" }, A) << std::endl;
  164. }
  165. tr.section("Example from the manual [ma102]");
  166. {
  167. // ra::Big<char const *, 1> A({3}, "hello"); // ERROR bc of pointer constructor
  168. ra::Big<char const *, 1> A({3}, ra::scalar("hello"));
  169. std::cout << fmt({ .shape=ra::noshape, .sep0="|" }, A) << std::endl;
  170. }
  171. tr.section("Example from the manual [ma108]");
  172. {
  173. std::cout << fmt({.sep0="|"}, ra::Small<int, 2> { 2, 3 }) << std::endl;
  174. std::print(stdout, "{}\n", fmt({.sep0="|"}, ra::Small<int, 2> { 2, 3 }));
  175. }
  176. tr.section("Example from the manual [ma103]");
  177. {
  178. ra::Big<int, 2> A {{1, 2}, {3, 4}, {5, 6}};
  179. ra::Big<int, 2> B {{7, 8, 9}, {10, 11, 12}};
  180. ra::Big<int, 2> C({3, 3}, 0.);
  181. for_each(ra::wrank<1, 1, 2>(ra::wrank<1, 0, 1>([](auto && c, auto && a, auto && b) { c += a*b; })), C, A, B);
  182. /* 3 3
  183. 27 30 33
  184. 61 68 75
  185. 95 106 117 */
  186. cout << C << endl;
  187. }
  188. tr.section("Example from the manual [ma104] - dynamic size");
  189. {
  190. ra::Big<int, 3> c({3, 2, 2}, ra::_0 - ra::_1 - 2*ra::_2);
  191. cout << "c: " << c << endl;
  192. cout << "s: " << map([](auto && a) { return sum(diag(a)); }, iter<-1>(c)) << endl;
  193. }
  194. tr.section("Example from the manual [ma104] - static size");
  195. {
  196. ra::Small<int, 3, 2, 2> c = ra::_0 - ra::_1 - 2*ra::_2;
  197. cout << "c: " << c << endl;
  198. cout << "s: " << map([](auto && a) { return sum(diag(a)); }, iter<-1>(c)) << endl;
  199. }
  200. tr.section("Example from the manual [ma105]");
  201. {
  202. ra::Big<double, 2> a {{1, 2, 3}, {4, 5, 6}};
  203. ra::Big<double, 1> b {10, 20, 30};
  204. ra::Big<double, 2> c({2, 3}, 0);
  205. iter<1>(c) = iter<1>(a) * iter<1>(b); // multiply each item of a by b
  206. cout << c << endl;
  207. }
  208. tr.section("Example from the manual [ma109]. Contrived to need explicit ply");
  209. {
  210. ra::Big<int, 1> o = {};
  211. ra::Big<int, 1> e = {};
  212. ra::Big<int, 1> n = {1, 2, 7, 9, 12};
  213. ply(where(odd(n), map([&o](auto && x) { o.push_back(x); }, n), map([&e](auto && x) { e.push_back(x); }, n)));
  214. cout << "o: " << fmt(ra::nstyle, o) << ", e: " << fmt(ra::nstyle, e) << endl;
  215. }
  216. tr.section("Example from manual [ma110]");
  217. {
  218. std::cout << exp(ra::Small<double, 3> {4, 5, 6}) << std::endl;
  219. }
  220. tr.section("Example from manual [ma111]");
  221. {
  222. ra::Small<int, 2, 2> a = {{1, 2}, {3, 4}}; // explicit contents
  223. ra::Small<int, 2, 2> b = {1, 2, 3, 4}; // ravel of content
  224. cout << "a: " << a << ", b: " << b << endl;
  225. }
  226. tr.section("Example from manual [ma112]");
  227. {
  228. double bx[6] = {1, 2, 3, 4, 5, 6};
  229. ra::Big<double, 2> b({3, 2}, bx); // {{1, 2}, {3, 4}, {5, 6}}
  230. cout << "b: " << b << endl;
  231. }
  232. tr.section("Example from manual [ma114]");
  233. {
  234. constexpr std::array dimv = {ra::Dim {2, 1}, ra::Dim {3, 2}};
  235. ra::SmallArray<int, ra::ic_t<dimv>> a {{1, 2, 3}, {4, 5, 6}}; // stored column-major
  236. cout << "a: " << a << endl;
  237. cout << ra::Small<int, 6>(ra::ptr(a.data())) << endl;
  238. }
  239. tr.section("Example from manual [ma116]");
  240. {
  241. ra::Big<int, 2> a({3, 2}, {1, 2, 3, 4, 5, 6});
  242. ra::Big<int, 1> x = {1, 10};
  243. cout << (x(ra::all, ra::insert<2>) * a(ra::insert<1>)) << endl;
  244. cout << (x * a(ra::insert<1>)) << endl; // same thing
  245. }
  246. tr.section("Examples from manual [ma118]");
  247. {
  248. ra::Big<int, 2> A = {{3, 0, 0}, {4, 5, 6}, {0, 5, 6}};
  249. tr.test_eq(sum(A), std::accumulate(ra::begin(A), ra::end(A), 0));
  250. tr.test_eq(sum(cast<int>(A>3)), std::ranges::count(range(A>3), true));
  251. // count rows with 0s in them
  252. tr.test_eq(2, std::ranges::count_if(range(iter<1>(A)), [](auto const & x) { return any(x==0); }));
  253. }
  254. return tr.summary();
  255. }