Utopia  2
Framework for studying models of complex & adaptive systems.
select.hh
Go to the documentation of this file.
1 #ifndef UTOPIA_CORE_SELECT_HH
2 #define UTOPIA_CORE_SELECT_HH
3 
4 #include <map>
5 #include <set>
6 #include <unordered_set>
7 #include <random>
8 #include <algorithm>
9 #include <type_traits>
10 
11 #include <armadillo>
12 #include <yaml-cpp/yaml.h>
13 #include <spdlog/spdlog.h> // for fmt::
14 
15 #include "types.hh"
16 #include "exceptions.hh"
17 #include "entity.hh"
18 #include "cell.hh"
19 #include "agent.hh"
20 #include "logging.hh"
21 #include "../data_io/cfg_utils.hh"
22 
63 namespace Utopia {
64 
65 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
66 
68 
72 enum class SelectionMode {
73  // .. Working on entities . . . . . . . . . . . . . . . . . . . . . . . . .
75  condition = 0,
76 
78  sample = 1,
79 
81  probability = 2,
82 
83  // .. Clustering . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
84  // (Offset by 20 to accomodate different algorithms)
86 
93  clustered_simple = 20,
94 
95  // .. Only relevant for CellManager . . . . . . . . . . . . . . . . . . . .
96  // (Offset by 100)
98  position = 100,
99 
101  boundary = 101,
102 
104  lanes = 102
105 
106  // .. Only relevant for AgentManager . . . . . . . . . . . . . . . . . . .
107  // (Offset by 200)
108 
109  // .. Only relevant for GraphManager . . . . . . . . . . . . . . . . . . .
110  // (Offset by 300)
111 };
112 // NOTE When adding new enum members, take care to update the select_key_map!
113 
114 
116 const std::map<std::string, SelectionMode> selection_mode_map {
117  // General
118  {"condition", SelectionMode::condition},
119  {"sample", SelectionMode::sample},
120  {"probability", SelectionMode::probability},
121 
122  // CellManager
123  {"position", SelectionMode::position},
124  {"boundary", SelectionMode::boundary},
125  {"lanes", SelectionMode::lanes},
126 
127  // Clustered
128  {"clustered_simple", SelectionMode::clustered_simple}
129 };
130 
132 
135 std::string selection_mode_to_string(const SelectionMode& mode) {
136  for (const auto& m : selection_mode_map) {
137  if (m.second == mode) {
138  return m.first;
139  }
140  }
141  // Entry is missing; this should not happen, as the map is meant to
142  // include all possible enum values. Inform about it ...
143  throw std::invalid_argument("The given entity selection mode is not "
144  "available! Are all SelectionMode values represented in the map?");
145 };
146 
147 
149 
151 template<class M>
153  static constexpr bool value =
154  std::is_same_v<Cell<typename M::Entity::Traits>,
155  typename M::Entity>;
156 };
157 
159 
162 template<class M>
164  static constexpr bool value =
165  std::is_same_v<Agent<typename M::Entity::Traits,
166  typename M::Space>,
167  typename M::Entity>;
168 };
169 
170 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
171 // ++ Convenience Wrappers ++++++++++++++++++++++++++++++++++++++++++++++++++++
172 
173 
175 
197 template<
198  class Manager,
200 Container select_entities(const Manager& mngr, const DataIO::Config& sel_cfg)
201 {
202  // Determine the selection mode
203  if (not sel_cfg["mode"]) {
204  throw KeyError("mode", sel_cfg, "Could not select entities!");
205  }
206  const auto mode_str = get_as<std::string>("mode", sel_cfg);
207 
208  if (not selection_mode_map.count(mode_str)) {
209  throw std::invalid_argument("The given selection mode string ('"
210  + mode_str +"') is invalid! For available modes, consult the "
211  "EntitySelection group in the doxygen documentation.");
212  }
213  const SelectionMode mode = selection_mode_map.at(mode_str);
214 
215  mngr.log()->debug("Selecting entities using mode '{}' ...", mode_str);
216  mngr.log()->debug("Parameters:\n{}", DataIO::to_string(sel_cfg));
217 
218  // Depending on the mode, extract the required parameters and invoke
219  // the mode-specific methods directly
220  // .. Generally available .................................................
221  if (mode == SelectionMode::sample) {
222  const auto N = get_as<int>("num_cells", sel_cfg); // FIXME not general!
223  return select_entities<SelectionMode::sample>(mngr, N);
224  }
225 
226  if (mode == SelectionMode::probability) {
227  const auto p = get_as<double>("probability", sel_cfg);
228  return select_entities<SelectionMode::probability>(mngr, p);
229  }
230 
231  // .. Only for CellManager ................................................
232  if constexpr (is_cell_manager<Manager>::value) {
233  if (mode == SelectionMode::position) {
234  // Populate a vector with SpaceVec objects
235  std::vector<typename Manager::SpaceVec> positions{};
236 
237  if (not sel_cfg["positions"]) {
238  throw KeyError("positions", sel_cfg,
239  "Could not select cells by positions!");
240  }
241 
242  for (const auto& pos_node : sel_cfg["positions"]) {
243  using SpaceVec = typename Manager::SpaceVec;
244  using ET = typename Manager::SpaceVec::elem_type;
245 
246  const auto vec = pos_node.as<std::array<ET, Manager::dim>>();
247 
248  // Construct the SpaceVec from the array
249  // NOTE Can be outsourced by making get_as directly accept
250  // nodes instead of (key, parent node) pairs.
251  SpaceVec pos;
252  for (DimType i = 0; i < Manager::dim; i++) {
253  pos[i] = vec[i];
254  }
255  positions.push_back(pos);
256  }
257 
258  return select_entities<SelectionMode::position>(mngr, positions);
259  }
260 
261  if (mode == SelectionMode::boundary) {
262  const auto b = get_as<std::string>("boundary", sel_cfg);
263  return select_entities<SelectionMode::boundary>(mngr, b);
264  }
265 
266  if (mode == SelectionMode::lanes) {
267  const auto num_v = get_as<unsigned>("num_vertical", sel_cfg);
268  const auto num_h = get_as<unsigned>("num_horizontal", sel_cfg);
269 
270  // Handle optional arguments
271  // Permeability
272  auto perm = std::pair<double, double>{0., 0.};
273  auto perm_cfg = sel_cfg["permeability"];
274  if (perm_cfg and perm_cfg.IsSequence()) {
275  perm = perm_cfg.as<std::pair<double, double>>();
276  }
277  else if (perm_cfg and perm_cfg.IsMap()) {
278  perm.first = get_as<double>("horizontal", perm_cfg);
279  perm.second = get_as<double>("vertical", perm_cfg);
280  }
281  else if (perm_cfg and perm_cfg.IsScalar()) {
282  perm.first = perm_cfg.as<double>();
283  perm.second = perm_cfg.as<double>();
284  }
285 
286  // Gate width
287  auto gate_width = std::pair<unsigned, unsigned>{0, 0};
288  auto gate_cfg = sel_cfg["gate_width"];
289  if (gate_cfg and gate_cfg.IsSequence()) {
290  gate_width = gate_cfg.as<std::pair<unsigned, unsigned>>();
291  }
292  else if (gate_cfg and gate_cfg.IsMap()) {
293  gate_width.first = get_as<unsigned>("horizontal", gate_cfg);
294  gate_width.second = get_as<unsigned>("vertical", gate_cfg);
295  }
296  else if (gate_cfg and gate_cfg.IsScalar()) {
297  gate_width.first = gate_cfg.as<unsigned>();
298  gate_width.second = gate_cfg.as<unsigned>();
299  }
300 
301  return select_entities<SelectionMode::lanes>(mngr, num_v, num_h,
302  perm, gate_width);
303  }
304 
305  if (mode == SelectionMode::clustered_simple) {
306  const auto p_seed = get_as<double>("p_seed", sel_cfg);
307  const auto p_attach = get_as<double>("p_attach", sel_cfg);
308  const auto num_passes = get_as<unsigned>("num_passes", sel_cfg);
309 
310  // TODO Also make the neighborhood mode configurable here
311 
312  return
313  select_entities<SelectionMode::clustered_simple>(mngr,
314  p_seed,
315  p_attach,
316  num_passes);
317  }
318  }
319 
320  // .. Only for AgentManager ...............................................
321  if constexpr (is_agent_manager<Manager>::value) {
322  // ...
323  }
324 
325  // If this point is reached, we did not return, i.e.: no type was available
326  throw std::invalid_argument("The selection mode '"
327  + selection_mode_to_string(mode) + "' is not available for the given "
328  "manager type or via the configuration!");
329 }
330 
331 
332 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
333 // ++ General selection functions +++++++++++++++++++++++++++++++++++++++++++++
334 
336 
341 template<
342  SelectionMode mode,
343  class Manager,
344  class Container = EntityContainer<typename Manager::Entity>,
345  class Condition = std::function<bool(const std::shared_ptr<typename Manager::Entity>&)>,
346  typename std::enable_if_t<mode == SelectionMode::condition, int> = 0
347  >
348 Container select_entities(const Manager& mngr,
349  const Condition& condition)
350 {
351  Container selected{};
352  std::copy_if(mngr.entities().begin(), mngr.entities().end(),
353  std::back_inserter(selected),
354  condition);
355  return selected;
356 }
357 
359 
368 template<
369  SelectionMode mode,
370  class Manager,
371  class Container = EntityContainer<typename Manager::Entity>,
372  typename std::enable_if_t<mode == SelectionMode::sample, int> = 0
373  >
374 Container select_entities(const Manager& mngr,
375  const int num_entities)
376 {
377  if ( num_entities < 0
378  or static_cast<std::size_t>(num_entities) > mngr.entities().size())
379  {
380  throw std::invalid_argument("Argument num_entities need be in the "
381  "interval [0, entity container size]!");
382  }
383 
384  Container selected{};
385  selected.reserve(num_entities);
386 
387  // Populate it with the sampled cells, then return
388  std::sample(mngr.entities().begin(), mngr.entities().end(),
389  std::back_inserter(selected),
390  static_cast<std::size_t>(num_entities), *mngr.rng());
391  return selected;
392 }
393 
395 
404 template<
405  SelectionMode mode,
406  class Manager,
407  class Container = EntityContainer<typename Manager::Entity>,
408  typename std::enable_if_t<mode == SelectionMode::probability, int> = 0
409  >
410 Container select_entities(const Manager& mngr,
411  const double probability)
412 {
413  // Before drawing a huge amount of random numbers, check obvious cases
414  if (probability == 0.) {
415  return {};
416  }
417  else if (probability == 1.) {
418  return mngr.entities();
419  }
420  else if (probability < 0. or probability > 1.) {
421  throw std::invalid_argument("Entity selection in mode 'probability' "
422  "failed due to probability argument outside of interval [0., 1.]");
423  }
424 
425  // Build the distribution, selection condition lambda, and select ...
426  std::uniform_real_distribution<> dist(0., 1.);
427 
428  return
429  select_entities<SelectionMode::condition>(
430  mngr,
431  // Declare a mutable lambda (needed for dist)
432  [&, dist{std::move(dist)}](const auto&) mutable {
433  return (dist(*mngr.rng()) < probability);
434  }
435  );
436 }
437 
438 // ++ Cell-based selection functions ++++++++++++++++++++++++++++++++++++++++++
439 
441 
450 template<
451  SelectionMode mode,
452  class Manager,
453  class Container = EntityContainer<typename Manager::Entity>,
454  typename std::enable_if_t<mode == SelectionMode::position, int> = 0
455  >
456 Container select_entities(const Manager& mngr,
457  const std::vector<typename Manager::SpaceVec>& positions)
458 {
459  Container selected{};
460  for (const auto& pos : positions) {
461  selected.push_back(mngr.cell_at(pos));
462  }
463  return selected;
464 }
465 
467 
473 template<
474  SelectionMode mode,
475  class Manager,
476  class Container = EntityContainer<typename Manager::Entity>,
477  typename std::enable_if_t<mode == SelectionMode::boundary, int> = 0
478  >
479 Container select_entities(const Manager& mngr,
480  const std::string& boundary)
481 {
482  return mngr.boundary_cells(boundary);
483 }
484 
485 
487 
521 template<
522  SelectionMode mode,
523  class Manager,
524  class Container = EntityContainer<typename Manager::Entity>,
525  typename std::enable_if_t<mode == SelectionMode::lanes, int> = 0
526  >
527 Container select_entities(
528  const Manager& mngr,
529  const unsigned int num_vertical,
530  const unsigned int num_horizontal,
531  const std::pair<double, double> permeability = {0., 0.},
532  const std::pair<unsigned int, unsigned int> gate_width = {0, 0}
533 )
534 {
535  static_assert(Manager::Space::dim == 2, "Only 2D space is supported.");
536  using SpaceVec = typename Manager::SpaceVec;
537  using MultiIndex = typename Manager::MultiIndex;
538 
539  // Get the shared pointers to the grid and some further information
540  const auto grid = mngr.grid();
541  const MultiIndex shape = grid->shape();
542  const auto num_cells = grid->num_cells();
543  const SpaceVec extent = grid->space()->extent;
544  const auto eff_resolution = grid->effective_resolution();
545 
546  // The number of lanes should not exceed the number of cells
547  if (num_vertical >= shape[0] or num_horizontal >= shape[1]) {
548  throw std::invalid_argument("Given number of vertical and/or "
549  "horizontal lanes is equal or larger to the number of cells along "
550  "that dimension! Choose a smaller value.");
551  }
552 
553  // Check permeability
554  if (permeability.first < 0. or permeability.first > 1.) {
555  throw std::invalid_argument(fmt::format(
556  "Permeability in horizontal lanes needs to be in interval "
557  "[0., 1.], but was: {}", permeability.first)
558  );
559  }
560  if (permeability.second < 0. or permeability.second > 1.) {
561  throw std::invalid_argument(fmt::format(
562  "Permeability in vertical lanes needs to be in interval "
563  "[0., 1.], but was: {}", permeability.second)
564  );
565  }
566 
567  // Emit information
568  mngr.log()->debug(
569  "Selecting cells for lanes ...\n"
570  " num: {} horizontal, \t{} vertical\n"
571  " permeability: {} horizontal, \t{} vertical\n"
572  " gate width: {} horizontal, \t{} vertical\n",
573  num_horizontal, num_vertical,
574  permeability.first, permeability.second,
575  gate_width.first, gate_width.second
576  );
577 
578  // .. Lanes ...............................................................
579  // Define the required variables for vertical and horizontal lanes. It is
580  // important to work on absolute positions such that rounding errors are
581  // not propagated along the grid ...
582  // Do all this vector based to be able to use armadillo
583  const auto num_lanes = MultiIndex{num_vertical, num_horizontal};
584  SpaceVec lane_start, lane_step;
585 
586  if (grid->is_periodic()) {
587  lane_start.fill(0.);
588  lane_step = extent / num_lanes;
589  }
590  else {
591  lane_start = extent / (num_lanes + 1);
592  lane_step = lane_start;
593  }
594 
595  // Determine x- and y-indices for all the lanes that can be reached with
596  // these positions. To avoid rounding errors, use the absolute position to
597  // find the first cells of each lane: construct a proxy position and then
598  // ask the grid what the corresponding multi index is. The respective
599  // component can then be used to select the lanes.
600  // Using sets to have faster lookups
601  std::set<IndexType> indices_x, indices_y;
602 
603  for (unsigned int i = 0; i < num_vertical; i++) {
604  const SpaceVec proxy_pos = {lane_start[0] + i * lane_step[0], 0.};
605  indices_x.insert(grid->midx_of(grid->cell_at(proxy_pos))[0]);
606  }
607 
608  for (unsigned int i = 0; i < num_horizontal; i++) {
609  const SpaceVec proxy_pos = {0., lane_start[1] + i * lane_step[1]};
610  indices_y.insert(grid->midx_of(grid->cell_at(proxy_pos))[1]);
611  }
612 
613  // .. Gates in lanes ......................................................
614  // Gates are centered between the lanes
615  auto num_gates = num_lanes;
616  SpaceVec gate_start, gate_step;
617  const SpaceVec grid_step = 1./eff_resolution;
618 
619  if (grid->is_periodic()) {
620  gate_step = extent / num_gates;
621  }
622  else {
623  num_gates = num_gates + 1;
624  gate_step = extent / num_gates;
625  }
626 
627  // center of gate at gate_step / 2.
628  // but, want lower edge of the gate and iterate from there
629  // hence, distinguish pair and impair gates width
630  if (gate_width.first % 2 == 0) {
631  gate_start = gate_step / 2. - SpaceVec(
632  {grid_step[0] * (gate_width.first - 1) / 2.,
633  grid_step[1] * (gate_width.second - 1) / 2.});
634  }
635  else {
636  gate_start = gate_step / 2. - SpaceVec(
637  {grid_step[0] * (gate_width.first) / 2.,
638  grid_step[1] * (gate_width.second) / 2.});
639  }
640 
641  // Determine x- and y-indices for every gate
642  // Need error handling here because with a large gate width in non-periodic
643  // space, the grid->cell_at lookup might throw std::invalid_argument
644  std::set<IndexType> gates_indices_x, gates_indices_y;
645  SpaceVec proxy_pos;
646 
647  try {
648  for (unsigned int i = 0; i < num_gates[0]; i++) {
649  for (unsigned int j = 0; j < gate_width.first; j++) {
650  proxy_pos = {gate_start[0] + i*gate_step[0] + j*grid_step[0],
651  0.};
652  gates_indices_x.insert(
653  grid->midx_of(grid->cell_at(proxy_pos))[0]
654  );
655  }
656  }
657  for (unsigned int i = 0; i < num_gates[1]; i++) {
658  for (unsigned int j = 0; j < gate_width.second; j++) {
659  proxy_pos = {0.,
660  gate_start[1] + i*gate_step[1] + j*grid_step[1]};
661  gates_indices_y.insert(
662  grid->midx_of(grid->cell_at(proxy_pos))[1]
663  );
664  }
665  }
666  }
667  catch (std::invalid_argument& exc) {
668  throw std::invalid_argument(fmt::format(
669  "Failed to determine gate cells for lane selection, presumably "
670  "because the gate width was chosen larger than the compartment "
671  "size. Check that the gate width (h: {:d}, v: {:d}) fits into the "
672  "compartment. Grid shape: ({:d} x {:d}, {}). "
673  "Number of lanes: (h: {:d}, v: {:d}).",
674  gate_width.first, gate_width.second,
675  shape[0], shape[1],
676  grid->is_periodic() ? "periodic" : "non-periodic",
677  num_horizontal, num_vertical
678  ));
679  }
680 
681  // .. ID selection ........................................................
682  // Now need a container for selected cell IDs
683  std::vector<IndexType> selected_ids{};
684 
685  // Build the distribution, selection condition lambda, and select ...
686  std::uniform_real_distribution<> dist(0., 1.);
687 
688  // Populate it by iterating over all grid cell IDs, determining their
689  // multi index, and then checking it against the containers of cells.
690  // NOTE There is hardly a way around std::set::find if one wants to
691  // ascertain that the lanes are distributed evenly on the grid, which
692  // requires to determine the desired multi index components explicitly
693  // rather than calculating them via modulo operations (which adds
694  // rounding errors that are propagated over the grid). Still, the
695  // std::set::find method is rather efficient (logarithmic complexity)
696  // as it operates on a tree and the indices can be easily sorted.
697  for (IndexType cell_id = 0; cell_id < num_cells; cell_id++) {
698  const auto midx = grid->midx_of(cell_id);
699 
700  // Check whether this cell belongs to a horizontal lane and
701  // if so, add its index to the container of indices.
702  if (indices_y.find(midx[1]) != indices_y.end()) {
703  // skip because this cell is a gate
704  if (gates_indices_x.find(midx[0]) != gates_indices_x.end()) {
705  continue;
706  }
707  // skip because of permeability
708  else if (permeability.first > 0. and
709  dist(*mngr.rng()) < permeability.first)
710  {
711  continue;
712  }
713 
714  // else: not skipped. Add the cell ID
715  selected_ids.push_back(cell_id);
716  }
717  // Do the same for cells belonging to vertical lanes
718  else if (indices_x.find(midx[0]) != indices_x.end()) {
719  // skip because this cell is a gate
720  if (gates_indices_y.find(midx[1]) != gates_indices_y.end()) {
721  continue;
722  }
723  // skip because of permeability
724  else if (permeability.second > 0. and
725  dist(*mngr.rng()) < permeability.second)
726  {
727  continue;
728  }
729 
730  // else: not skipped. Add the cell ID
731  selected_ids.push_back(cell_id);
732  }
733  }
734  // Alternative implementation: For each of the entries in the indices_*
735  // containers, construct a full multi index and then compute the cell ID
736  // from it. However, the Grid does not currently supply a method to
737  // calculate the ID from a given multi index.
738 
739  mngr.log()->debug("Selected {:d} / {:d} cells using mode 'lanes'.",
740  selected_ids.size(), mngr.cells().size());
741 
742  // Return as container of shared pointers to cell objects
743  return mngr.entity_pointers_from_ids(selected_ids);
744 }
745 
746 
748 
761 template<
762  SelectionMode mode,
763  class Manager,
764  class Container = EntityContainer<typename Manager::Entity>,
765  typename std::enable_if_t<mode == SelectionMode::clustered_simple, int> = 0
766  >
767 Container select_entities(const Manager& mngr,
768  const double p_seed,
769  const double p_attach,
770  const unsigned int num_passes)
771 {
772  if (p_attach < 0. or p_attach > 1.) {
773  throw std::invalid_argument("Argument p_attach needs to be a "
774  "probability, i.e. be in interval [0., 1.]!");
775  }
776 
777  mngr.log()->debug("Selecting cell clusters ... (p_seed: {}, p_attach: {}, "
778  "num_passes: {})", p_seed, p_attach, num_passes);
779 
780  // Get an initial selection of clustering "seeds"
781  const auto seeds = select_entities<SelectionMode::probability>(mngr,
782  p_seed);
783 
784  mngr.log()->debug("Selected {} clustering seeds.", seeds.size());
785 
786  // Need to work on a set and also need a temporary container for those
787  // cells that are to be added after each pass.
788  // To make this more efficient, work on cell IDs instead of shared_ptr.
789  std::unordered_set<IndexType> selected_ids{};
790  std::unordered_set<IndexType> ids_to_attach{};
791 
792  // Populate the set of selected IDs
793  for (const auto& cell : seeds) {
794  selected_ids.insert(cell->id());
795  }
796 
797  // For probabilities, need a distribution object
798  std::uniform_real_distribution<> dist(0., 1.);
799 
800  // Do multiple passes ...
801  for (unsigned int i = 0; i < num_passes; i++) {
802  // ... in which all already selected cells are iterated over and
803  // each cell's neighbours are added with the given attachment
804  // probability
805 
806  // Determine which cell IDs to attach
807  ids_to_attach.clear();
808  for (const auto cell_id : selected_ids) {
809  for (const auto nb_id : mngr.grid()->neighbors_of(cell_id)) {
810  if (dist(*mngr.rng()) < p_attach) {
811  ids_to_attach.insert(nb_id);
812  }
813  }
814  }
815 
816  // Add them to the set of selected cells
817  selected_ids.insert(ids_to_attach.begin(), ids_to_attach.end());
818 
819  mngr.log()->debug("Finished pass {}. Have {} cells selected now.",
820  i + 1, selected_ids.size());
821  }
822 
823  // Convert into container of pointers to cells and return
824  return
825  mngr.entity_pointers_from_ids(
826  std::vector<IndexType>(selected_ids.begin(), selected_ids.end())
827  );
828 }
829 
830 
831 // ++ Agent-based selection functions +++++++++++++++++++++++++++++++++++++++++
832 // ... nothing yet.
833 
834 // end group EntitySelection
839 } // namespace Utopia
840 
841 #endif // UTOPIA_CORE_SELECT_HH
An agent is a slightly specialized state container.
Definition: agent.hh:47
For access to a dict-like structure with a bad key.
Definition: exceptions.hh:67
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::string selection_mode_to_string(const SelectionMode &mode)
Given a SelectionMode enum value, return the corresponding string key.
Definition: select.hh:135
SelectionMode
Possible selection modes; availability depends on choice of manager.
Definition: select.hh:72
const std::map< std::string, SelectionMode > selection_mode_map
A map from strings to Select enum values.
Definition: select.hh:116
Container select_entities(const Manager &mngr, const DataIO::Config &sel_cfg)
Select entities according to parameters specified in a configuration.
Definition: select.hh:200
@ lanes
(For CellManager only) Selects horizontal or vertical lanes of cells
@ condition
Select if a condition is fulfilled.
@ position
(For CellManager only) Selects cells at given positions in space
@ sample
Select a random sample of entities with a known sample size.
@ probability
Select an entity with a given probability.
@ boundary
(For CellManager only) Select the boundary cells of a grid
@ clustered_simple
Select entity clusters using a simple neighborhood-based algorithm.
Definition: agent.hh:11
std::vector< std::shared_ptr< EntityType > > EntityContainer
Type of the variably sized container for entities.
Definition: types.hh:22
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
Metafunction to determine whether a Manager is an AgentManager.
Definition: select.hh:163
static constexpr bool value
Definition: select.hh:164
Metafunction to determine whether a Manager is a CellManager.
Definition: select.hh:152
static constexpr bool value
Definition: select.hh:153