Utopia 2
Framework for studying models of complex & adaptive systems.
Loading...
Searching...
No Matches
Namespaces | Classes | Typedefs | Enumerations | Functions
Rules on Entities

Algorithms for conveniently applying function objects on entities. More...

Collaboration diagram for Rules on Entities:

Namespaces

namespace  Utopia::GraphUtils
 

Classes

class  Utopia::rule_invoke_result< State, Rule, Args >
 Helper class for checking rule signatures and return types. More...
 
class  Utopia::StateContainer< StateType, mode >
 Container for states. More...
 
class  Utopia::StateContainer< StateType, Update::manual >
 A very simple, default constructible container with public members. More...
 
class  Utopia::StateContainer< StateType, Update::async >
 State Container specialization for async states. More...
 
class  Utopia::StateContainer< StateType, Update::sync >
 State Container specialization for sync states. More...
 

Typedefs

template<typename State , typename Rule , typename... Args>
using Utopia::rule_invoke_result_t = typename rule_invoke_result< State, Rule, Args... >::type
 Helper definition to query the rule result type.
 

Enumerations

enum class  Utopia::Shuffle { Utopia::Shuffle::on , Utopia::Shuffle::off }
 Switch for enabling/disabling shuffling the cells for asynchronous updates. More...
 
enum class  Utopia::Update { Utopia::Update::manual , Utopia::Update::sync , Utopia::Update::async }
 Update modes when applying rules. More...
 

Functions

template<typename State , typename Rule , typename... Args>
constexpr bool Utopia::is_void_rule ()
 Helper function to check if the rule returns void
 
