Utopia  2
Framework for studying models of complex & adaptive systems.
apply.hh
Go to the documentation of this file.
1 #ifndef UTOPIA_CORE_GRAPH_APPLY_HH
2 #define UTOPIA_CORE_GRAPH_APPLY_HH
3 
4 #include <type_traits>
5 #include <boost/graph/graph_traits.hpp>
6 
7 #include "iterator.hh"
8 #include "apply.hh"
9 #include "../apply.hh" // for Shuffle enum
10 #include "../state.hh" // for Update enum
11 #include "../zip.hh"
12 
13 namespace Utopia {
14 
15 
22 namespace GraphUtils{
23 
25 
38 template<typename Graph, typename Iter, typename Rule>
39 void apply_async(Iter it_begin, Iter it_end, Graph&& g, Rule&& rule)
40 {
41  // Determine whether the lambda function returns a void
42  // The lambda function can either be called with a vertex descriptor or
43  // and edge descriptor. Therefore, both cases need to be accounted for
44  using Desc = typename std::iterator_traits<Iter>::value_type;
45  using ReturnType = typename std::invoke_result_t<Rule, Desc, Graph>;
46  constexpr bool lambda_returns_void = std::is_same_v<ReturnType, void>;
47 
48  // Apply the rule to each element
49  if constexpr (lambda_returns_void){
50  std::for_each(it_begin, it_end,
51  [&rule, &g](auto g_entity){
52  rule(g_entity, g);
53  });
54  }
55  else {
56  std::for_each(it_begin, it_end, [&rule, &g](auto g_entity){
57  g[g_entity].state = rule(g_entity, g);
58  });
59  }
60 }
61 
63 
84 template<typename Iter, typename Graph, typename Rule>
85 void apply_sync(Iter it_begin, Iter it_end, Graph&& g, Rule&& rule)
86 {
87  // initialize the state cache
88  std::vector<decltype(g[*it_begin].state)> state_cache;
89  state_cache.reserve(std::distance(it_begin, it_end));
90 
91  // apply the rule
92  for (auto entity : boost::make_iterator_range(it_begin, it_end)){
93  state_cache.push_back(rule(entity, g));
94  }
95 
96  // move the cache
97  auto counter = 0u;
98  for (auto entity : boost::make_iterator_range(it_begin, it_end)){
99  g[entity].state = std::move(state_cache[counter]);
100 
101  ++counter;
102  }
103 }
104 
105 } // namespace GraphUtils
106 
107 
108 
109 // ----------------------------------------------------------------------------
110 // apply_rule definitions WITHOUT the need for a reference vertex
111 
113 
128 template<IterateOver iterate_over,
129  Update mode,
130  typename Graph,
131  typename Rule,
132  typename std::enable_if_t<mode == Update::sync, int> = 0>
133 void apply_rule(Rule&& rule, Graph&& g)
134 {
135  using namespace GraphUtils;
136  auto [it, it_end] = iterator_pair<iterate_over>(g);
137  apply_sync(it, it_end, g, rule);
138 }
139 
140 
142 
159 template<IterateOver iterate_over,
160  Update mode,
161  Shuffle shuffle,
162  typename Graph,
163  typename Rule,
164  typename std::enable_if_t<mode == Update::async, int> = 0>
165 void apply_rule(Rule&& rule, Graph&& g)
166 {
167  static_assert(shuffle == Shuffle::off,
168  "Refusing to asynchronously apply a rule without shuffling. Either "
169  "explicitly specify Shuffle::off or pass an RNG to apply_rule to "
170  "allow shuffling.");
171 
172  using namespace GraphUtils;
173  auto [it, it_end] = iterator_pair<iterate_over>(g);
174  apply_async(it, it_end, g, rule);
175 }
176 
177 
179 
196 template<IterateOver iterate_over,
197  Update mode,
198  Shuffle shuffle = Shuffle::on,
199  typename Graph,
200  typename Rule,
201  typename RNG,
202  typename std::enable_if_t<mode == Update::async, int> = 0,
203  typename std::enable_if_t<shuffle == Shuffle::on, int> = 0>
204 void apply_rule(Rule&& rule, Graph&& g, RNG&& rng)
205 {
206  using namespace GraphUtils;
207 
208  // Get the iterators and create a vector with a copy because the original
209  // iterators are const, thus cannot be shuffled. Then shuffle.
210  auto [it, it_end] = Utopia::GraphUtils::iterator_pair<iterate_over>(g);
211  using Desc = typename std::iterator_traits<decltype(it)>::value_type;
212  std::vector<Desc> it_shuffled(it, it_end);
213 
214  std::shuffle(std::begin(it_shuffled),
215  std::end(it_shuffled),
216  std::forward<RNG>(rng));
217 
218  // Now with the shuffled container, apply the rule to each element
219  apply_async(std::begin(it_shuffled), std::end(it_shuffled), g, rule);
220 }
221 
222 
223 
224 // ----------------------------------------------------------------------------
225 // apply_rule definitions WITH the need for a reference vertex
226 
227 
229 
247 template<IterateOver iterate_over,
248  Update mode,
249  typename Graph,
250  typename Rule,
251  typename VertexDesc =
252  typename boost::graph_traits<
253  std::remove_reference_t<Graph>>::vertex_descriptor,
254  typename std::enable_if_t<mode == Update::sync, int> = 0>
255 void apply_rule(Rule&& rule,
256  const VertexDesc ref_vertex,
257  Graph&& g)
258 {
259  using namespace GraphUtils;
260  auto [it, it_end] = iterator_pair<iterate_over>(ref_vertex, g);
261  apply_sync(it, it_end, g, rule);
262 }
263 
264 
266 
284 template<IterateOver iterate_over,
285  Update mode,
286  Shuffle shuffle = Shuffle::on,
287  typename Graph,
288  typename Rule,
289  typename VertexDesc =
290  typename boost::graph_traits<
291  std::remove_reference_t<Graph>>::vertex_descriptor,
292  typename std::enable_if_t<mode == Update::async, int> = 0,
293  typename std::enable_if_t<shuffle == Shuffle::off, int> = 0>
294 void apply_rule(Rule&& rule,
295  const VertexDesc ref_vertex,
296  Graph&& g)
297 {
298  using namespace GraphUtils;
299  auto [it, it_end] = iterator_pair<iterate_over>(ref_vertex, g);
300  apply_async(it, it_end, g, rule);
301 }
302 
303 
305 
325 template<IterateOver iterate_over,
326  Update mode,
327  Shuffle shuffle = Shuffle::on,
328  typename Graph,
329  typename Rule,
330  typename RNG,
331  typename VertexDesc =
332  typename boost::graph_traits<
333  std::remove_reference_t<Graph>>::vertex_descriptor>
334 void apply_rule(Rule&& rule,
335  const VertexDesc ref_vertex,
336  Graph&& g,
337  RNG&& rng)
338 {
339  static_assert(mode == Update::async,
340  "Shuffled apply_rule is only possible for Update::async!");
341 
342  using namespace GraphUtils;
343 
344  // Get the iterators and create a vector with a copy because the original
345  // iterators are const, thus cannot be shuffled. Then shuffle.
346  auto [it, it_end] = iterator_pair<iterate_over>(ref_vertex, g);
347  std::vector<VertexDesc> it_shuffled(it, it_end);
348 
349  std::shuffle(std::begin(it_shuffled), std::end(it_shuffled), rng);
350 
351  apply_async(std::begin(it_shuffled), std::end(it_shuffled), g, rule);
352 }
353 
354 
359 } // namespace Utopia
360 
361 #endif // UTOPIA_CORE_GRAPH_APPLY_HH
void for_each(const Utopia::ExecPolicy policy, InputIt first, InputIt last, UnaryFunction f)
Apply a function to a range.
Definition: parallel.hh:346
IterateOver
Over which graph entity to iterate.
Definition: iterator.hh:19
Shuffle
Switch for enabling/disabling shuffling the cells for asynchronous updates.
Definition: apply.hh:27
Update
Update modes when applying rules.
Definition: state.hh:20
void apply_rule(Rule &&rule, const ContTarget &cont_target, ContArgs &&... cont_args)
Sequential overload.
Definition: apply.hh:133
@ off
Immediately apply the rule sequentially.
@ on
Shuffle the container before applying the rule sequentially.
@ async
Asynchronous update.
void apply_sync(Iter it_begin, Iter it_end, Graph &&g, Rule &&rule)
Apply a rule synchronously.
Definition: apply.hh:85
void apply_async(Iter it_begin, Iter it_end, Graph &&g, Rule &&rule)
Apply a rule asynchronously.
Definition: apply.hh:39
auto end(zip< Containers... > &zipper)
end function like std::end
Definition: zip.hh:550
auto begin(zip< Containers... > &zipper)
Begin function like std::begin.
Definition: zip.hh:537
std::mt19937 rng
– Type definitions ----------------------------------------------------—
Definition: test_revision.cc:17
Definition: agent.hh:11