indirect.cc 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // -*- mode: c++; coding: utf-8 -*-
  2. // Adapted from blitz++/examples/indirect.cpp
  3. // Daniel Llorens - 2015
  4. // The point of this example in Blitz++ seems to be to show off the compact
  5. // notation. ra:: doesn't support special sparse selectors like Blitz++ does,
  6. // and the alternatves look more verbose, but I don't want to clog every expr
  7. // type with additional variants of operator() or operator[] (which on C++17
  8. // still need to be members). The sparse selectors should be better defined as
  9. // standalone functions with separate names depending on the kind of indexing
  10. // they do.
  11. #include "ra/ra.hh"
  12. #include <vector>
  13. #include <iostream>
  14. using std::cout, std::endl;
  15. void example1()
  16. {
  17. // Indirection using a list of coordinates
  18. ra::Big<int, 2> A({4, 4}, 0), B({4, 4}, 10*ra::_0 + ra::_1);
  19. using coord = ra::Small<int, 2>;
  20. ra::Big<coord, 1> I = { {1, 1}, {2, 2} };
  21. // Blitz++ had A[I] = B.
  22. // In ra::, both sides of = must agree in shape.
  23. // Also, the selector () is outer product (to index two axes, you need two arguments). The 'coord' selector is at().
  24. // So this is the most direct translation. Note the -> decltype(auto) to construct a reference expr.
  25. map([&A](auto && c) -> decltype(auto) { return A.at(c); }, I)
  26. = map([&B](auto && c) { return B.at(c); }, I);
  27. // More reasonably
  28. for_each([&A, &B](auto && c) { A.at(c) = B.at(c); }, I);
  29. // There is an array op for at(). See also example5 below.
  30. at(A, I) = at(B, I);
  31. cout << "B = " << B << endl << "A = " << A << endl;
  32. // B = 4 x 4
  33. // 0 1 2 3
  34. // 10 11 12 13
  35. // 20 21 22 23
  36. // 30 31 32 33
  37. // A = 4 x 4
  38. // 0 0 0 0
  39. // 0 11 0 0
  40. // 0 0 22 0
  41. // 0 0 0 0
  42. }
  43. void example2()
  44. {
  45. // Cartesian-product indirection
  46. ra::Big<int, 2> A({6, 6}, 0), B({6, 6}, 10*ra::_0 + ra::_1);
  47. ra::Big<int, 1> I { 1, 2, 4 }, J { 2, 0, 5 };
  48. // The normal selector () already has Cartesian-product behavior. As before, both sides must agree in shape.
  49. A(I, J) = B(I, J);
  50. cout << "B = " << B << endl << "A = " << A << endl;
  51. // B = 6 x 6
  52. // 0 1 2 3 4 5
  53. // 10 11 12 13 14 15
  54. // 20 21 22 23 24 25
  55. // 30 31 32 33 34 35
  56. // 40 41 42 43 44 45
  57. // 50 51 52 53 54 55
  58. // A = 6 x 6
  59. // 0 0 0 0 0 0
  60. // 10 0 12 0 0 15
  61. // 20 0 22 0 0 25
  62. // 0 0 0 0 0 0
  63. // 40 0 42 0 0 45
  64. // 0 0 0 0 0 0
  65. }
  66. void example3()
  67. {
  68. // Simple 1-D indirection, using a STL container of int
  69. ra::Big<int, 1> A({5}, 0), B({ 1, 2, 3, 4, 5 });
  70. ra::Big<int, 1> I {2, 4, 1};
  71. // As before, both sides must agree in shape.
  72. A(I) = B(I);
  73. cout << "B = " << B << endl << "A = " << A << endl;
  74. // B = [ 1 2 3 4 5 ]
  75. // A = [ 0 2 3 0 5 ]
  76. }
  77. void example4()
  78. {
  79. // Indirection using a list of rect domains (RectDomain<N> objects in Blitz++).
  80. // ra:: doesn't have those, so we fake it.
  81. const int N = 7;
  82. ra::Big<int, 2> A({N, N}, 0.), B({N, N}, 1.);
  83. double centre_i = (N-1)/2.0;
  84. double centre_j = (N-1)/2.0;
  85. double radius = 0.8 * N/2.0;
  86. // circle will contain a list of strips which represent a circular subdomain.
  87. ra::Big<std::tuple<int, ra::Iota<int>>, 1> circle; // [y x0 x1; ...]
  88. for (int i=0; i < N; ++i)
  89. {
  90. double jdist2 = sqr(radius) - sqr(i-centre_i);
  91. if (jdist2 < 0.0)
  92. continue;
  93. int jdist = int(sqrt(jdist2));
  94. int begin = int(centre_j - jdist);
  95. int end = int(centre_j + jdist);
  96. circle.push_back(std::make_tuple(i, ra::iota<int>(end-begin+1, begin)));
  97. }
  98. // Set only those points in the circle subdomain to 1
  99. map([&A](auto && c) -> decltype(auto) { return A(std::get<0>(c), std::get<1>(c)); }, circle)
  100. = map([&B](auto && c) { return B(std::get<0>(c), std::get<1>(c)); }, circle);
  101. // or more reasonably
  102. for_each([&A, &B](auto && c) { A(std::get<0>(c), std::get<1>(c)) = B(std::get<0>(c), std::get<1>(c)); }, circle);
  103. // but it would be easier to just do
  104. A = 0.;
  105. B = 1.;
  106. A = where(sqr(ra::_0-centre_i)+sqr(ra::_1-centre_j)<sqr(radius), B, A);
  107. cout << "A = " << A << endl;
  108. // A = 7 x 7
  109. // 0 0 0 0 0 0 0
  110. // 0 0 1 1 1 0 0
  111. // 0 1 1 1 1 1 0
  112. // 0 1 1 1 1 1 0
  113. // 0 1 1 1 1 1 0
  114. // 0 0 1 1 1 0 0
  115. // 0 0 0 0 0 0 0
  116. }
  117. void example5()
  118. {
  119. // suppose you have the x coordinates in one array and the y coordinates in another array.
  120. ra::Big<int, 2> x({4, 4}, {0, 1, 2, 0, /* */ 0, 1, 2, 0, /* */ 0, 1, 2, 0, /* */ 0, 1, 2, 0});
  121. ra::Big<int, 2> y({4, 4}, {1, 2, 0, 1, /* */ 1, 2, 0, 1, /* */ 1, 2, 0, 1, /* */ 1, 2, 0, 1});
  122. cout << "coordinates: " << format_array(ra::pack<ra::Small<int, 2>>(x, y), "|") << endl;
  123. // you can use these for indirect access without creating temporaries.
  124. ra::Big<int, 2> a({3, 3}, {0, 1, 2, 3, 4, 5, 6, 7, 8});
  125. ra::Big<int, 2> b = at(a, ra::pack<ra::Small<int, 2>>(x, y));
  126. cout << "sampling of a using coordinates: " << b << endl;
  127. // cf the default selection operator, which creates an outer product a(x(i, j), y(k, l)) (a 4x4x4x4 array).
  128. cout << "outer product selection: " << a(x, y) << endl;
  129. }
  130. int main()
  131. {
  132. example1();
  133. example2();
  134. example3();
  135. example4();
  136. example5();
  137. }