template<class Tuple , std::size_t... I>
constexpr decltype(autoUtopia::make_tuple_from_tuple_impl (Tuple &&t, std::index_sequence< I... >)
 Helper function to create a tuple from a tuple using an index sequence.
 
template<class Tuple >
constexpr decltype(autoUtopia::make_tuple_from_tuple (Tuple &&t)
 Helper function to create a tuple from a tuple.
 
template<Update mode, class Rule , class ContTarget , class... ContArgs, typename std::enable_if_t< mode==Update::sync, int > = 0, typename std::enable_if_t< impl::entity_t< ContTarget >::mode==Update::manual, int > = 0>
void Utopia::apply_rule (Rule &&rule, const ContTarget &cont_target, ContArgs &&... cont_args)
 Sequential overload.
 
template<Update mode, class Rule , class ContTarget , class... ContArgs, typename std::enable_if_t< mode==Update::sync, int > = 0, typename std::enable_if_t< impl::entity_t< ContTarget >::mode==Update::manual, int > = 0>
void Utopia::apply_rule (const Utopia::ExecPolicy policy, Rule &&rule, const ContTarget &cont_target, ContArgs &&... cont_args)
 Apply a rule synchronously to manually updated states.
 
template<Update mode, Shuffle shuffle = Shuffle::on, class Rule , class ContTarget , class... ContArgs, typename std::enable_if_t< mode==Update::async, int > = 0, typename std::enable_if_t< impl::entity_t< ContTarget >::mode==Update::manual, int > = 0, typename std::enable_if_t< shuffle==Shuffle::off, int > = 0>
void Utopia::apply_rule (Rule &&rule, const ContTarget &cont_target, ContArgs &&... cont_args)
 Sequential case overload.
 
template<Update mode, Shuffle shuffle = Shuffle::on, class Rule , class ContTarget , class... ContArgs, typename std::enable_if_t< mode==Update::async, int > = 0, typename std::enable_if_t< impl::entity_t< ContTarget >::mode==Update::manual, int > = 0, typename std::enable_if_t< shuffle==Shuffle::off, int > = 0>
void Utopia::apply_rule (const Utopia::ExecPolicy policy, Rule &&rule, const ContTarget &cont_target, ContArgs &&... cont_args)
 Apply a rule asynchronously to manually updated states.
 
template<Update mode, Shuffle shuffle = Shuffle::on, class Rule , class ContTarget , class RNG , class... ContArgs, typename std::enable_if_t< mode==Update::async, int > = 0, typename std::enable_if_t< impl::entity_t< ContTarget >::mode==Update::manual, int > = 0, typename std::enable_if_t< shuffle==Shuffle::on, int > = 0>
void Utopia::apply_rule (Rule &&rule, const ContTarget &cont_target, RNG &&rng, ContArgs &&... cont_args)
 Sequential case overload.
 
template<Update mode, Shuffle shuffle = Shuffle::on, class Rule , class ContTarget , class RNG , class... ContArgs, typename std::enable_if_t< mode==Update::async, int > = 0, typename std::enable_if_t< impl::entity_t< ContTarget >::mode==Update::manual, int > = 0, typename std::enable_if_t< shuffle==Shuffle::on, int > = 0>
void Utopia::apply_rule (const Utopia::ExecPolicy policy, Rule &&rule, const ContTarget &cont_target, RNG &&rng, ContArgs &&... cont_args)
 Apply a rule asynchronously and shuffled to manually updated states.
 
template<class Rule , class Container , bool sync = impl::entity_t<Container>::is_sync()>
std::enable_if_t< sync, voidUtopia::apply_rule (const Rule &rule, const Container &container)
 Apply a rule synchronously on the state of all entities of a container.
 
template<bool shuffle = true, class Rule , class Container , bool sync = impl::entity_t<Container>::is_sync()>
std::enable_if_t< not sync &&not shuffle, voidUtopia::apply_rule (const Rule &rule, const Container &container)
 Apply a rule on asynchronous states without prior shuffling.
 
template<bool shuffle = true, class Rule , class Container , class RNG , bool sync = impl::entity_t<Container>::is_sync()>
std::enable_if_t< not sync &&shuffle, voidUtopia::apply_rule (const Rule &rule, const Container &container, RNG &&rng)
 Apply a rule on asynchronous states with prior shuffling.
 
template<IterateOver iterate_over, Update mode, typename Graph , typename Rule , typename std::enable_if_t< mode==Update::sync, int > = 0>
void Utopia::apply_rule (Rule &&rule, Graph &&g)
 Synchronously apply a rule to graph entities.
 
template<IterateOver iterate_over, Update mode, Shuffle shuffle, typename Graph , typename Rule , typename std::enable_if_t< mode==Update::async, int > = 0>
void Utopia::apply_rule (Rule &&rule, Graph &&g)
 Asynchronously apply a rule to graph entities, without shuffling.
 
template<IterateOver iterate_over, Update mode, Shuffle shuffle = Shuffle::on, typename Graph , typename Rule , typename RNG , typename std::enable_if_t< mode==Update::async, int > = 0, typename std::enable_if_t< shuffle==Shuffle::on, int > = 0>
void Utopia::apply_rule (Rule &&rule, Graph &&g, RNG &&rng)
 Asynchronously, in shuffled order, apply a rule to graph entities.
 
template<IterateOver iterate_over, Update mode, typename Graph , typename Rule , typename VertexDesc = typename boost::graph_traits< std::remove_reference_t<Graph>>::vertex_descriptor, typename std::enable_if_t< mode==Update::sync, int > = 0>
void Utopia::apply_rule (Rule &&rule, const VertexDesc ref_vertex, Graph &&g)
 Synchronously apply a rule to graph entities.
 
template<IterateOver iterate_over, Update mode, Shuffle shuffle = Shuffle::on, typename Graph , typename Rule , typename VertexDesc = typename boost::graph_traits< std::remove_reference_t<Graph>>::vertex_descriptor, typename std::enable_if_t< mode==Update::async, int > = 0, typename std::enable_if_t< shuffle==Shuffle::off, int > = 0>
void Utopia::apply_rule (Rule &&rule, const VertexDesc ref_vertex, Graph &&g)
 Asynchronously apply a rule to graph entities, without shuffling.
 
template<IterateOver iterate_over, Update mode, Shuffle shuffle = Shuffle::on, typename Graph , typename Rule , typename RNG , typename VertexDesc = typename boost::graph_traits< std::remove_reference_t<Graph>>::vertex_descriptor>
void Utopia::apply_rule (Rule &&rule, const VertexDesc ref_vertex, Graph &&g, RNG &&rng)
 Asynchronously, in shuffled order, apply a rule to graph entities.
 

Detailed Description

Algorithms for conveniently applying function objects on entities.

The General Idea

A rule is a function that computes the new state of the entity it is applied to.

Synchronous vs Asynchronous Updates

Applying a rule synchronously means that its result is applied to all entities simultaneously. In practice, one uses a cache for storing the intermediate states of the cells and then copies/moves the cache entries into the respective cell states.

Applying a rule asynchronously means that its result for a single entity is immediately imposed onto this entity. To avoid preferences in execution order, users may choose to use shuffling before iterating over the cells.

Implementation

A rule must be implemented by the programmer as a function (object). The function it represents must take a pointer to the entity as first argument and any number of additional arguments, and may capture arbitrary objects. The function's return value is the new state of the entity it is applied to.

For asynchronously applied rules, no return value is required; the state may be changed directly.

Currently, rules may also alter other members (i.e., tags) of the entity they are applied to, and may even change states of other entities. Notice that this does not make sense if you intend to apply such a rule synchronously!

A rule is applied with the Utopia::apply_rule() function. All overloads of this function take the rule, a container of entities, and optionally additional containers as arguments. The latter two are iterated and their elements are applied to the rule in order of their occurance in the containers. The number of containers the function is called with must match the number of arguments of the rule. All containers must have the same size.

Warning
There will be no runtime check if the inserted containers match in size! The zip iterator will cause undefined behavior in that case.

Defining a rule taking two arguments and passing an additional container with arguments to be applied can look like this:

auto my_rule = [](auto&& cell, auto&& arg){ return cell->state * arg; };
Container select_entities(const Manager &mngr, const DataIO::Config &sel_cfg)
Select entities according to parameters specified in a configuration.
Definition select.hh:213
void apply_rule(Rule &&rule, const ContTarget &cont_target, ContArgs &&... cont_args)
Sequential overload.
Definition apply.hh:133
@ off
Immediately apply the rule sequentially.
@ async
Asynchronous update.

Utopia::apply_rule() uses the Zip iteration utilities. The above function call is equivalent to the Python code:

for cell, arg in zip(my_cells, my_args):
cell.state = rule(cell, arg)
Note
Additional argument containers are only available with Utopia::apply_rule() overloads for manual state update (see next section).

Choosing Update Type When Calling <tt>apply_rule</tt>

Previously, the state cache for synchronous updates was implemented in the entity itself. Therefore, users had to opt for either sync or async updates when choosing the state data type. We now encourage the usage of the Utopia::StateContainer with the specialization for the Utopia::Update::manual tag (see Utopia::StateContainer<StateType, Update::manual>). This allows for choosing the update type via the Utopia::Update switch directly when calling Utopia::apply_rule.

CellManager cm = get_cm_from_somewhere();
Manages a physical space, its grid discretization, and cells on that grid.
Definition cell_manager.hh:41
The entity traits struct gathers types to be used for specializing an entity.
Definition entity.hh:49

Multithreading Rules

Rules can also be applied in parallel, speeding up the entire computation. This requires the optional dependencies to be installed (consult the README.md for further information). Overloads for manual state update of apply_rule() can take a Utopia::ExecPolicy as first argument. Omitting this policy argument has the same effect as using the Utopia::ExecPolicy::seq policy. Parallel execution of Utopia algorithms can be selected at runtime. See Multithreading for details.

// Sequential execution
// Possibly parallel execution
// NOTE: `my_rule` must avoid data races!
);
@ par
Parallel/multithreaded execution.
Definition parallel.hh:68

{}

Typedef Documentation

◆ rule_invoke_result_t

template<typename State , typename Rule , typename... Args>
using Utopia::rule_invoke_result_t = typedef typename rule_invoke_result<State, Rule, Args...>::type

Helper definition to query the rule result type.

See also
rule_invoke_result

Enumeration Type Documentation

◆ Shuffle

Switch for enabling/disabling shuffling the cells for asynchronous updates.

Enumerator
on 

Shuffle the container before applying the rule sequentially.

off 

Immediately apply the rule sequentially.

27 {
29 on,
31 off
32};
@ on
Shuffle the container before applying the rule sequentially.

◆ Update

Update modes when applying rules.

It is recommended to use Update::manual in the EntityTraits because this gives full flexibility when applying the rules.

Note
To retain compatiblity with older implementations, this switch is used twice: For specializing the StateContainer, and for setting the update type in apply_rule() if said specialization is for Update::manual.
Enumerator
manual 

User chooses update type when calling apply_rule()

sync 

Synchronous update.

async 

Asynchronous update.

20 {
21 manual,
22 sync,
23 async
24};
@ manual
Fully manual: write_data method is always called.
@ sync
Synchronous update.

Function Documentation

◆ apply_rule() [1/15]

template<class Rule , class Container , bool sync = impl::entity_t<Container>::is_sync()>
std::enable_if_t< sync, void > Utopia::apply_rule ( const Rule &  rule,
const Container container 
)

Apply a rule synchronously on the state of all entities of a container.

Applies the rule function to each of the entities' states and stores the result in a buffer. Afterwards, it iterates over all entities again and applies the buffer to the actual state.

Parameters
ruleAn application rule, see rule
ContainerA container with the entities upon whom rule is applied
431{
432 // Apply the rule, distinguishing by return type of the rule
433 using ReturnType =
434 std::invoke_result_t<Rule, typename Container::value_type>;
435
436 if constexpr(std::is_same_v<ReturnType, void>) {
438 std::begin(container), std::end(container),
439 [&rule](const auto& entity){ rule(entity); }
440 );
441 }
442 else {
444 std::begin(container), std::end(container),
445 [&rule](const auto& entity){ entity->state_new() = rule(entity); }
446 );
447 }
448
449 // Let the entity update its state, moving it from the buffer state to the
450 // actual state and potentially applying other updating operations
452 std::begin(container), std::end(container),
453 [](const auto& entity){ entity->update(); }
454 );
455}
void for_each(const Utopia::ExecPolicy policy, InputIt first, InputIt last, UnaryFunction f)
Apply a function to a range.
Definition parallel.hh:346

