Utopia  2
Framework for studying models of complex & adaptive systems.
env_state_func_collection.hh
Go to the documentation of this file.
1 #ifndef UTOPIA_MODELS_ENVIRONMENT_ENVSTATEFUNCCOLLECTION_HH
2 #define UTOPIA_MODELS_ENVIRONMENT_ENVSTATEFUNCCOLLECTION_HH
3 
4 #include "tools.hh"
5 
7 namespace StateFunctionCollection {
8 
11 
12 // -- Helper functions --------------------------------------------------------
13 
15 
18 template<typename EnvModel, class DistType,
19  class EnvStateFunc = typename EnvModel::EnvStateFunc>
20 EnvStateFunc
21  build_rng_env_state_func(EnvModel& model,
22  DistType&& dist,
23  const std::string& param_name,
24  const ValMode& mode)
25 {
26  // NOTE It is VITAL to move-construct the perfectly-forwarded dist into
27  // the lambda; otherwise it has to be stored outside, which is a
28  // real pita. Also, the lambda has to be declared mutable such
29  // that the captured object are allowed to be changed; again, this
30  // is only relevant for the distribution's internal state ...
31  return
32  [&model, param_name, mode, dist{std::move(dist)}]
33  (const auto& env_cell) mutable {
34  auto& env_state = env_cell->state;
35 
36  double current_value = 0.;
37  if (mode == ValMode::Add) {
38  current_value = env_state.get_env(param_name);
39  }
40  const double rn = dist(*model.get_rng());
41 
42  env_state.set_env(param_name, current_value + rn);
43  return env_state;
44  };
45 }
46 
51 // -- Environment state modification functions --------------------------------
52 // .. Keep these in alphabetical order and prefix with _esf_ ! .............
53 // NOTE The methods below do _not_ change any state, they just generate
54 // a function object that does so at the desired point in time.
55 
57 
75 template<typename EnvModel,
76  class EnvStateFunc = typename EnvModel::EnvStateFunc>
77 std::pair<EnvStateFunc, Update> esf_noise(const EnvModel& model,
78  const std::string& param_name,
79  const Config& cfg)
80 {
81  const auto mode = extract_val_mode(cfg, "noise");
82  const auto distribution = get_as<std::string>("distribution", cfg);
83 
84  // Depending on chosen distribution, construct it and build a rule
85  // function using a reference to the newly created one...
86  if (distribution == "normal") {
87  const auto mean = get_as<double>("mean", cfg);
88  const auto stddev = get_as<double>("stddev", cfg);
89  std::normal_distribution<> dist(mean, stddev);
90 
91  auto esf = build_rng_env_state_func(model, std::move(dist), param_name,
92  mode);
93  return {esf, Update::sync};
94  }
95  else if (distribution == "poisson") {
96  const auto mean = get_as<double>("mean", cfg);
97  std::poisson_distribution<> dist(mean);
98 
99  auto esf = build_rng_env_state_func(model, std::move(dist), param_name,
100  mode);
101  return {esf, Update::sync};
102  }
103  else if (distribution == "exponential") {
104  const auto lambda = get_as<double>("lambda", cfg);
105  std::exponential_distribution<> dist(lambda);
106 
107  auto esf = build_rng_env_state_func(model, std::move(dist), param_name,
108  mode);
109  return {esf, Update::sync};
110  }
111  else if (distribution == "uniform_int") {
112  auto interval = get_as<std::array<int, 2>>("interval", cfg);
113  std::uniform_int_distribution<> dist(interval[0], interval[1]);
114 
115  auto esf = build_rng_env_state_func(model, std::move(dist), param_name,
116  mode);
117  return {esf, Update::sync};
118  }
119  else if (distribution == "uniform_real" or distribution == "uniform") {
120  auto interval = get_as<std::array<double, 2>>("interval", cfg);
121  std::uniform_real_distribution<> dist(interval[0], interval[1]);
122 
123  auto esf = build_rng_env_state_func(model, std::move(dist), param_name,
124  mode);
125  return {esf, Update::sync};
126  }
127  else {
128  throw std::invalid_argument("No method implemented to resolve "
129  "noise distribution '" + distribution + "'! Valid options: "
130  "normal, poisson, uniform_int, uniform_real.");
131  }
132 };
133 
135 
149 template<typename EnvModel, typename Extent,
150  class EnvStateFunc = typename EnvModel::EnvStateFunc>
151 std::pair<EnvStateFunc, Update> esf_slope(const EnvModel&,
152  const std::string& param_name,
153  const Config& cfg,
154  const Extent& extent)
155 {
156  const auto mode = extract_val_mode(cfg, "slope");
157 
158  const auto values_north_south =
159  get_as<std::array<double, 2>>("values_north_south", cfg);
160 
161  EnvStateFunc esf =
162  [param_name, mode, values_north_south, extent]
163  (const auto& env_cell) mutable
164  {
165  auto& env_state = env_cell->state;
166 
167  // Use the relative position along y-dimension
168  const double pos = ( env_state.position[1] / extent[1]);
169  const double slope = values_north_south[0] - values_north_south[1];
170  const double value = values_north_south[1] + pos * slope;
171 
172  double current_value = 0.;
173  if (mode == ValMode::Add) {
174  current_value = env_state.get_env(param_name);
175  }
176  env_state.set_env(param_name, current_value + value);
177  return env_state;
178  };
179 
180  return {esf, Update::sync};
181 };
182 
184 
199 template<typename EnvModel,
200  class EnvStateFunc = typename EnvModel::EnvStateFunc>
201 std::pair<EnvStateFunc, Update> esf_steps(const EnvModel&,
202  const std::string& param_name,
203  const Config& cfg)
204 {
205  const auto mode = extract_val_mode(cfg, "steps");
206 
207  const auto latitudes =
208  get_as<std::vector<double>>("latitudes", cfg, {0.5});
209  const auto values_north_south =
210  get_as<std::vector<double>>("values_north_south", cfg);
211 
212  if (latitudes.size() != values_north_south.size() - 1) {
213  throw std::invalid_argument("The list of 'latitudes' and"
214  " 'values_north_south' don't match in size. Sizes were "
215  + std::to_string(latitudes.size()) + " and "
216  + std::to_string(values_north_south.size()) +
217  ". Values_north_south must have one element more that"
218  " latitudes.");
219  }
220 
221  EnvStateFunc esf =
222  [param_name, mode, latitudes, values_north_south]
223  (const auto& env_cell) mutable
224  {
225  auto& env_state = env_cell->state;
226  double value = values_north_south[0];
227  for (unsigned int i = 0; i < latitudes.size(); ++i) {
228  if (env_state.position[1] > latitudes[i]) {
229  break;
230  }
231  value = values_north_south[i+1];
232  }
233 
234  double current_value = 0.;
235  if (mode == ValMode::Add) {
236  current_value = env_state.get_env(param_name);
237  }
238  env_state.set_env(param_name, current_value + value);
239  return env_state;
240  };
241 
242  return {esf, Update::sync};
243 };
244 
246 
258 template<typename EnvModel,
259  class EnvStateFunc = typename EnvModel::EnvStateFunc>
260 std::pair<EnvStateFunc, Update> esf_uniform(const EnvModel&,
261  const std::string& param_name,
262  const Config& cfg)
263 {
264  ValMode mode;
265  double value;
266 
267  // Extract configuration depending on whether cfg is scalar or mapping
268  if (cfg.IsScalar()) {
269  // Interpret as desiring to set to the given scalar value
270  mode = ValMode::Set;
271  value = cfg.as<double>();
272  }
273  else if (cfg.IsMap()) {
274  mode = extract_val_mode(cfg, "uniform");
275  value = get_as<double>("value", cfg);
276  }
277  else {
278  throw std::invalid_argument("The configuration for environment "
279  "function 'uniform' must be a scalar or a mapping!");
280  }
281 
282  EnvStateFunc esf =
283  [param_name, mode, value]
284  (const auto& env_cell) mutable
285  {
286  auto& env_state = env_cell->state;
287 
288  double current_value = 0.;
289  if (mode == ValMode::Add) {
290  current_value = env_state.get_env(param_name);
291  }
292 
293  env_state.set_env(param_name, current_value + value);
294  return env_state;
295  };
296 
297  return {esf, Update::sync};
298 }
299 
300 // End group Environment
305 } // namespace StateFunctionCollection
306 } // namespace Utopia::Models::Environment
307 
308 #endif
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::pair< EnvStateFunc, Update > esf_uniform(const EnvModel &, const std::string &param_name, const Config &cfg)
Creates a rule function for spatially uniform parameter values.
Definition: env_state_func_collection.hh:260
std::pair< EnvStateFunc, Update > esf_steps(const EnvModel &, const std::string &param_name, const Config &cfg)
Creates a rule function for spatial steps in the parameter values.
Definition: env_state_func_collection.hh:201
ValMode extract_val_mode(const DataIO::Config &cfg, const std::string &context)
Given a configuration node, extract the value mode.
Definition: tools.hh:19
std::pair< EnvStateFunc, Update > esf_slope(const EnvModel &, const std::string &param_name, const Config &cfg, const Extent &extent)
Creates a rule function for spatially linearly parameter values.
Definition: env_state_func_collection.hh:151
ValMode
Value calculation mode.
Definition: tools.hh:10
std::pair< EnvStateFunc, Update > esf_noise(const EnvModel &model, const std::string &param_name, const Config &cfg)
Creates a rule function for noisy parameter values.
Definition: env_state_func_collection.hh:77
@ Set
Set a value, discarding the current state.
@ Add
Add the new value to the existing value.
@ sync
Synchronous update.
EnvStateFunc build_rng_env_state_func(EnvModel &model, DistType &&dist, const std::string &param_name, const ValMode &mode)
Create a rule function that uses a random number distribution.
Definition: env_state_func_collection.hh:21
DataIO::Config Config
Configuration node type alias.
Definition: env_state_func_collection.hh:10
Definition: env_param_func_collection.hh:8
unsigned int DistType
Type for distancens, i.e. intermediately long unsigned integers.
Definition: types.hh:37