Utopia 2
Framework for studying models of complex & adaptive systems.
Loading...
Searching...
No Matches
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
13namespace Utopia {
14
15
22namespace GraphUtils{
23
25
38template<typename Graph, typename Iter, typename Rule>
39void 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){
51 [&rule, &g](auto g_entity){
52 rule(g_entity, g);
53 });
54 }
55 else {
57 g[g_entity].state = rule(g_entity, g);
58 });
59 }
60}
61
63
84template<typename Iter, typename Graph, typename Rule>
85void 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
129 Update mode,
130 typename Graph,
131 typename Rule,
132 typename std::enable_if_t<mode == Update::sync, int> = 0>
133void apply_rule(Rule&& rule, Graph&& g)
134{
135 using namespace GraphUtils;
137 apply_sync(it, it_end, g, rule);
138}
139
140
142
160 Update mode,
162 typename Graph,
163 typename Rule,
164 typename std::enable_if_t<mode == Update::async, int> = 0>
165void 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;
174 apply_async(it, it_end, g, rule);
175}
176
177
179
197 Update mode,
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>
204void 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
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>
255void apply_rule(Rule&& rule,
256 const VertexDesc ref_vertex,
257 Graph&& g)
258{
259 using namespace GraphUtils;
261 apply_sync(it, it_end, g, rule);
262}
263
264
266
285 Update mode,
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>
294void apply_rule(Rule&& rule,
295 const VertexDesc ref_vertex,
296 Graph&& g)
297{
298 using namespace GraphUtils;
300 apply_async(it, it_end, g, rule);
301}
302
303
305
326 Update mode,
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>
334void 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.
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
Container select_entities(const Manager &mngr, const DataIO::Config &sel_cfg)
Select entities according to parameters specified in a configuration.
Definition select.hh:213
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
Definition agent.hh:11