◆ apply_rule() [2/15]

template<bool shuffle = true, class Rule , class Container , bool sync = impl::entity_t<Container>::is_sync()>
std::enable_if_t< not sync &&not shuffle, void > Utopia::apply_rule ( const Rule &  rule,
const Container container 
)

Apply a rule on asynchronous states without prior shuffling.

Parameters
ruleAn application rule, see rule
ContainerA container with the entities upon whom rule is applied
470{
471 // Apply the rule, distinguishing by return type of the rule
472 using ReturnType =
473 std::invoke_result_t<Rule, typename Container::value_type>;
474
475 if constexpr(std::is_same_v<ReturnType, void>) {
477 std::begin(container), std::end(container),
478 [&rule](const auto& entity){ rule(entity); }
479 );
480 }
481 else {
483 std::begin(container), std::end(container),
484 [&rule](const auto& entity){ entity->state() = rule(entity); }
485 );
486 }
487}

◆ apply_rule() [3/15]

template<bool shuffle = true, class Rule , class Container , class RNG , bool sync = impl::entity_t<Container>::is_sync()>
std::enable_if_t< not sync &&shuffle, void > Utopia::apply_rule ( const Rule &  rule,
const Container container,
RNG &&  rng 
)

Apply a rule on asynchronous states with prior shuffling.

