Utopia  2
Framework for studying models of complex & adaptive systems.
PredatorPrey.hh
Go to the documentation of this file.
1 #ifndef UTOPIA_MODELS_PREDATORPREY_HH
2 #define UTOPIA_MODELS_PREDATORPREY_HH
3 
4 #include <cstdint>
5 #include <algorithm>
6 #include <random>
7 
8 #include <utopia/core/apply.hh>
9 #include <utopia/core/model.hh>
11 
12 #include "species.hh"
13 
14 
15 namespace Utopia {
16 namespace Models {
17 namespace PredatorPrey {
18 
19 // ++ Type definitions ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
20 
22 struct State {
25 
28 
31 
33  template<class RNGType>
34  State(const DataIO::Config& cfg, const std::shared_ptr<RNGType>& rng)
35  :
36  predator{},
37  prey{},
38  moved_predator(false)
39  {
40  std::uniform_real_distribution<double> dist(0., 1.);
41 
42  // Get the threshold probability value
43  const auto p_prey = get_as<double>("p_prey", cfg);
44  const auto p_predator = get_as<double>("p_predator", cfg);
45 
46  // Set a prey on a cell with the given probability by generating a
47  // random number in [0, 1) and compare it to wanted probability.
48  if (dist(*rng) < p_prey){
49  prey.on_cell = true;
50  prey.resources = get_as<double>("init_resources",
51  cfg["prey"]);
52  }
53 
54  // Set a predator on a cell with the given probability.
55  if (dist(*rng) < p_predator){
56  predator.on_cell = true;
57  predator.resources = get_as<double>("init_resources",
58  cfg["predator"]);
59  }
60  }
61 };
62 
63 
65 
70 
73 
74 
75 // ++ Model definition ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
76 
78 
93  : public Model<PredatorPrey, ModelTypes>
94 {
95 public:
98 
100  using DataSet = typename Base::DataSet;
101 
104 
106  using Cell = typename CellManager::Cell;
107 
109  using Rule = typename CellManager::RuleFunc;
110 
111 
112 private:
113  // Base members: _time, _name, _cfg, _hdfgrp, _rng, _monitor, _log, _space
114  // ... but you should definitely check out the documentation ;)
115 
116  // -- Members -------------------------------------------------------------
119 
120  // .. Model parameters ....................................................
123 
124  // .. Temporary objects ...................................................
127 
130 
133 
134  // Uniform real distribution [0, 1) for evaluating probabilities
135  std::uniform_real_distribution<double> _prob_distr;
136 
137 
138  // .. Datasets ............................................................
140  const std::shared_ptr<DataSet> _dset_prey;
141 
143  const std::shared_ptr<DataSet> _dset_predator;
144 
146  const std::shared_ptr<DataSet> _dset_resource_prey;
147 
149  const std::shared_ptr<DataSet> _dset_resource_predator;
150 
151 
152  // .. Rule functions and helper methods ...................................
154 
158  Rule _cost_of_living = [this](const auto& cell) {
159  // Get the state of the cell
160  auto& state = cell->state;
161 
162  // Subtract the cost of living and clamp the resources to the limits:
163  // If the resources exceed the maximal resources they are equal to
164  // the maximal resources and if they go below 0 they are mapped to 0.
165  state.predator.resources =
166  std::clamp( state.predator.resources
169  state.prey.resources =
170  std::clamp( state.prey.resources
173 
174  // Remove predators that have no resources.
175  if (state.predator.on_cell and state.predator.resources <= 0.) {
176  state.predator.on_cell = false;
177  }
178 
179  // Remove prey that have no resources.
180  if (state.prey.on_cell and state.prey.resources <= 0.) {
181  state.prey.on_cell = false;
182  }
183 
184  return state;
185  };
186 
188 
191  void move_predator_to_nb_cell(const std::shared_ptr<Cell>& cell,
192  const std::shared_ptr<Cell>& nb_cell) {
193  auto& state = cell->state;
194  auto& nb_state = nb_cell->state;
195 
196  nb_state.predator.on_cell = true;
197  nb_state.predator.resources = state.predator.resources;
198 
199  state.predator.on_cell = false;
200  state.predator.resources = 0.;
201  }
202 
204 
207  void move_prey_to_nb_cell(const std::shared_ptr<Cell>& cell,
208  const std::shared_ptr<Cell>& nb_cell) {
209  auto& state = cell->state;
210  auto& nb_state = nb_cell->state;
211 
212  nb_state.prey.on_cell = true;
213  nb_state.prey.resources = state.prey.resources;
214 
215  state.prey.on_cell = false;
216  state.prey.resources = 0.;
217  }
218 
220 
225  Rule _move_predator = [this](const auto& cell) {
226  // Get the state of the Cell
227  auto& state = cell->state;
228 
229  // Case: only a predator is on the cell
230  if ((state.predator.on_cell) and (not state.moved_predator)) {
231  // checking neighbouring cells for prey and moving to that cell
232 
233  // clear the containers for cells that contain prey or empty cells
234  // in the neighbourhood
235  _prey_cell.clear();
236  _empty_cell.clear();
237 
238  for (const auto& nb : this->_cm.neighbors_of(cell)) {
239  auto& nb_state = nb->state;
240 
241  if ((nb_state.prey.on_cell) and (not nb_state.predator.on_cell)) {
242  _prey_cell.push_back(nb);
243  }
244  // if neither a prey nor a predator is on the cell, then
245  // it is empty
246  else if (not nb_state.predator.on_cell) {
247  _empty_cell.push_back(nb);
248  }
249  }
250 
251  // if there is prey in the vicinity move the predator to a random
252  // prey cell
253  if (_prey_cell.size() > 0) {
254  // distribution to choose a random cell for the movement if
255  // there is more than one
256  std::uniform_int_distribution<>
257  dist_prey(0, _prey_cell.size() - 1);
258 
259  // Select a random neighbor cell and move the predator to it
260  auto nb_cell = _prey_cell[dist_prey(*this->_rng)];
261  // Mark target cell as cell with moved predator
262  nb_cell->state.moved_predator=true;
263  move_predator_to_nb_cell(cell, nb_cell);
264  }
265 
266  // if there is no prey the predator makes a random move
267  else if (_empty_cell.size() > 0) {
268  // distribution to choose a random cell for the movement if
269  // there is more than one
270  std::uniform_int_distribution<>
271  dist_empty(0, _empty_cell.size() - 1);
272 
273  // Select a random empty neighbor cell and move the predator
274  // to it
275  auto nb_cell = _empty_cell[dist_empty(*this->_rng)];
276  // Mark target cell as cell with moved predator
277  nb_cell->state.moved_predator=true;
278  move_predator_to_nb_cell(cell, nb_cell);
279  }
280  }
281  return state;
282  };
283 
285 
288  Rule _flee_prey = [this](const auto& cell) {
289  auto& state = cell->state;
290 
291  if (state.prey.on_cell and state.predator.on_cell) {
292  // Flee, if possible, with a given flee probability
293  if (this->_prob_distr(*this->_rng) < _params.prey.p_flee){
294  // Collect empty neighboring cells to which the prey could flee
295  _empty_cell.clear();
296  for (const auto& nb : this->_cm.neighbors_of(cell)) {
297  if ( (not nb->state.prey.on_cell)
298  and (not nb->state.predator.on_cell)) {
299  _empty_cell.push_back(nb);
300  }
301  }
302 
303  // If there is an empty cell, move there
304  if (_empty_cell.size() > 0) {
305  // Choose a random cell to move to
306  std::uniform_int_distribution<>
307  dist(0, _empty_cell.size() - 1);
308 
309  auto nb_cell = _empty_cell[dist(*this->_rng)];
310  move_prey_to_nb_cell(cell, nb_cell);
311  }
312  }
313  }
314  return state;
315  // NOTE Should the case be added that the neighboring cell has a predator?
316  // In case that there are no empty cells.
317  // Then the prey has again a small probability to flee in the next
318  // round.
319  };
320 
322 
327  Rule _eat = [this](const auto& cell) {
328  // Get the state of the cell
329  auto& state = cell->state;
330 
331  // Predator eats prey
332  if (state.predator.on_cell and state.prey.on_cell) {
333  // Increase the predator's resources and assure that the resource
334  // maximum is not exceeded by clamping into the correct interval.
335  state.predator.resources = std::clamp(
336  state.predator.resources
338  0.,
340 
341  // Remove the prey from the cell
342  state.prey.on_cell = false;
343  state.prey.resources = 0.;
344  }
345 
346  // Prey eats grass
347  else if (state.prey.on_cell == true) {
348  // Increase the resources and clamp to the allowed range
349  // [0, resource_max]
350  state.prey.resources = std::clamp(
351  state.prey.resources
353  0.,
355  }
356  return state;
357  };
358 
360 
363  Rule _repro = [this](const auto& cell) {
364  // Get the state of the cell
365  auto& state = cell->state;
366 
367  // Reproduce predators
368  if ( state.predator.on_cell
369  and (this->_prob_distr(*this->_rng) < _params.predator.repro_prob)
370  and (state.predator.resources >= _params.predator.repro_resource_requ))
371  {
372  // Collect available neighboring spots without predators
373  _repro_cell.clear();
374  for (const auto& nb : this->_cm.neighbors_of(cell)) {
375  if (not nb->state.predator.on_cell) {
376  _repro_cell.push_back(nb);
377  }
378  }
379 
380  // Choose an available neighboring cell to put an offspring on
381  if (_repro_cell.size() > 0) {
382  // choose a random cell for the offspring to be placed on
383  std::uniform_int_distribution<> dist(0, _repro_cell.size()-1);
384  const auto& nb_cell = _repro_cell[dist(*this->_rng)];
385  auto& nb_state = nb_cell->state;
386 
387  // neighboring cell has now a predator.
388  // Congratulations to your new baby! :)
389  nb_state.predator.on_cell = true;
390 
391  // transfer resources from parent to offspring
392  nb_state.predator.resources = _params.predator.repro_cost;
393  state.predator.resources -= _params.predator.repro_cost;
394  }
395  }
396 
397  // Reproduce prey
398  if ( state.prey.on_cell
399  and this->_prob_distr(*this->_rng) < _params.prey.repro_prob
400  and state.prey.resources >= _params.prey.repro_resource_requ)
401  {
402  _repro_cell.clear();
403 
404  for (const auto& nb : this->_cm.neighbors_of(cell)) {
405  if (not nb->state.prey.on_cell) {
406  _repro_cell.push_back(nb);
407  }
408  }
409 
410  if (_repro_cell.size() > 0) {
411  // choose a random cell for the offspring to be placed on
412  std::uniform_int_distribution<> dist(0, _repro_cell.size() - 1);
413  // TODO Use std::sample
414 
415  auto nb_cell = _repro_cell[dist(*this->_rng)];
416 
417  // neighboring cell has now a prey.
418  // Congratulations to your new baby! :)
419  nb_cell->state.prey.on_cell = true;
420 
421  // transfer energy from parent to offspring
422  nb_cell->state.prey.resources = _params.prey.repro_cost;
423  state.prey.resources -= _params.prey.repro_cost;
424 
425  // NOTE Should there be some dissipation of resources during
426  // reproduction?
427  }
428  }
429  return state;
430  };
431 
433  Rule _reset_predator_movement = [](const auto& cell) {
434  auto& state = cell->state;
435  state.moved_predator = false;
436  return state;
437  };
438 
439 
440 public:
441  // -- Model setup ---------------------------------------------------------
443 
451  template <class ParentModel>
453  const std::string& name,
454  ParentModel& parent_model,
455  const DataIO::Config& custom_cfg = {}
456  )
457  :
458  Base(name, parent_model, custom_cfg),
459  // Initialize the cell manager, binding it to this model
460  _cm(*this),
461 
462  // Extract model parameters
463  _params(this->_cfg),
464 
465  // Temporary cell containers
466  _prey_cell(),
467  _empty_cell(),
468  _repro_cell(),
469 
470  // Initialize distributions
471  _prob_distr(0., 1.),
472 
473  // Create datasets
474  _dset_prey(this->create_cm_dset("prey", _cm)),
475  _dset_predator(this->create_cm_dset("predator", _cm)),
476  _dset_resource_prey(this->create_cm_dset("resource_prey", _cm)),
477  _dset_resource_predator(this->create_cm_dset("resource_predator", _cm))
478  {
479  // Load the cell state from a file, overwriting the current state
480  if (this->_cfg["cell_states_from_file"]) {
481  const auto& cs_cfg = this->_cfg["cell_states_from_file"];
482  const auto hdf5_file = get_as<std::string>("hdf5_file", cs_cfg);
483  const auto predator_init_resources = get_as<double>("init_resources",
484  this->_cfg["cell_manager"]["cell_params"]["predator"]);
485  const auto prey_init_resources = get_as<double>("init_resources",
486  this->_cfg["cell_manager"]["cell_params"]["prey"]);
487  if (get_as<bool>("load_predator", cs_cfg)) {
488  this->_log->info("Loading predator positions from file ...");
489 
490  // Use the CellManager to set the cell state from the data
491  // given by the `predator` dataset. Load as int to be able to
492  // detect that a user supplied invalid values (better than
493  // failing silently, which would happen with booleans).
494  _cm.set_cell_states<int>(hdf5_file, "predator",
495  [predator_init_resources](auto& cell, const int on_cell){
496  if (on_cell == 0 or on_cell == 1) {
497  cell->state.predator.on_cell = on_cell;
498  if(on_cell == 1){
499  cell->state.predator.resources =
500  predator_init_resources;
501  }
502  else{
503  cell->state.predator.resources = 0;
504  }
505  return;
506  }
507  throw std::invalid_argument("While setting predator "
508  "positions, encountered an invalid value: "
509  + std::to_string(on_cell) + ". Allowed: 0 or 1.");
510  }
511  );
512 
513  this->_log->info("Predator positions loaded.");
514  }
515 
516  if (get_as<bool>("load_prey", cs_cfg)) {
517  this->_log->info("Loading prey positions from file ...");
518 
519  _cm.set_cell_states<int>(hdf5_file, "prey",
520  [prey_init_resources](auto& cell, const int on_cell){
521  if (on_cell == 0 or on_cell == 1) {
522  cell->state.prey.on_cell = on_cell;
523  if(on_cell == 1){
524  cell->state.prey.resources =
525  prey_init_resources;
526  }
527  else{
528  cell->state.prey.resources = 0;
529  }
530  return;
531  }
532  throw std::invalid_argument("While setting prey "
533  "positions, encountered an invalid value: "
534  + std::to_string(on_cell) + ". Allowed: 0 or 1.");
535  }
536  );
537 
538  this->_log->info("Prey positions loaded.");
539  }
540  }
541 
542  // Reserve memory in the size of the neighborhood for the temp. vectors
543  const auto nb_size = _cm.nb_size();
544  _prey_cell.reserve(nb_size);
545  _empty_cell.reserve(nb_size);
546  _repro_cell.reserve(nb_size);
547 
548  // Initialization finished
549  this->_log->info("{} model fully set up.", this->_name);
550  }
551 
552 public:
553  // -- Public Interface ----------------------------------------------------
554  // .. Simulation Control ..................................................
555 
557 
564  void perform_step() {
565  apply_rule<Update::async, Shuffle::off>
567 
568  apply_rule<Update::async, Shuffle::on>
569  (_move_predator, _cm.cells(), *this->_rng);
570 
571  apply_rule<Update::async, Shuffle::on>
572  (_flee_prey, _cm.cells(), *this->_rng);
573 
574  apply_rule<Update::async, Shuffle::off>
575  (_eat, _cm.cells());
576 
577  apply_rule<Update::async, Shuffle::on>
578  (_repro, _cm.cells(), *this->_rng);
579 
580  apply_rule<Update::sync>
582  }
583 
585  void monitor () {
587  auto [pred_density, prey_density] = [this](){
588  double predator_sum = 0.;
589  double prey_sum = 0.;
590  double num_cells = this->_cm.cells().size();
591 
592  for (const auto& cell : this->_cm.cells()) {
593  auto state = cell->state;
594 
595  if (state.prey.on_cell) {
596  prey_sum++;
597  }
598 
599  if (state.predator.on_cell) {
600  predator_sum++;
601  }
602  }
603  return std::pair{predator_sum / num_cells, prey_sum / num_cells};
604  }();
605 
606  this->_monitor.set_entry("predator_density", pred_density);
607  this->_monitor.set_entry("prey_density", prey_density);
608  }
609 
611 
619  void write_data() {
620  // Predator
621  _dset_predator->write(_cm.cells().begin(), _cm.cells().end(),
622  [](const auto& cell) {
623  return static_cast<char>(cell->state.predator.on_cell);
624  }
625  );
626 
627  // Prey
628  _dset_prey->write(_cm.cells().begin(), _cm.cells().end(),
629  [](const auto& cell) {
630  return static_cast<char>(cell->state.prey.on_cell);
631  }
632  );
633 
634  // resource of predator
635  _dset_resource_predator->write(_cm.cells().begin(), _cm.cells().end(),
636  [](const auto& cell) {
637  return cell->state.predator.resources;
638  }
639  );
640 
641  // resource of prey
642  _dset_resource_prey->write(_cm.cells().begin(), _cm.cells().end(),
643  [](const auto& cell) {
644  return cell->state.prey.resources;
645  }
646  );
647  }
648 };
649 
650 } // namespace PredatorPrey
651 } // namespace Models
652 } // namespace Utopia
653 
654 #endif // UTOPIA_MODELS_PREDATORPREY_HH
void set_cell_states(const std::string &hdf5_file, const std::string &dset_path, const SetterFunc &setter_func)
Set all cell states using information from a HDF5 file.
Definition: cell_manager.hh:388
const CellContainer< Cell > & cells() const
Return const reference to the managed CA cells.
Definition: cell_manager.hh:219
typename std::function< CellState(const std::shared_ptr< Cell > &)> RuleFunc
The type of a rule function acting on cells of this cell manager.
Definition: cell_manager.hh:84
auto nb_size() const
Return the (maximum) size of the currently selected neighborhood.
Definition: cell_manager.hh:450
CellContainer< Cell > neighbors_of(const Cell &cell) const
Retrieve the given cell's neighbors.
Definition: cell_manager.hh:458
Utopia::Cell< CellTraits > Cell
Type of the managed cells.
Definition: cell_manager.hh:47
Base class interface for Models using the CRT Pattern.
Definition: model.hh:112
Monitor _monitor
The monitor.
Definition: model.hh:188
std::shared_ptr< DataSet > create_cm_dset(const std::string name, const CellManager &cm, const std::size_t compression_level=1, const std::vector< hsize_t > chunksize={})
Create a dataset storing data from a CellManager.
Definition: model.hh:849
typename ModelTypes::DataSet DataSet
Data type that is used for storing data.
Definition: model.hh:125
const Config _cfg
Config node belonging to this model instance.
Definition: model.hh:158
const std::string _name
Name of the model instance.
Definition: model.hh:149
const std::shared_ptr< spdlog::logger > _log
The (model) logger.
Definition: model.hh:164
const std::shared_ptr< RNG > _rng
The RNG shared between models.
Definition: model.hh:161
PredatorPrey Model on grid cells.
Definition: PredatorPrey.hh:94
void perform_step()
Perform an iteration step.
Definition: PredatorPrey.hh:564
void move_prey_to_nb_cell(const std::shared_ptr< Cell > &cell, const std::shared_ptr< Cell > &nb_cell)
Move a prey to a neighboring cell.
Definition: PredatorPrey.hh:207
typename Base::DataSet DataSet
Data type for a dataset.
Definition: PredatorPrey.hh:100
CellContainer< Cell > _empty_cell
A container to temporarily accumulate empty neighbour cells.
Definition: PredatorPrey.hh:129
SpeciesParams _params
Predator-specific model parameters.
Definition: PredatorPrey.hh:122
Rule _flee_prey
Define the movement rule of prey.
Definition: PredatorPrey.hh:288
void monitor()
Monitor model information.
Definition: PredatorPrey.hh:585
const std::shared_ptr< DataSet > _dset_predator
Dataset of Predator locations on the grid.
Definition: PredatorPrey.hh:143
Rule _cost_of_living
Cost of Living.
Definition: PredatorPrey.hh:158
std::uniform_real_distribution< double > _prob_distr
Definition: PredatorPrey.hh:135
Rule _reset_predator_movement
Resets the movement flag of predators to "false" for next turn.
Definition: PredatorPrey.hh:433
const std::shared_ptr< DataSet > _dset_prey
Dataset of Prey locations on the grid.
Definition: PredatorPrey.hh:140
void write_data()
Write data.
Definition: PredatorPrey.hh:619
const std::shared_ptr< DataSet > _dset_resource_predator
Dataset of Predator resources on the grid.
Definition: PredatorPrey.hh:149
void move_predator_to_nb_cell(const std::shared_ptr< Cell > &cell, const std::shared_ptr< Cell > &nb_cell)
Move a predator to a neighboring cell.
Definition: PredatorPrey.hh:191
Rule _repro
Define the reproduction rule.
Definition: PredatorPrey.hh:363
typename CellManager::Cell Cell
The type of a cell.
Definition: PredatorPrey.hh:106
CellContainer< Cell > _repro_cell
A container to temporarily accumulate neighbour cells for reproduction.
Definition: PredatorPrey.hh:132
PredatorPrey(const std::string &name, ParentModel &parent_model, const DataIO::Config &custom_cfg={})
Construct the PredatorPrey model.
Definition: PredatorPrey.hh:452
Model< PredatorPrey, ModelTypes > Base
The base model.
Definition: PredatorPrey.hh:97
const std::shared_ptr< DataSet > _dset_resource_prey
Dataset of Prey resources on the grid.
Definition: PredatorPrey.hh:146
Rule _eat
Define the eating rule.
Definition: PredatorPrey.hh:327
CellManager _cm
The cell manager.
Definition: PredatorPrey.hh:118
Rule _move_predator
Define the movement rule of predator.
Definition: PredatorPrey.hh:225
CellContainer< Cell > _prey_cell
A container to temporarily accumulate the prey neighbour cells.
Definition: PredatorPrey.hh:126
typename CellManager::RuleFunc Rule
Type of the update rules.
Definition: PredatorPrey.hh:109
YAML::Node Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition: types.hh:71
std::string to_string(const Config &node)
Given a config node, returns a string representation of it.
Definition: cfg_utils.hh:110
std::mt19937 rng
– Type definitions ----------------------------------------------------—
Definition: test_revision.cc:17
ModelTypes<> ModelTypes
Typehelper to define data types of PredatorPrey model.
Definition: PredatorPrey.hh:72
Definition: agent.hh:11
EntityContainer< CellType > CellContainer
Type of the variably sized container for cells.
Definition: types.hh:26
The entity traits struct gathers types to be used for specializing an entity.
Definition: entity.hh:49
double p_flee
Probability to flee from a predator if on the same cell.
Definition: species.hh:87
double resource_intake
Resource intake from eating.
Definition: species.hh:33
double repro_cost
Cost of reproduction.
Definition: species.hh:43
double resource_max
Maximal resource level.
Definition: species.hh:39
double cost_of_living
Cost of living that is taken each time step.
Definition: species.hh:30
double repro_resource_requ
Minimal reproduction resources requirements.
Definition: species.hh:36
double repro_prob
Reproduction probability.
Definition: species.hh:46
The parameter of all species.
Definition: species.hh:106
PredatorParams predator
Predator parameters.
Definition: species.hh:111
PreyParams prey
Prey parameters.
Definition: species.hh:108
Struct that holds all species states.
Definition: species.hh:11
double resources
The internal resources reservoir.
Definition: species.hh:16
bool on_cell
Whether the species is on the cell.
Definition: species.hh:13
Cell State struct.
Definition: PredatorPrey.hh:22
State(const DataIO::Config &cfg, const std::shared_ptr< RNGType > &rng)
Construct a cell state with the use of a RNG.
Definition: PredatorPrey.hh:34
bool moved_predator
Flag to indicate if the predator on this cell has already moved
Definition: PredatorPrey.hh:30
SpeciesState prey
The state a prey on this cell has.
Definition: PredatorPrey.hh:27
SpeciesState predator
The state a predator on this cell has.
Definition: PredatorPrey.hh:24