Utopia  2
Framework for studying models of complex & adaptive systems.
SimpleEG.hh
Go to the documentation of this file.
1 #ifndef UTOPIA_MODELS_SIMPLEEG_HH
2 #define UTOPIA_MODELS_SIMPLEEG_HH
3 
4 #include <functional>
5 
6 #include <utopia/core/types.hh>
7 #include <utopia/core/model.hh>
9 #include <utopia/core/apply.hh>
10 
11 
12 namespace Utopia {
13 namespace Models {
14 namespace SimpleEG {
15 
16 // ++ Type definitions ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
17 
19 enum Strategy : unsigned short int { S0=0, S1=1 };
20 
22 struct CellState {
25 
27  double payoff;
28 
31  :
32  strategy(S0),
33  payoff(0.)
34  {}
35 };
36 
37 
39 
48 
49 
50 
53 
54 
55 
56 // ++ Model definition ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
57 
59 
70 class SimpleEG:
71  public Model<SimpleEG, ModelTypes>
72 {
73 public:
76 
78  using Config = typename Base::Config;
79 
81  using DataSet = typename Base::DataSet;
82 
85 
87 
95  using RuleFunc = typename CellManager::RuleFunc;
96 
98  using RNG = typename Base::RNG;
99 
101  using IAMatrixType = typename std::array<std::array<double, 2>, 2>;
102 
103 
104 private:
105  // Base members: _time, _name, _cfg, _hdfgrp, _rng, _log, _monitor, _space
106  // ... but you should definitely check out the documentation ;)
107 
108  // -- Members -------------------------------------------------------------
111 
114 
115 
116  // .. Temporary objects ...................................................
119 
121  std::uniform_real_distribution<double> _prob_distr;
122 
123 
124  // .. Datasets ............................................................
126  std::shared_ptr<DataSet> _dset_strategy;
127 
129  std::shared_ptr<DataSet> _dset_payoff;
130 
131 
132 public:
133  // -- Public interface ----------------------------------------------------
135 
143  template<class ParentModel>
145  const std::string& name,
146  ParentModel& parent_model,
147  const DataIO::Config& custom_cfg = {}
148  )
149  :
150  // Initialize first via base model
151  Base(name, parent_model, custom_cfg),
152 
153  // Now initialize the cell manager
154  _cm(*this),
155 
156  // Initialize model parameters
157  _ia_matrix(this->extract_ia_matrix()),
158 
159  // ... and helpers objects
161  _prob_distr(0., 1.),
162 
163  // And open datasets for strategy and payoff
164  _dset_strategy(this->create_cm_dset("strategy", _cm)),
165  _dset_payoff(this->create_cm_dset("payoff", _cm))
166  {
167  // Initialize cells
168  this->initialize_cells();
169 
170  // Write calculated _ia_matrix to _hdfgrp attribute
171  this->_hdfgrp->add_attribute("ia_matrix", _ia_matrix);
172  }
173 
174 
175 private:
176  // Setup functions ........................................................
178 
184  static_assert(Base::Space::dim == 2, "Only 2D space is supported.");
185 
186  // Extract the mode that determines the initial strategy
187  auto initial_state = get_as<std::string>("initial_state", this->_cfg);
188 
189  this->_log->info("Initializing cells in '{}' mode ...", initial_state);
190 
191  // Distinguish according to the mode, which strategy to choose
192  if (initial_state == "random") {
193  // Get the threshold probability value
194  const auto s1_prob = get_as<double>("s1_prob", this->_cfg);
195 
196  // Define the update rule
197  auto set_random_strategy = [this, &s1_prob](const auto cell) {
198  auto state = cell->state();
199 
200  // Draw a random number and compare it to the threshold
201  if (this->_prob_distr(*this->_rng) < s1_prob) {
202  state.strategy = Strategy::S1;
203  }
204  else {
205  state.strategy = Strategy::S0;
206  }
207 
208  return state;
209  };
210 
211  // Apply the rule to all cells
212  apply_rule(set_random_strategy, _cm.cells());
213  }
214 
215  else if (initial_state == "fraction") {
216  // Get the value for the fraction of cells to have strategy 1
217  const auto s1_fraction = get_as<double>("s1_fraction", this->_cfg);
218 
219  if (s1_fraction > 1. or s1_fraction < 0.) {
220  throw std::invalid_argument("Need `s1_fraction` in [0, 1], "
221  "but got value: " + std::to_string(s1_fraction));
222  }
223 
224  // Calculate the number of cells that should have that strategy
225  const auto num_cells = _cm.cells().size();
226  const std::size_t num_s1 = s1_fraction * num_cells;
227  // NOTE this is a flooring calculation!
228 
229  this->_log->debug("Cells with strategy 1: {} of {}",
230  num_s1, num_cells);
231 
232  // TODO Optionally, can add some logic here to make more clever
233  // assignments, i.e. starting out with all S1 if the number
234  // to set is higher than half ...
235 
236  // Need a counter of cells that were set to S1
237  std::size_t num_set = 0;
238 
239  // Get a copy of the cells container... and shuffle them.
240  auto random_cells = _cm.cells();
241  std::shuffle(random_cells.begin(), random_cells.end(),
242  *this->_rng);
243 
244  // Make num_s1 cells use strategy 1
245  for (const auto& cell : random_cells) {
246  // If the desired number of cells using strategy 1 is not yet reached change another cell's strategy
247  if (num_set < num_s1) {
248  // Check if it already has strategy 1.
249  if (cell->state().strategy == Strategy::S1) {
250  // Already has strategy 1, don't set it again
251  continue;
252  }
253  // else: has strategy 0 -> set to S1 and increment counter
254  cell->state_new().strategy = Strategy::S1;
255  cell->update();
256 
257  num_set++;
258  }
259  // Break, if fraction of strategy 1 is reached
260  else break;
261  }
262  }
263 
264  else if (initial_state == "single_s0" or initial_state == "single_s1"){
265  // Get the grid shape and perform checks on it
266  const auto& grid_shape = _cm.grid()->shape();
267 
268  // Need to throw an error for non-odd-valued grid shape
269  if (grid_shape[0] % 2 == 0 or grid_shape[1] % 2 == 0) {
270  throw std::invalid_argument("In mode '" + initial_state + "', "
271  "odd grid extensions are required to calculate the "
272  "central cell! Either adapt your grid resolution or the "
273  "space's extent in order to have a different shape. "
274  );
275  }
276  // FIXME This and the below is rather fragile and should be
277  // improved. Better approach: calculate the central point
278  // (here!) and find the cell beneath that point, setting it
279  // to single_strategy. Then use rule application to set
280  // default_strategy.
281 
282  // Determine which strategy is the common default strategy
283  // and which one is the single strategy in the center of the grid
284  Strategy default_strategy, single_strategy;
285  if (initial_state == "single_s0") {
286  default_strategy = Strategy::S1;
287  single_strategy = Strategy::S0;
288  }
289  else {
290  default_strategy = Strategy::S0;
291  single_strategy = Strategy::S1;
292  }
293 
294  // Define the rule function
295  auto set_initial_strategy = [&](const auto& cell) {
296  // Get the state of this cell
297  auto state = cell->state();
298 
299  // Get the multi index to find out if this cell is central
300  const auto midx = _cm.midx_of(cell);
301 
302  if ( midx[0] == grid_shape[0] / 2
303  and midx[1] == grid_shape[1] / 2) {
304  // The cell _is_ in the center of the grid
305  state.strategy = single_strategy;
306  }
307  else {
308  // The cell _is not_ in the center of the grid
309  state.strategy = default_strategy;
310  }
311 
312  return state;
313  };
314 
315  // Apply the rule
316  apply_rule(set_initial_strategy, _cm.cells());
317  }
318  else {
319  throw std::invalid_argument("Invalid value '" + initial_state
320  + "' for parameter `initial_state`! Allowed values: random, "
321  "fraction, single_s0, single_s1.");
322  }
323 
324  // Done.
325  this->_log->info("Initialized all {} cells.", _cm.cells().size());
326  }
327 
328  // .. Helper functions ....................................................
329 
331 
357  // Return the ia_matrix if it is explicitly given in the config
358  if (this->_cfg["ia_matrix"]) {
359  return get_as<IAMatrixType>("ia_matrix", this->_cfg);
360  }
361  else if (this->_cfg["bc_pair"]) {
362  // If ia_matrix is not provided in the config, get the ia_matrix
363  // from the bc-pair
364  const auto [b, c] = get_as<std::pair<double, double>>("bc_pair",
365  this->_cfg);
366  const double ia_00 = b - c;
367  const double ia_01 = -c;
368  const double ia_10 = b;
369  const double ia_11 = 0.;
370 
371  const std::array<double,2> row0({{ia_00, ia_01}});
372  const std::array<double,2> row1({{ia_10, ia_11}});
373 
374  return IAMatrixType({{row0, row1}});
375  }
376  else if (this->_cfg["b"]) {
377  // If both previous cases are not provided, then return the
378  // ia_matrix given by the parameter "b"
379  const auto b = get_as<double>("b", this->_cfg);
380 
381  if (b <= 1.) {
382  throw std::invalid_argument("Parameter `b` needs to be >1, "
383  "but was: " + std::to_string(b));
384  }
385 
386  const double ia_00 = 1;
387  const double ia_01 = 0;
388  const double ia_10 = b;
389  const double ia_11 = 0.;
390 
391  const std::array<double, 2> row0({{ia_00, ia_01}});
392  const std::array<double, 2> row1({{ia_10, ia_11}});
393 
394  return IAMatrixType({{row0, row1}});;
395  }
396 
397  // If we reach this point, not enough parameters were provided
398  throw std::invalid_argument("No interaction matrix given! Check that "
399  "at least one of the following config "
400  "entries is available: `ia_matrix`, "
401  "`bc_pair`, `b`");
402  }
403 
404 
405  // .. Rule Functions ......................................................
406  // Rule functions that can be applied to the CellManager's cells
407 
409 
412  RuleFunc _interaction = [this](const auto& cell){
413  // Get the state of the current cell and its strategy
414  auto state = cell->state();
415 
416  // First, reset payoff to zero
417  state.payoff = 0.;
418 
419  // Go through neighboring cells, look at their strategies and add
420  // the corresponding payoff only to the current cell's payoff.
421  // NOTE: Careful! Adding the corresponding payoff to the neighboring
422  // cell would lead to payoffs being added multiple times!
423  for (const auto& nb : this->_cm.neighbors_of(cell)) {
424  const auto nb_strategy = nb->state().strategy;
425 
426  // Calculate the payoff depending on this cell's and the other
427  // cell's state
428  if (state.strategy == S0 and nb_strategy == S0) {
429  state.payoff += _ia_matrix[0][0];
430  }
431  else if (state.strategy == S0 and nb_strategy == S1) {
432  state.payoff += _ia_matrix[0][1];
433  }
434  else if (state.strategy == S1 and nb_strategy == S0) {
435  state.payoff += _ia_matrix[1][0];
436  }
437  else if (state.strategy == S1 and nb_strategy == S1) {
438  state.payoff += _ia_matrix[1][1];
439  }
440  }
441 
442  // Return the updated state of this cell
443  return state;
444  };
445 
447 
458  RuleFunc _update = [this](const auto& cell){
459  // Get the state of the cell
460  auto state = cell->state();
461 
462  // Set highest payoff in the neighborhood to the cell's payoff
463  double highest_payoff = state.payoff;
464  _fittest_cells_in_nbhood.clear();
465  _fittest_cells_in_nbhood.push_back(cell);
466 
467  // Iterate over neighbors of this cell:
468  for (const auto& nb : this->_cm.neighbors_of(cell)){
469  if (nb->state().payoff > highest_payoff) {
470  // Found a new highest payoff
471  highest_payoff = nb->state().payoff;
472  _fittest_cells_in_nbhood.clear();
473  _fittest_cells_in_nbhood.push_back(nb);
474  }
475  else if (nb->state().payoff == highest_payoff) {
476  // Have a payoff equal to that of another cell
477  _fittest_cells_in_nbhood.push_back(nb);
478  }
479  // else: payoff was below highest payoff
480  }
481 
482  // Now, update the strategy according to the fittest neighbor
483  if (_fittest_cells_in_nbhood.size() == 1) {
484  // Only one fittest neighbor. -> The state of the current cell
485  // is updated with that of the fittest neighbor.
486  state.strategy = _fittest_cells_in_nbhood[0]->state().strategy;
487  }
488  else if (_fittest_cells_in_nbhood.size() > 1) {
489  // There are multiple nbs with the same (highest) payoff.
490  // -> Choose randomly one of them to pass on its strategy
491  std::uniform_int_distribution<> dist(0, _fittest_cells_in_nbhood.size()-1);
492  state.strategy = _fittest_cells_in_nbhood[dist(*this->_rng)]->state().strategy;
493  }
494  else {
495  // There is no fittest neighbor. This case should never occur!
496  throw std::runtime_error("There was no fittest neighbor in the "
497  "cell update. Should not have occurred!");
498  }
499 
500  return state;
501  };
502 
503 
504 public:
505  // -- Public Interface ----------------------------------------------------
506  // .. Simulation Control ..................................................
507 
518  void perform_step () {
519  // Apply the rules to all cells
522  }
523 
524 
526  void monitor () {
527 
528  }
529 
530 
532  void write_data () {
533  // strategy
534  _dset_strategy->write(_cm.cells().begin(), _cm.cells().end(),
535  [](const auto& cell) {
536  return static_cast<unsigned short int>(cell->state().strategy);
537  }
538  );
539 
540  // payoffs
541  _dset_payoff->write(_cm.cells().begin(), _cm.cells().end(),
542  [](const auto& cell) {
543  return cell->state().payoff;
544  }
545  );
546  }
547 
548  // .. Getters and setters .................................................
549  // Add getters and setters here to interface with other models
550 
551 };
552 
553 
554 } // namespace SimpleEG
555 } // namespace Models
556 } // namespace Utopia
557 #endif // UTOPIA_MODELS_SIMPLEEG_HH
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
MultiIndex midx_of(const Cell &cell) const
Returns the multi-index of the given cell.
Definition: cell_manager.hh:236
const std::shared_ptr< GridType > & grid() const
Return const reference to the grid.
Definition: cell_manager.hh:214
CellContainer< Cell > neighbors_of(const Cell &cell) const
Retrieve the given cell's neighbors.
Definition: cell_manager.hh:458
Base class interface for Models using the CRT Pattern.
Definition: model.hh:112
const std::shared_ptr< DataGroup > _hdfgrp
The HDF group this model instance should write its data to.
Definition: model.hh:176
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
typename ModelTypes::Config Config
Data type that holds the configuration.
Definition: model.hh:116
typename ModelTypes::RNG RNG
Data type of the shared RNG.
Definition: model.hh:128
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
Simple model of evolutionary games on grids.
Definition: SimpleEG.hh:72
typename CellManager::RuleFunc RuleFunc
Extract the type of the rule function from the CellManager.
Definition: SimpleEG.hh:95
typename Base::RNG RNG
Data type of the shared RNG.
Definition: SimpleEG.hh:98
typename Base::Config Config
Data type that holds the configuration.
Definition: SimpleEG.hh:78
RuleFunc _update
The update rule.
Definition: SimpleEG.hh:458
RuleFunc _interaction
The interaction between players.
Definition: SimpleEG.hh:412
CellManager _cm
The cell manager.
Definition: SimpleEG.hh:110
void perform_step()
Iterate a single step.
Definition: SimpleEG.hh:518
std::shared_ptr< DataSet > _dset_payoff
Stores cell payoffs over time.
Definition: SimpleEG.hh:129
std::shared_ptr< DataSet > _dset_strategy
Stores cell strategies over time.
Definition: SimpleEG.hh:126
SimpleEG(const std::string &name, ParentModel &parent_model, const DataIO::Config &custom_cfg={})
Construct the SimpleEG model.
Definition: SimpleEG.hh:144
IAMatrixType extract_ia_matrix() const
Extract the interaction matrix from the config file.
Definition: SimpleEG.hh:356
typename std::array< std::array< double, 2 >, 2 > IAMatrixType
Type of the interaction matrix.
Definition: SimpleEG.hh:101
void monitor()
Monitor model information.
Definition: SimpleEG.hh:526
void write_data()
Write data: the strategy and payoff of each cell.
Definition: SimpleEG.hh:532
void initialize_cells()
Initialize the cells according to initial_state config parameter.
Definition: SimpleEG.hh:183
Model< SimpleEG, ModelTypes > Base
The base model.
Definition: SimpleEG.hh:75
typename Base::DataSet DataSet
Data type for a dataset.
Definition: SimpleEG.hh:81
std::uniform_real_distribution< double > _prob_distr
Uniform real distribution in [0., 1.) used for evaluating probabilities.
Definition: SimpleEG.hh:121
const IAMatrixType _ia_matrix
The interaction matrix (extracted during initialization)
Definition: SimpleEG.hh:113
CellContainer< typename CellManager::Cell > _fittest_cells_in_nbhood
A container to temporarily accumulate the fittest neighbor cells in.
Definition: SimpleEG.hh:118
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
void apply_rule(Rule &&rule, const ContTarget &cont_target, ContArgs &&... cont_args)
Sequential overload.
Definition: apply.hh:133
Strategy
Strategy enum.
Definition: SimpleEG.hh:19
@ S1
Definition: SimpleEG.hh:19
@ S0
Definition: SimpleEG.hh:19
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
Wrapper struct for defining model class data types.
Definition: model.hh:92
The type of the cell state for the SimpleEG model.
Definition: SimpleEG.hh:22
CellState()
Default constructor: strategy S0 and zero payoff.
Definition: SimpleEG.hh:30
double payoff
The payoff.
Definition: SimpleEG.hh:27
Strategy strategy
The strategy.
Definition: SimpleEG.hh:24