Utopia  2
Framework for studying models of complex & adaptive systems.
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 
12 namespace Utopia {
19 
30 template<class AgentTraits, class Model>
32 public:
35 
37  using Space = typename Model::Space;
38 
41 
43  using Entity = Agent;
44 
46  using AgentState = typename AgentTraits::State;
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 
76 private:
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 
103 public:
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 
184  const AgentContainer<Agent>& agents () const {
185  return _agents;
186  }
187 
189  const AgentContainer<Agent>& entities () const {
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 
349  void update_agents() {
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 
389  return select_entities(*this, sel_cfg);
390  }
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  {
437  AgentContainer<Agent> nbs{};
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 
448 private:
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 
466  Config setup_cfg(const Model& model, const Config& custom_cfg) {
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 
519  void setup_agents(const AgentState& initial_state) {
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){
531  add_agent(initial_state, initial_agent_pos());
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 
559  void setup_agents() {
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++) {
604  add_agent(AgentState(agent_params, _rng),
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
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
AgentContainer< Agent > select_agents(const Config &sel_cfg)
Select entities according to parameters specified in a configuration.
Definition: agent_manager.hh:388
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
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
const AgentContainer< Agent > & entities() const
Return const reference to the entities managed by this manager: agents.
Definition: agent_manager.hh:189
SpaceVec random_pos() const
Returns a valid (uniformly) random position in space.
Definition: agent_manager.hh:451
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
const DataIO::Config & cfg() const
Return the configuration used for building this AgentManager.
Definition: agent_manager.hh:169
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
AgentContainer< Agent > select_agents(Args &&... args)
Select agents using the Utopia::select_entities interface.
Definition: agent_manager.hh:374
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
const std::shared_ptr< RNG > & rng() const
Return a reference to the shared random number generator.
Definition: agent_manager.hh:174
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
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
const auto & log() const
Definition: agent_manager.hh:164
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
void setup_agents(const AgentState &initial_state)
Set up the manager's agent container with initial states.
Definition: agent_manager.hh:519
const std::shared_ptr< Space > & space() const
Return pointer to the space, for convenience.
Definition: agent_manager.hh:179
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
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 AgentContainer< Agent > & agents() const
Return const reference to the managed agents.
Definition: agent_manager.hh:184
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
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
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
std::function< void(Agent &, const SpaceVec &)> MoveFunc
The type of the move function type.
Definition: agent_manager.hh:55
SpaceVec initial_agent_pos()
Setup helper used to determine a single agent's initial position.
Definition: agent_manager.hh:490
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
std::shared_ptr< spdlog::logger > get_logger() const
Return a pointer to the logger of this model.
Definition: model.hh:485
typename ModelTypes::Space Space
Data type of the space this model resides in.
Definition: model.hh:131
std::string get_name() const
Return the name of this model instance.
Definition: model.hh:417
Config get_cfg() const
Return the config node of this model.
Definition: model.hh:412
std::shared_ptr< RNG > get_rng() const
Return a pointer to the shared RNG.
Definition: model.hh:480
typename ModelTypes::RNG RNG
Data type of the shared RNG.
Definition: model.hh:128
const std::shared_ptr< Space > & get_space() const
Return the space this model resides in.
Definition: model.hh:388
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
SelectionMode
Possible selection modes; availability depends on choice of manager.
Definition: select.hh:72
Container select_entities(const Manager &mngr, const DataIO::Config &sel_cfg)
Select entities according to parameters specified in a configuration.
Definition: select.hh:200
@ condition
Select if a condition is fulfilled.
@ sync
Synchronous update.
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