Utopia 2
Framework for studying models of complex & adaptive systems.
Loading...
Searching...
No Matches
agent_manager.hh
Go to the documentation of this file.
1#ifndef UTOPIA_CORE_AGENTMANAGER_HH
2#define UTOPIA_CORE_AGENTMANAGER_HH
3
4#include <algorithm>
5#include <string_view>
6
7#include "types.hh"
8#include "exceptions.hh"
9#include "agent.hh"
10#include "select.hh"
11
12namespace Utopia {
19
30template<class AgentTraits, class Model>
32public:
35
37 using Space = typename Model::Space;
38
41
43 using Entity = Agent;
44
47
49 static constexpr DimType dim = Space::dim;
50
53
55 using MoveFunc = std::function<void(Agent&, const SpaceVec&)>;
56
58 using PosFunc = std::function<SpaceVec(const SpaceVec&)>;
59
61
64 using RuleFunc = std::function<AgentState(const std::shared_ptr<Agent>&)>;
65
67
70 using VoidRuleFunc = std::function<void(const std::shared_ptr<Agent>&)>;
71
73 using RNG = typename Model::RNG;
74
75
76private:
77 // -- Members --------–––––------------------------------------------------
80
82 const std::shared_ptr<spdlog::logger> _log;
83
86
88 const std::shared_ptr<RNG> _rng;
89
91 const std::shared_ptr<Space> _space;
92
95
98
101
102
103public:
104 // -- Constructors --------------------------------------------------------
105
107
120 AgentManager(const Model& model,
121 const DataIO::Config& custom_cfg = {})
122 :
123 _id_counter(0),
124 _log(model.get_logger()),
125 _cfg(setup_cfg(model, custom_cfg)),
126 _rng(model.get_rng()),
127 _space(model.get_space()),
128 _agents(),
131 {
132 setup_agents();
133 _log->info("AgentManager is all set up.");
134 }
135
137
145 AgentManager(const Model& model,
146 const AgentState initial_state,
147 const DataIO::Config& custom_cfg = {})
148 :
149 _id_counter(0),
150 _log(model.get_logger()),
151 _cfg(setup_cfg(model, custom_cfg)),
152 _rng(model.get_rng()),
153 _space(model.get_space()),
154 _agents(),
157 {
158 setup_agents(initial_state);
159 _log->info("AgentManager is all set up.");
160 }
161
164 const auto& log () const {
165 return _log;
166 }
167
169 const DataIO::Config& cfg () const {
170 return _cfg;
171 }
172
174 const std::shared_ptr<RNG>& rng () const {
175 return _rng;
176 }
177
179 const std::shared_ptr<Space>& space () const {
180 return _space;
181 }
182
185 return _agents;
186 }
187
190 return agents();
191 }
192
195 return _id_counter;
196 }
197
198 // -- Public interface ----------------------------------------------------
200 void move_to(const std::shared_ptr<Agent>& agent,
201 const SpaceVec& pos) const
202 {
203 _move_to_func(*agent, pos);
204 }
205
207 void move_to(Agent& agent,
208 const SpaceVec& pos) const
209 {
210 _move_to_func(agent, pos);
211 }
212
214 void move_by(const std::shared_ptr<Agent>& agent,
215 const SpaceVec& move_vec) const
216 {
217 _move_to_func(*agent, agent->position() + move_vec);
218 }
219
221 void move_by(Agent& agent,
222 const SpaceVec& move_vec) const
223 {
224 _move_to_func(agent, agent.position() + move_vec);
225 }
226
227
229
236 const std::shared_ptr<Agent>& add_agent (const AgentState& state,
237 const SpaceVec& pos)
238 {
239 _log->trace("Creating agent with ID {:d} ...", _id_counter);
240 _agents.emplace_back(
241 std::make_shared<Agent>(_id_counter, state, _prepare_pos(pos))
242 );
243 ++_id_counter;
244
245 return _agents.back();
246 }
247
249
258 const std::shared_ptr<Agent>& add_agent (const SpaceVec& pos,
259 const Config& custom_cfg = {})
260 {
261 // Check if flag for default constructor was set
263 if (custom_cfg.size()) {
264 throw std::runtime_error("custom_cfg was passed but "
265 "AgentTraits specified use of default constructor!");
266 }
267
268 return add_agent(AgentState(), pos);
269 }
270 else {
271 // Determine whether to use the given custom configuration or the
272 // one passed at construction of AgentManager
274
275 if (custom_cfg.size()) {
276 cfg = custom_cfg;
277 }
278 else {
279 cfg = _cfg["agent_params"];
280 }
281
282 // Distinguish the two config-constructible states
283 if constexpr (std::is_constructible<AgentState,
284 const DataIO::Config&,
285 const std::shared_ptr<RNG>&
286 >())
287 {
288 return add_agent(AgentState(cfg, _rng), pos);
289 }
290 else {
291 // Should be config-constructible
292 return add_agent(AgentState(cfg), pos);
293 }
294 }
295
296 }
298
303 const std::shared_ptr<Agent>& add_agent (const Config& custom_cfg = {}) {
304 return add_agent(random_pos(), custom_cfg);
305 }
306
307
309 void remove_agent (const std::shared_ptr<Agent>& agent) {
310 // Find the position in the agents container that belongs to the agent
311 const auto it = std::find(_agents.cbegin(), _agents.cend(), agent);
312
313 if (it == _agents.cend()) {
314 throw std::invalid_argument("The given agent is not handled by "
315 "this manager!");
316 };
317
318 _log->trace("Removing agent with ID {:d} ...", agent->id());
319 _agents.erase(it);
320 }
321
323
331 template<typename UnaryPredicate>
332 void erase_agent_if (UnaryPredicate&& condition) {
333 _agents.erase(
334 std::remove_if(_agents.begin(), _agents.end(), condition),
335 _agents.cend()
336 );
337 }
338
339
341
350 // Assert that the agents update synchronously
351 static_assert(AgentTraits::mode == Update::sync,
352 "The update_agents method only makes sense to call when agents "
353 "are set to be updated synchronously, which is not the case! "
354 "Either adapt the AgentTraits to that update mode or remove the "
355 "call to the update_agents method.");
356
357 // Go through all agents and update them
358 for (const auto& agent : _agents){
359 agent->update();
360 }
361 }
362
363 // .. Agent Selection .....................................................
365
373 template<SelectionMode mode, class... Args>
375 return select_entities<mode>(*this, std::forward<Args>(args)...);
376 }
377
379
391
392 // .. Agent relations .....................................................
393
395
400 SpaceVec displacement(const std::shared_ptr<Agent>& a,
401 const std::shared_ptr<Agent>& b) const
402 {
403 return this->_space->displacement(a->position(), b->position());
404 }
405
407
409 template<class NormType=std::size_t>
410 double distance(const std::shared_ptr<Agent>& a,
411 const std::shared_ptr<Agent>& b,
412 const NormType p = 2) const
413 {
414 return this->_space->distance(a->position(), b->position(), p);
415 }
416
417
418 // .. Agent neighborhood ..................................................
419
421
434 AgentContainer<Agent> neighbors_of(const std::shared_ptr<Agent>& agent,
435 const double radius) const
436 {
438 for (const auto& a : agents()) {
439 if (distance(a, agent) <= radius and a != agent) {
440 nbs.push_back(a);
441 }
442 }
443 return nbs;
444 }
445
446
447
448private:
449 // -- Helper functions ----------------------------------------------------
452 // Create a space vector with random relative positions [0, 1), and
453 // calculate the absolute position by multiplying element-wise with the
454 // extent of the space
455 std::uniform_real_distribution<double> dist(0., 1.);
456 return ( this->_space->extent
457 % SpaceVec().imbue([this,&dist](){return dist(*this->_rng);}));
458 }
459
460 // -- Setup functions -----------------------------------------------------
461
463
467 Config cfg;
468
469 if (custom_cfg.size() > 0) {
470 _log->debug("Using custom config for agent manager setup ...");
471 cfg = custom_cfg;
472 }
473 else {
474 _log->debug("Using '{}' model's configuration for agent manager "
475 "setup ... ", model.get_name());
476
477 if (not model.get_cfg()["agent_manager"]) {
478 throw std::invalid_argument("Missing config entry "
479 "'agent_manager' in model configuration! Either specify "
480 "that key or pass a custom configuration node to the "
481 "AgentManager constructor.");
482 }
483 cfg = model.get_cfg()["agent_manager"];
484 }
485 return cfg;
486 }
487
488
491 // By default, use a random initial position
492 std::string initial_pos_mode = "random";
493
494 // If given, extract the initial_position mode from the configuration
495 if (_cfg["initial_position"]) {
496 initial_pos_mode = get_as<std::string>("initial_position", _cfg);
497 }
498
499 // Return the agent position depending on the mode
500 if (initial_pos_mode == "random") {
501 return random_pos();
502 }
503 else {
504 throw std::invalid_argument("AgentManager got an invalid "
505 "configuration entry for 'initial_position': '"
506 + initial_pos_mode + "'. Valid options are: 'random'");
507 }
508 }
509
510
512
520 // Extract parameters from the configuration
521 if (not _cfg["initial_num_agents"]) {
522 throw std::invalid_argument("AgentManager is missing the "
523 "configuration entry 'initial_num_agents' that specifies the "
524 "number of agents to set up!");
525 }
526 const auto num_agents = get_as<IndexType>("initial_num_agents", _cfg);
527
528 // Construct all the agents with incremented IDs, the initial state
529 // and a random position
530 for (IndexType i=0; i<num_agents; ++i){
532 }
533
534 // Done. Shrink it.
535 _agents.shrink_to_fit();
536 _log->info("Populated agent container with {:d} agents.",
537 _agents.size());
538 }
539
540
542
560 // Distinguish depending on constructor.
561 // Is the default constructor to be used?
563 static_assert(std::is_default_constructible<AgentState>(),
564 "AgentTraits were configured to use the default constructor "
565 "to create agent states, but the AgentState is not "
566 "default-constructible! Either implement such a constructor, "
567 "unset the flag in the AgentTraits, or pass an explicit "
568 "initial agent state to the AgentManager.");
569
570 _log->info("Setting up agents using default constructor ...");
571
572 // Create the initial state (same for all agents)
574 }
575
576 // Is there a constructor available that allows passing the RNG?
577 else if constexpr (std::is_constructible<AgentState,
578 const DataIO::Config&,
579 const std::shared_ptr<RNG>&
580 >())
581 {
582 _log->info("Setting up agents using config constructor (with RNG) "
583 "...");
584
585 // Extract the configuration parameter
586 if (not _cfg["agent_params"]) {
587 throw std::invalid_argument("AgentManager is missing the "
588 "configuration entry 'agent_params' to set up the agents' "
589 "initial states!");
590 }
591 const auto agent_params = _cfg["agent_params"];
592
593 if (not _cfg["initial_num_agents"]){
594 throw std::invalid_argument("AgentManager is missing the "
595 "configuration entry 'initial_num_agents' to set up the "
596 "agents!"
597 );
598 }
599 const auto initial_num_agents =
600 get_as<IndexType>("initial_num_agents", _cfg);
601
602 // Populate the container, creating the agent state anew each time
603 for (IndexType i=0; i<initial_num_agents; i++) {
606 }
607
608 // Done. Shrink it.
609 _agents.shrink_to_fit();
610 _log->info("Populated agent container with {:d} agents.",
611 _agents.size());
612 }
613
614 // As default, require a Config constructor
615 else {
616 static_assert(std::is_constructible<AgentState,
617 const DataIO::Config&>(),
618 "AgentManager::AgentState needs to be constructible using "
619 "const DataIO::Config& as only argument. Either implement "
620 "such a constructor, pass an explicit initial agent state to "
621 "the AgentManager, or set the AgentTraits such that a default "
622 "constructor is to be used.");
623
624 _log->info("Setting up agents using config constructor ...");
625
626 // Extract the configuration parameter
627 if (not _cfg["agent_params"]) {
628 throw std::invalid_argument("AgentManager is missing the "
629 "configuration entry 'agent_params' to set up the agents' "
630 "initial states!");
631 }
632
633 // Create the initial state (same for all agents)
634 return setup_agents(AgentState(_cfg["agent_params"]));
635 }
636 // This point is never reached.
637 }
638
639
641
648 // Need to distinguish by periodicity of the space the agents live in
649 if (_space->periodic) {
650 // Can simply use the space's mapping function
651 return [this](Agent& agent, const SpaceVec& pos){
652 agent.set_pos(this->_space->map_into_space(pos));
653 };
654 }
655 else {
656 // For nonperiodic space, need to make sure the position is valid
657 return [this](Agent& agent, const SpaceVec& pos){
658 if (not this->_space->contains(pos)) {
659 throw OutOfSpace(pos, this->_space,
660 "Could not move agent!");
661 }
662
663 // Set the new agent position
664 agent.set_pos(pos);
665 };
666 }
667 }
668
671
680 // If periodic, map the position back into space
681 if (_space->periodic) {
682 return
683 [this](const SpaceVec& pos){
684 return this->_space->map_into_space(pos);
685 };
686 }
687 // If non-periodic, check wether the position is valid
688 else {
689 return
690 [this](const SpaceVec& pos) {
691 if (not _space->contains(pos)) {
692 throw OutOfSpace(pos, _space,
693 "Given position is out of space!");
694 }
695 return pos;
696 };
697 }
698 }
699
700};
701
702
703// end group AgentManager
708} //namespace Utopia
709
710#endif // UTOPIA_CORE_AGENTMANAGER_HH
An agent is a slightly specialized state container.
Definition agent.hh:47
The agent manager manages the agents living in a model.
Definition agent_manager.hh:31
const AgentContainer< Agent > & agents() const
Return const reference to the managed agents.
Definition agent_manager.hh:184
const std::shared_ptr< Space > & space() const
Return pointer to the space, for convenience.
Definition agent_manager.hh:179
void move_by(const std::shared_ptr< Agent > &agent, const SpaceVec &move_vec) const
Move an agent relative to its current position.
Definition agent_manager.hh:214
void update_agents()
Update the agents.
Definition agent_manager.hh:349
std::function< SpaceVec(const SpaceVec &)> PosFunc
The type of the function that prepares the position of a new agent.
Definition agent_manager.hh:58
void setup_agents()
Set up agents container via config or default constructor.
Definition agent_manager.hh:559
MoveFunc _move_to_func
Function that will be used for moving agents, called by move_* methods.
Definition agent_manager.hh:97
const std::shared_ptr< Agent > & add_agent(const AgentState &state, const SpaceVec &pos)
Create an agent and associate it with this AgentManager.
Definition agent_manager.hh:236
IndexType id_counter() const
Return the ID counter.
Definition agent_manager.hh:194
typename AgentTraits::State AgentState
The type of the agent state.
Definition agent_manager.hh:46
SpaceVec random_pos() const
Returns a valid (uniformly) random position in space.
Definition agent_manager.hh:451
const AgentContainer< Agent > & entities() const
Return const reference to the entities managed by this manager: agents.
Definition agent_manager.hh:189
const std::shared_ptr< Agent > & add_agent(const SpaceVec &pos, const Config &custom_cfg={})
add_agent overload for auto-constructed agent states
Definition agent_manager.hh:258
void erase_agent_if(UnaryPredicate &&condition)
Remove agents if the given condition is met.
Definition agent_manager.hh:332
const std::shared_ptr< RNG > _rng
The model's random number generator.
Definition agent_manager.hh:88
void remove_agent(const std::shared_ptr< Agent > &agent)
Removes the given agent from the agent manager.
Definition agent_manager.hh:309
SpaceVec displacement(const std::shared_ptr< Agent > &a, const std::shared_ptr< Agent > &b) const
Returns the (shortest) displacement vector between two agents.
Definition agent_manager.hh:400
double distance(const std::shared_ptr< Agent > &a, const std::shared_ptr< Agent > &b, const NormType p=2) const
Returns the (shortest) distance between two agents.
Definition agent_manager.hh:410
AgentManager(const Model &model, const DataIO::Config &custom_cfg={})
Construct an agent manager.
Definition agent_manager.hh:120
IndexType _id_counter
Counts the number of agents created with this manager, used for new IDs.
Definition agent_manager.hh:79
SpaceVecType< dim > SpaceVec
The type of the vectors that represent physical quantities.
Definition agent_manager.hh:52
std::function< void(const std::shared_ptr< Agent > &)> VoidRuleFunc
The type of a void rule function acting on agents of this agent manager.
Definition agent_manager.hh:70
AgentContainer< Agent > _agents
Storage container for agents.
Definition agent_manager.hh:94
PosFunc _prepare_pos
Function that will be used to prepare positions for adding an agent.
Definition agent_manager.hh:100
PosFunc setup_prepare_pos_func() const
Definition agent_manager.hh:679
AgentManager(const Model &model, const AgentState initial_state, const DataIO::Config &custom_cfg={})
Construct an agent manager, using the same initial state for all agents.
Definition agent_manager.hh:145
typename Model::RNG RNG
The random number generator type.
Definition agent_manager.hh:73
const std::shared_ptr< RNG > & rng() const
Return a reference to the shared random number generator.
Definition agent_manager.hh:174
void setup_agents(const AgentState &initial_state)
Set up the manager's agent container with initial states.
Definition agent_manager.hh:519
const DataIO::Config & cfg() const
Return the configuration used for building this AgentManager.
Definition agent_manager.hh:169
void move_to(const std::shared_ptr< Agent > &agent, const SpaceVec &pos) const
Move an agent to a new position in the space.
Definition agent_manager.hh:200
const std::shared_ptr< spdlog::logger > _log
The logger (same as the model this manager resides in)
Definition agent_manager.hh:82
static constexpr DimType dim
The dimensionality of the space.
Definition agent_manager.hh:49
void move_to(Agent &agent, const SpaceVec &pos) const
Move an agent to a new position in the space.
Definition agent_manager.hh:207
const DataIO::Config _cfg
Agent manager configuration node.
Definition agent_manager.hh:85
void move_by(Agent &agent, const SpaceVec &move_vec) const
Move an agent relative to its current position.
Definition agent_manager.hh:221
const std::shared_ptr< Space > _space
The physical space the agents are to reside in.
Definition agent_manager.hh:91
AgentContainer< Agent > select_agents(const Config &sel_cfg)
Select entities according to parameters specified in a configuration.
Definition agent_manager.hh:388
MoveFunc setup_move_to_func() const
Depending on periodicity, return the function to move agents in space.
Definition agent_manager.hh:647
std::function< AgentState(const std::shared_ptr< Agent > &)> RuleFunc
The type of a rule function acting on agents of this agent manager.
Definition agent_manager.hh:64
AgentContainer< Agent > neighbors_of(const std::shared_ptr< Agent > &agent, const double radius) const
Returns a container of all agents within a certain radius.
Definition agent_manager.hh:434
std::function< void(Agent &, const SpaceVec &)> MoveFunc
The type of the move function type.
Definition agent_manager.hh:55
const auto & log() const
Definition agent_manager.hh:164
SpaceVec initial_agent_pos()
Setup helper used to determine a single agent's initial position.
Definition agent_manager.hh:490
const std::shared_ptr< Agent > & add_agent(const Config &custom_cfg={})
add_agent overload for auto constructed states and random position
Definition agent_manager.hh:303
AgentContainer< Agent > select_agents(Args &&... args)
Select agents using the Utopia::select_entities interface.
Definition agent_manager.hh:374
Config setup_cfg(const Model &model, const Config &custom_cfg)
Set up the agent manager configuration member.
Definition agent_manager.hh:466
Utopia::Agent< AgentTraits, Space > Agent
The type of the managed agents.
Definition agent_manager.hh:40
typename Model::Space Space
The type of the space.
Definition agent_manager.hh:37
Base class interface for Models using the CRT Pattern.
Definition model.hh:112
typename ModelTypes::Space Space
Data type of the space this model resides in.
Definition model.hh:131
typename ModelTypes::RNG RNG
Data type of the shared RNG.
Definition model.hh:128
An exception class for invalid positions in Utopia::Space.
Definition exceptions.hh:113
YAML::Node Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition types.hh:71
Container select_entities(const Manager &mngr, const DataIO::Config &sel_cfg)
Select entities according to parameters specified in a configuration.
Definition select.hh:213
@ sync
Synchronous update.
SelectionMode
Possible selection modes; availability depends on choice of manager.
Definition select.hh:78
@ condition
Select if a condition is fulfilled.
Definition agent.hh:11
DataIO::Config Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition types.hh:80
EntityContainer< AgentType > AgentContainer
Type of the variably sized container for agents.
Definition types.hh:30
arma::Col< double >::fixed< dim > SpaceVecType
Type for vector-like data that is associated with a physical space.
Definition types.hh:61
unsigned short DimType
Type for dimensions, i.e. very small unsigned integers.
Definition types.hh:34
std::size_t IndexType
Type for indices, i.e. values used for container indexing, agent IDs, ...
Definition types.hh:40
static constexpr bool use_default_state_constructor
Whether to use the default constructor for constructing a entity state.
Definition entity.hh:57
static constexpr Update mode
Whether the entitys should be synchronously updated.
Definition entity.hh:54
StateType State
Type of the entitys' state container.
Definition entity.hh:51
static constexpr std::size_t dim
The dimensionality of the space.
Definition space.hh:30