Parameters
ruleAn application rule, see rule
ContainerA container with the entities upon whom rule is applied
502{
503 std::remove_const_t<Container> container_shuffled(container);
504 std::shuffle(std::begin(container_shuffled),
505 std::end(container_shuffled),
506 std::forward<RNG>(rng));
507
508 // Apply the rule, distinguishing by return type of the rule
509 using ReturnType =
510 std::invoke_result_t<Rule, typename Container::value_type>;
511
512 if constexpr(std::is_same_v<ReturnType, void>)
513 {
515 std::begin(container_shuffled), std::end(container_shuffled),
516 [&rule](const auto& entity){ rule(entity); }
517 );
518 }
519 else
520 {
522 std::begin(container_shuffled), std::end(container_shuffled),
523 [&rule](const auto& entity){ entity->state() = rule(entity); }
524 );
525 }
526}

◆ apply_rule() [4/15]

template<Update mode, class Rule , class ContTarget , class... ContArgs, typename std::enable_if_t< mode==Update::sync, int > = 0, typename std::enable_if_t< impl::entity_t< ContTarget >::mode==Update::manual, int > = 0>
void Utopia::apply_rule ( const Utopia::ExecPolicy  policy,
Rule &&  rule,
const ContTarget cont_target,
ContArgs &&...  cont_args 
)

Apply a rule synchronously to manually updated states.

This creates a cache for new states whose contents are moved into the respective state containers after all rules have been applied.

Template Parameters
modeUpdate mode for this rule. This is the overload for synchronous updates (Update::sync).
RuleThe type of the rule function (object).
ContTargetThe type of entity container.
ContArgsThe types of argument containers.
Parameters
policyUtopia::ExecPolicy for the rule when applied in parallel
ruleThe function (object) to apply to the entities
cont_targetThe container of entities the function will be applied to
cont_argsThe containers of additional argument for the function.
168{
169 using State = typename impl::entity_t<ContTarget>::State;
170
171 // Make sure to call `invoke_result_type` at least for a conversion check
172 static_assert(not is_void_rule<State, Rule, ContTarget, ContArgs...>(),
173 "Cannot apply void rules in a synchronous update!");
174
175 // Initialize the state cache
176 // NOTE: Copy one element to avoid requirement of default initialization
177 std::vector<State> state_cache(cont_target.size(),
178 cont_target.front()->state);
179
180 // create the input zip iterators
181 auto range = Itertools::zip(cont_target, cont_args...);
182 using std::begin, std::end;
183
184 // Apply the rule
185 // NOTE: Capture by reference is fine because rule is a temporary
186 std::transform(policy,
187 begin(range),
188 end(range),
189 begin(state_cache),
190 [&rule](auto&& args) {
191 return std::apply(rule,
192 std::forward<decltype(args)>(args));
193 });
194
195 // move the cache
196 auto move_range = Itertools::zip(cont_target, state_cache);
198 begin(move_range),
199 end(move_range),
200 [](auto&& tpl) {
201 std::get<0>(tpl)->state = std::move(std::get<1>(tpl));
202 });
203}
OutputIt transform(const Utopia::ExecPolicy policy, InputIt first1, InputIt last1, OutputIt d_first, UnaryOperation unary_op)
Apply a unary operator to a range and store the result in a new range.
Definition parallel.hh:368
decltype(auto) range(const Graph &g)
Get the iterator range over selected graph entities.
Definition iterator.hh:149
@ par_unseq
SIMD execution on multiple threads.
Definition parallel.hh:69
constexpr bool is_void_rule()
Helper function to check if the rule returns void
Definition apply.hh:82
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

◆ apply_rule() [5/15]

template<Update mode, Shuffle shuffle = Shuffle::on, class Rule , class ContTarget , class... ContArgs, typename std::enable_if_t< mode==Update::async, int > = 0, typename std::enable_if_t< impl::entity_t< ContTarget >::mode==Update::manual, int > = 0, typename std::enable_if_t< shuffle==Shuffle::off, int > = 0>
void Utopia::apply_rule ( const Utopia::ExecPolicy  policy,
Rule &&  rule,
const ContTarget cont_target,
ContArgs &&...  cont_args 
)

Apply a rule asynchronously to manually updated states.

Directly overwrite the states of the passed entities, according to the storage order inside the container.

Template Parameters
modeUpdate mode for this rule. This is the overload for asynchronous updates (Update::async).
shuffleSwitch for enabling the shuffling of cells before applying the rule. This is the overload with shuffling disabled (Shuffle::off).
RuleThe type of the rule function (object).
ContainerThe type of entity container.
ContArgsThe types of argument containers.
Parameters
policyUtopia::ExecPolicy for the rule when applied in parallel
ruleThe function (object) to apply to the entities
containerThe container of entities the function will be applied to
cont_argsThe containers of additional argument for the function.
258{
259 // Create the input range zip iterators
260 auto range = Itertools::zip(cont_target, cont_args...);
261 using std::begin, std::end;
262
263 // Apply the rule, distinguishing by return type of the rule
264 using State = typename impl::entity_t<ContTarget>::State;
265 if constexpr (is_void_rule<State, Rule, ContTarget, ContArgs...>()) {
266 // Is a void-rule; no need to set the return value
267 std::for_each(policy, begin(range), end(range), [&rule](auto&& args) {
268 std::apply(rule,
269 std::forward<decltype(args)>(args));
270 });
271 }
272 else {
273 std::for_each(policy, begin(range), end(range), [&rule](auto&& args) {
274 auto& cell = std::get<0>(args);
275 cell->state = std::apply(rule,
276 std::forward<decltype(args)>(args));
277 });
278 }
279}

◆ apply_rule() [6/15]

template<Update mode, Shuffle shuffle = Shuffle::on, class Rule , class ContTarget , class RNG , class... ContArgs, typename std::enable_if_t< mode==Update::async, int > = 0, typename std::enable_if_t< impl::entity_t< ContTarget >::mode==Update::manual, int > = 0, typename std::enable_if_t< shuffle==Shuffle::on, int > = 0>
void Utopia::apply_rule ( const Utopia::ExecPolicy  policy,
Rule &&  rule,
const ContTarget cont_target,
RNG &&  rng,
ContArgs &&...  cont_args 
)

Apply a rule asynchronously and shuffled to manually updated states.

Copy the container of (pointers to) entities, shuffle it, and apply the rule sequentially to the shuffled container. The original container remains unchanged.

Template Parameters
modeUpdate mode for this rule. This is the overload for asynchronous updates (Update::async).
shuffleSwitch for enabling the shuffling of containers before applying the rule. This is the overload with shuffling enabled (Shuffle::on).
RuleThe type of the rule function (object).
ContainerThe type of entity container.
RNGThe type of the random number generator.
ContArgsThe types of argument containers.
Parameters
policyUtopia::ExecPolicy for the rule when applied in parallel
ruleThe function (object) to apply to the entities
cont_targetThe container of entities the function will be applied to
rngThe random number generator used for shuffling the entities
cont_argsThe containers of additional argument for the function.
Note
Shuffling only changes the order of execution of this function! It occurs on cont_target and all cont_args simultaneously, guaranteeing that entity at index i will be applied with additional arguments at index i for all container indices i. If you want to shuffle argument containers or entities independently from each other, you must do so yourself before applying this function. shuffle the arguments against the entities they are applied on, do so yourself before calling this function!
350{
351 // Create the input range zip iterators
352 auto range = Itertools::zip(cont_target, cont_args...);
353 using std::begin, std::end;
354
355 // NOTE: std::shuffle requires the container elements to be swappable.
356 // This is not the case for tuples of T& so we need a container of
357 // tuples of std::reference_wrapper<T>. For this to work, we must
358 // propagate the const-ness of the container to the wrapper.
359 // When applying the rule, we must convert the reference wrappers back
360 // to regular references, which can be done with std::make_tuple.
361 using Tuple = std::tuple<
362 // 'ContTarget' is always const
363 std::reference_wrapper<
364 const typename std::remove_reference_t<ContTarget>::value_type>,
365 // Universal references to 'ContArgs' *can* be const
366 std::conditional_t<
367 // Check if container is const
368 std::is_const_v<std::remove_reference_t<ContArgs>>,
369 // If true:
370 std::reference_wrapper<
371 const typename std::remove_reference_t<ContArgs>::value_type>,
372 // If false:
373 std::reference_wrapper<
374 typename std::remove_reference_t<ContArgs>::value_type>>...>;
375
376 // Create a container of tuples of arguments and shuffle it
377 std::vector<Tuple> args_container(begin(range), end(range));
378 std::shuffle(
379 begin(args_container), end(args_container), std::forward<RNG>(rng));
380
381 // Apply the rule, distinguishing by return type of the rule
382 using State = typename impl::entity_t<ContTarget>::State;
383 if constexpr(is_void_rule<State, Rule, ContTarget, ContArgs...>())
384 {
385 // Is a void-rule; no need to set the return value
386 std::for_each(policy,
387 begin(args_container),
388 end(args_container),
389 [&rule](auto&& args) {
390 // NOTE: 'args' is tuple of reference_wrappers, and we
391 // want a tuple of regular references.
392 std::apply(rule,
394 std::forward<decltype(args)>(args)));
395 });
396 }
397 else
398 {
399 std::for_each(policy,
400 begin(args_container),
401 end(args_container),
402 [&rule](auto&& args) {
403 // NOTE: 'args' is tuple of reference_wrappers, and we
404 // want a tuple of regular references.
405 auto tpl = make_tuple_from_tuple(
406 std::forward<decltype(args)>(args));
407 auto& entity = std::get<0>(tpl);
408 // NOTE: Do not move 'tpl' as this invalidates ref
409 // 'entity'
410 entity->state = std::apply(rule, tpl);
411 });
412 }
413}
constexpr decltype(auto) make_tuple_from_tuple(Tuple &&t)
Helper function to create a tuple from a tuple.
Definition apply.hh:114

◆ apply_rule() [7/15]

template<Update mode, class Rule , class ContTarget , class... ContArgs, typename std::enable_if_t< mode==Update::sync, int > = 0, typename std::enable_if_t< impl::entity_t< ContTarget >::mode==Update::manual, int > = 0>
void Utopia::apply_rule ( Rule &&  rule,
const ContTarget cont_target,
ContArgs &&...  cont_args 
)

Sequential overload.

136{
137 apply_rule<mode>(
138 Utopia::ExecPolicy::seq, rule, cont_target, cont_args...);
139}
@ seq
Sequential (i.e., regular) execution.
Definition parallel.hh:66

◆ apply_rule() [8/15]

template<Update mode, Shuffle shuffle = Shuffle::on, class Rule , class ContTarget , class... ContArgs, typename std::enable_if_t< mode==Update::async, int > = 0, typename std::enable_if_t< impl::entity_t< ContTarget >::mode==Update::manual, int > = 0, typename std::enable_if_t< shuffle==Shuffle::off, int > = 0>
void Utopia::apply_rule ( Rule &&  rule,
const ContTarget cont_target,
ContArgs &&...  cont_args 
)

Sequential case overload.

220{
221 apply_rule<mode, shuffle>(
222 Utopia::ExecPolicy::seq, rule, cont_target, cont_args...);
223}

◆ apply_rule() [9/15]

template<Update mode, Shuffle shuffle = Shuffle::on, class Rule , class ContTarget , class RNG , class... ContArgs, typename std::enable_if_t< mode==Update::async, int > = 0, typename std::enable_if_t< impl::entity_t< ContTarget >::mode==Update::manual, int > = 0, typename std::enable_if_t< shuffle==Shuffle::on, int > = 0>
void Utopia::apply_rule ( Rule &&  rule,
const ContTarget cont_target,
RNG &&  rng,
ContArgs &&...  cont_args 
)

Sequential case overload.

298{
299 apply_rule<mode, shuffle>(
300 Utopia::ExecPolicy::seq, rule, cont_target, rng, cont_args...);
301}

◆ apply_rule() [10/15]

template<IterateOver iterate_over, Update mode, typename Graph , typename Rule , typename VertexDesc = typename boost::graph_traits< std::remove_reference_t<Graph>>::vertex_descriptor, typename std::enable_if_t< mode==Update::sync, int > = 0>
void Utopia::apply_rule ( Rule &&  rule,
const VertexDesc  ref_vertex,
Graph &&  g 
)

Synchronously apply a rule to graph entities.

This overload specified the apply_rule function for not shuffled entities where getting the correct iterator pair is dependent on a ref_vertex, for example if the rule should be applied to the neighbors, inv_neighbors, in_degree, out_degree or degree wrt. the ref_vertex.

Template Parameters
iterate_overOver which kind of graph entity to iterate over. See IterateOver
modeThe update mode, see UpdateMode
ShuffleWhether to shuffle the container
GraphThe graph type
RuleThe rule type
Parameters
ruleThe rule function, expecting (descriptor, graph) as arguments. For the synchronous update, the rule function needs to return the new state.
ref_vertexReference vertex descriptor to create the iterator from
gThe graph
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}
void apply_sync(Iter it_begin, Iter it_end, Graph &&g, Rule &&rule)
Apply a rule synchronously.
Definition apply.hh:85

◆ apply_rule() [11/15]

template<IterateOver iterate_over, Update mode, Shuffle shuffle = Shuffle::on, typename Graph , typename Rule , typename VertexDesc = typename boost::graph_traits< std::remove_reference_t<Graph>>::vertex_descriptor, typename std::enable_if_t< mode==Update::async, int > = 0, typename std::enable_if_t< shuffle==Shuffle::off, int > = 0>
void Utopia::apply_rule ( Rule &&  rule,
const VertexDesc  ref_vertex,
Graph &&  g 
)

Asynchronously apply a rule to graph entities, without shuffling.

This overload specifies the apply_rule function for an asynchronous update.

Warning
Not shuffling a rule often creates unwanted artifacts. Thus, to use this function, the Shuffle::off template argument needs to be given explicitly.
Template Parameters
iterate_overOver which kind of graph entity to iterate over. See IterateOver
modeThe update mode, see UpdateMode
GraphThe graph type
RuleThe rule type
Parameters
ruleThe rule function, expecting (descriptor, graph) as arguments. For an asynchronous update, returning the state is optional.
ref_vertexReference vertex descriptor to create the iterator from
gThe graph
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}
void apply_async(Iter it_begin, Iter it_end, Graph &&g, Rule &&rule)
Apply a rule asynchronously.
Definition apply.hh:39

◆ apply_rule() [12/15]

template<IterateOver iterate_over, Update mode, Shuffle shuffle = Shuffle::on, typename Graph , typename Rule , typename RNG , typename VertexDesc = typename boost::graph_traits< std::remove_reference_t<Graph>>::vertex_descriptor>
void Utopia::apply_rule ( Rule &&  rule,
const VertexDesc  ref_vertex,
Graph &&  g,
RNG &&  rng 
)

Asynchronously, in shuffled order, apply a rule to graph entities.

This overload specified the apply_rule function for shuffled entities where getting the correct iterator pair is dependent on a ref_vertex, for example if the rule should be applied to the neighbors, inv_neighbors, in_degree, out_degree or degree wrt. the ref_vertex.

Template Parameters
iterate_overOver which kind of graph entity to iterate over. See IterateOver
modeThe update mode, see UpdateMode
ShuffleWhether to shuffle the iteration
GraphThe graph type
RuleThe rule type
RNGThe random number generator type
Parameters
ruleThe rule function, expecting (descriptor, graph) as arguments. For an asynchronous update, returning the state is optional.
ref_vertexReference vertex descriptor to create the iterator from
gThe graph
rngThe random number generator
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}

◆ apply_rule() [13/15]

template<IterateOver iterate_over, Update mode, typename Graph , typename Rule , typename std::enable_if_t< mode==Update::sync, int > = 0>
void Utopia::apply_rule ( Rule &&  rule,
Graph &&  g 
)

Synchronously apply a rule to graph entities.

This overload specifies the apply_rule function for a synchronous update. In such a case, it makes no sense to shuffle, so the shuffle option is not available here.

Template Parameters
iterate_overOver which kind of graph entity to iterate over. See IterateOver
modeThe update mode, see UpdateMode
GraphThe graph type
RuleThe rule type
Parameters
ruleThe rule function, expecting (descriptor, graph) as arguments. For the synchronous update, the rule function needs to return the new state.
gThe graph
134{
135 using namespace GraphUtils;
136 auto [it, it_end] = iterator_pair<iterate_over>(g);
137 apply_sync(it, it_end, g, rule);
138}

◆ apply_rule() [14/15]

template<IterateOver iterate_over, Update mode, Shuffle shuffle, typename Graph , typename Rule , typename std::enable_if_t< mode==Update::async, int > = 0>
void Utopia::apply_rule ( Rule &&  rule,
Graph &&  g 
)

Asynchronously apply a rule to graph entities, without shuffling.

This overload specifies the apply_rule function for an asynchronous update.

Warning
Not shuffling a rule often creates unwanted artifacts. Thus, to use this function, the Shuffle::off template argument needs to be given explicitly.
Template Parameters
iterate_overOver which kind of graph entity to iterate over. See IterateOver
modeThe update mode, see UpdateMode
GraphThe graph type
RuleThe rule type
Parameters
ruleThe rule function, expecting (descriptor, graph) as arguments. For an asynchronous update, returning the state is optional.
gThe graph
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}

◆ apply_rule() [15/15]

template<IterateOver iterate_over, Update mode, Shuffle shuffle = Shuffle::on, typename Graph , typename Rule , typename RNG , typename std::enable_if_t< mode==Update::async, int > = 0, typename std::enable_if_t< shuffle==Shuffle::on, int > = 0>
void Utopia::apply_rule ( Rule &&  rule,
Graph &&  g,
RNG &&  rng 
)

Asynchronously, in shuffled order, apply a rule to graph entities.

Using the given RNG, the iteration order is shuffled before the rule is applied sequentially to the specified entities.

Template Parameters
iterate_overOver which kind of graph entity to iterate over. See IterateOver
modeThe update mode, see UpdateMode
ShuffleWhether to shuffle the container
GraphThe graph type
RuleThe rule type
RNGThe random number generator type
Parameters
ruleThe rule function, expecting (descriptor, graph) as arguments. For an asynchronous update, returning the state is optional.
gThe graph
rngThe random number generator
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}

◆ is_void_rule()

template<typename State , typename Rule , typename... Args>
constexpr bool Utopia::is_void_rule ( )
constexpr

Helper function to check if the rule returns void

See also
rule_invoke_result
83{
84 return std::is_same_v<void, rule_invoke_result_t<State, Rule, Args...>>;
85}
typename rule_invoke_result< State, Rule, Args... >::type rule_invoke_result_t
Helper definition to query the rule result type.
Definition apply.hh:76

◆ make_tuple_from_tuple()

template<class Tuple >
constexpr decltype(auto) Utopia::make_tuple_from_tuple ( Tuple &&  t)
constexpr

Helper function to create a tuple from a tuple.

This seems silly, but calling std::make_tuple decays objects of std::reference_wrapper<T> toT&`, which is exactly what we use this function for.

This function creates a index sequence for the tuple and calls the implementation helper make_tuple_from_tuple_impl().

Parameters
tTuple to create a tuple from
Returns
Tuple containing the elements of the input tuple
115{
117 std::forward<Tuple>(t),
118 std::make_index_sequence<
119 std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
120}
constexpr decltype(auto) make_tuple_from_tuple_impl(Tuple &&t, std::index_sequence< I... >)
Helper function to create a tuple from a tuple using an index sequence.
Definition apply.hh:95

◆ make_tuple_from_tuple_impl()

template<class Tuple , std::size_t... I>
constexpr decltype(auto) Utopia::make_tuple_from_tuple_impl ( Tuple &&  t,
std::index_sequence< I... >   
)
constexpr

Helper function to create a tuple from a tuple using an index sequence.

Template Parameters
IIndex sequence to access the elements of the tuple
Parameters
tTuple to create a tuple from
Returns
Tuple containing the elements of the input tuple
96{
97 return std::make_tuple(std::get<I>(std::forward<Tuple>(t))...);
98}