graph_simulation.h Source File

CPP API: graph_simulation.h Source File
graph_simulation.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 MEmilio
3 *
4 * Authors: Daniel Abele, Henrik Zunker
5 *
6 * Contact: Martin J. Kuehn <Martin.Kuehn@DLR.de>
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20 #ifndef MIO_MOBILITY_GRAPH_SIMULATION_H
21 #define MIO_MOBILITY_GRAPH_SIMULATION_H
22 
23 #include "memilio/mobility/graph.h"
27 
28 namespace mio
29 {
30 
34 template <class GraphT, class Timepoint, class Timespan, class edge_f, class node_f>
36 {
37 public:
38  using Graph = GraphT;
39 
40  using node_function = node_f;
41  using edge_function = edge_f;
42 
43  GraphSimulationBase(Timepoint t0, Timespan dt, const Graph& g, const node_function& node_func,
44  const edge_function&& edge_func)
45  : m_t(t0)
46  , m_dt(dt)
47  , m_graph(g)
48  , m_node_func(node_func)
49  , m_edge_func(edge_func)
50  {
51  }
52 
53  GraphSimulationBase(Timepoint t0, Timespan dt, Graph&& g, const node_function& node_func,
54  const edge_function&& edge_func)
55  : m_t(t0)
56  , m_dt(dt)
57  , m_graph(std::move(g))
58  , m_node_func(node_func)
59  , m_edge_func(edge_func)
60  {
61  }
62 
63  Timepoint get_t() const
64  {
65  return m_t;
66  }
67 
69  {
70  return m_graph;
71  }
72 
73  const Graph& get_graph() const&
74  {
75  return m_graph;
76  }
77 
79  {
80  return std::move(m_graph);
81  }
82 
83 protected:
84  Timepoint m_t;
85  Timespan m_dt;
89 };
90 
91 template <typename FP, class Graph, class Timepoint, class Timespan,
92  class edge_f = void (*)(Timepoint, Timespan, typename Graph::EdgeProperty&, typename Graph::NodeProperty&,
93  typename Graph::NodeProperty&),
94  class node_f = void (*)(Timepoint, Timespan, typename Graph::NodeProperty&)>
96 {
98  using Base::Base;
99 
100 public:
101  void advance(Timepoint t_max = 1.0)
102  {
103  auto dt = Base::m_dt;
104  while (Base::m_t < t_max) {
105  if (Base::m_t + dt > t_max) {
106  dt = t_max - Base::m_t;
107  }
108 
109  for (auto& n : Base::m_graph.nodes()) {
110  Base::m_node_func(Base::m_t, dt, n.property);
111  }
112 
113  Base::m_t += dt;
114 
115  for (auto& e : Base::m_graph.edges()) {
116  Base::m_edge_func(Base::m_t, dt, e.property, Base::m_graph.nodes()[e.start_node_idx].property,
117  Base::m_graph.nodes()[e.end_node_idx].property);
118  }
119  }
120  }
121 };
122 
123 template <typename FP, class Graph>
125  : public GraphSimulationBase<Graph, FP, FP,
126  std::function<void(typename Graph::EdgeProperty&, size_t,
127  typename Graph::NodeProperty&, typename Graph::NodeProperty&)>,
128  std::function<void(FP, FP, typename Graph::NodeProperty&)>>
129 {
130  using Base = GraphSimulationBase<Graph, FP, FP,
131  std::function<void(typename Graph::EdgeProperty&, size_t,
132  typename Graph::NodeProperty&, typename Graph::NodeProperty&)>,
133  std::function<void(FP, FP, typename Graph::NodeProperty&)>>;
134 
137 
138 public:
139  GraphSimulationStochastic(FP t0, FP dt, const Graph& g, const node_function& node_func,
140  const edge_function&& edge_func)
141  : Base(t0, dt, g, node_func, std::move(edge_func))
142  , m_rates(Base::m_graph.edges().size() *
143  Base::m_graph.edges()[0].property.get_parameters().get_coefficients().get_shape().rows())
144  {
145  }
146 
147  GraphSimulationStochastic(FP t0, FP dt, Graph&& g, const node_function& node_func, const edge_function&& edge_func)
148  : Base(t0, dt, std::move(g), node_func, std::move(edge_func))
149  , m_rates(Base::m_graph.edges().size() *
150  Base::m_graph.edges()[0].property.get_parameters().get_coefficients().get_shape().rows())
151  {
152  }
153 
154  void advance(FP t_max)
155  {
156  //draw normalized waiting time
157  ScalarType normalized_waiting_time = ExponentialDistribution<ScalarType>::get_instance()(m_rng, 1.0);
158  std::vector<ScalarType> dt_cand(Base::m_graph.nodes().size());
159  ScalarType cumulative_rate = 0; //cumulative transition rate
160  size_t parameters_per_edge =
161  size_t(Base::m_graph.edges()[0].property.get_parameters().get_coefficients().get_shape().rows());
162  std::vector<ScalarType> transition_rates(parameters_per_edge * Base::m_graph.edges().size());
163  while (Base::m_t < t_max) {
164  Base::m_dt = std::min({Base::m_dt, t_max - Base::m_t});
165  //calculate current transition rates and cumulative rate
166  cumulative_rate = get_cumulative_transition_rate();
167  if (cumulative_rate * Base::m_dt >
168  normalized_waiting_time) { //at least one transition event during current time step
169  do {
170  //evaluate rates
171  get_rates(m_rates);
172  //draw transition event
173  size_t event = mio::DiscreteDistribution<size_t>::get_instance()(m_rng, m_rates);
174  //edge that performs transition event
175  auto& event_edge = Base::m_graph.edges()[event / parameters_per_edge];
176  //index for compartment and age group moving
177  auto flat_index = event % parameters_per_edge;
178 
179  //advance nodes until t + (waiting_time / cumulative_rate)
180  for (size_t node_iter = 0; node_iter < Base::m_graph.nodes().size(); ++node_iter) {
181  auto& node = Base::m_graph.nodes()[node_iter];
182  Base::m_node_func(Base::m_t, normalized_waiting_time / cumulative_rate, node.property);
183  }
184 
185  //advance time
186  Base::m_t += normalized_waiting_time / cumulative_rate;
187 
188  //reduce remaining time of current time step
189  Base::m_dt -= normalized_waiting_time / cumulative_rate;
190 
191  //perform transition
192  Base::m_edge_func(event_edge.property, flat_index,
193  Base::m_graph.nodes()[event_edge.start_node_idx].property,
194  Base::m_graph.nodes()[event_edge.end_node_idx].property);
195 
196  //calculate new cumulative rate
197  cumulative_rate = get_cumulative_transition_rate();
198 
199  //draw new normalized waiting time
200  normalized_waiting_time = ExponentialDistribution<ScalarType>::get_instance()(m_rng, 1.0);
201 
202  } while (cumulative_rate * Base::m_dt > normalized_waiting_time);
203  }
204  else { //no transition event in current time step
205  normalized_waiting_time -= cumulative_rate * Base::m_dt; //reduce waiting time by current time step
206  }
207 
208  //advance nodes until t+dt
209  for (size_t node_iter = 0; node_iter < Base::m_graph.nodes().size(); ++node_iter) {
210  auto& node = Base::m_graph.nodes()[node_iter];
211  Base::m_node_func(Base::m_t, Base::m_dt, node.property);
212  //get new dt of each node
213  dt_cand[node_iter] = node.property.get_simulation().get_dt();
214  }
215 
216  //advance time
217  Base::m_t += Base::m_dt;
218  //new dt ist the minimal dt of all nodes
219  Base::m_dt = *std::min_element(dt_cand.begin(), dt_cand.end());
220  }
221  }
222 
223  RandomNumberGenerator& get_rng()
224  {
225  return m_rng;
226  }
227 
228 private:
230  {
231  //compute current cumulative transition rate
232  FP cumulative_transition_rate = 0.0;
233  for (auto& e : Base::m_graph.edges()) {
234  cumulative_transition_rate +=
235  e.property.get_transition_rates(Base::m_graph.nodes()[e.start_node_idx].property).sum();
236  }
237  return cumulative_transition_rate;
238  }
239 
240  void get_rates(std::vector<FP>& rates)
241  {
242  size_t j = 0;
243  for (auto& e : Base::m_graph.edges()) {
244  auto edge_rates = e.property.get_transition_rates(Base::m_graph.nodes()[e.start_node_idx].property);
245  for (Eigen::Index i = 0; i < edge_rates.size(); ++i) {
246  const auto compartment_value =
247  Base::m_graph.nodes()[e.start_node_idx].property.get_result().get_last_value()[i];
248  rates[j] = (compartment_value < 1.0) ? 0.0 : edge_rates(i);
249 
250  j++;
251  }
252  }
253  }
254 
255  std::vector<FP> m_rates;
256  RandomNumberGenerator m_rng;
257 };
258 
259 template <typename FP, typename Timepoint, class Timespan, class Graph, class NodeF, class EdgeF>
260 auto make_graph_sim(Timepoint t0, Timespan dt, Graph&& g, NodeF&& node_func, EdgeF&& edge_func)
261 {
262  return GraphSimulation<FP, std::decay_t<Graph>, Timepoint, Timespan, EdgeF, NodeF>(
263  t0, dt, std::forward<Graph>(g), std::forward<NodeF>(node_func), std::forward<EdgeF>(edge_func));
264 }
265 
266 template <typename FP, class Graph, class NodeF, class EdgeF>
267 auto make_graph_sim_stochastic(FP t0, FP dt, Graph&& g, NodeF&& node_func, EdgeF&& edge_func)
268 {
270  t0, dt, std::forward<Graph>(g), std::forward<NodeF>(node_func), std::forward<EdgeF>(edge_func));
271 }
272 
273 // FeedbackGraphSimulation is only allowed to be used with local FeedbackSimulation.
274 // Therefore, we use type traits to check if the type is a specialization of FeedbackSimulation
275 template <class T>
276 struct is_feedback_simulation : std::false_type {
277 };
278 
279 template <typename FP, typename Sim, typename ContactPatterns>
280 struct is_feedback_simulation<FeedbackSimulation<FP, Sim, ContactPatterns>> : std::true_type {
281 };
282 
283 template <typename FP, class Graph>
285 {
286 public:
287  FeedbackGraphSimulation(Graph&& g, FP t0, FP dt)
288  : m_graph(std::move(g))
289  , m_t(t0)
290  , m_dt(dt)
291  , m_initialized(false)
293  {
294  using SimT = decltype(m_graph.nodes()[0].property.get_simulation());
295  static_assert(is_feedback_simulation<std::decay_t<SimT>>::value,
296  "Graph node simulation must be a FeedbackSimulation.");
297  }
298 
299  void advance(FP t_max)
300  {
301  // Initialize global and regional ICU occupancy if not done yet
302  if (!m_initialized) {
305  m_initialized = true;
306  }
307 
308  while (m_t < t_max) {
309  FP dt_eff = std::min(m_dt, t_max - m_t);
310 
311  // assign the regional and global ICU occupancy to each node
313 
314  // apply feedback and advance simulation for each node
315  for (auto& node : m_graph.nodes()) {
316  node.property.get_simulation().advance(m_t + dt_eff, m_dt);
317  }
318 
319  // apply mobility between nodes
320  for (auto& edge : m_graph.edges()) {
321  auto& node_from = m_graph.nodes()[edge.start_node_idx];
322  auto& node_to = m_graph.nodes()[edge.end_node_idx];
323  edge.property.apply_mobility(m_t, m_dt, node_from.property, node_to.property);
324  }
325 
326  // update ICU occupancy for each node
329 
330  m_t += dt_eff;
331  }
332  }
333 
335  {
336  return m_graph;
337  }
338 
339 private:
345  {
346  auto& first_node_sim = m_graph.nodes()[0].property.get_simulation();
347  auto& first_node_icu = first_node_sim.get_parameters().template get<ICUOccupancyHistory<FP>>();
348  m_global_icu_occupancy = mio::TimeSeries<FP>(first_node_icu.get_num_elements());
349  m_global_icu_occupancy.add_time_point(0.0, Eigen::VectorXd::Zero(first_node_icu.get_num_elements()));
350 
351  FP total_population = 0;
352  for (Eigen::Index i = 0; i < first_node_icu.get_num_time_points(); ++i) {
353  Eigen::VectorXd sum = Eigen::VectorXd::Zero(first_node_icu.get_num_elements());
354  for (auto& node : m_graph.nodes()) {
355  auto& sim = node.property.get_simulation();
356  auto& icu_history = sim.get_parameters().template get<ICUOccupancyHistory<FP>>();
357  sum += icu_history.get_value(i) * node.property.get_simulation().get_model().populations.get_total();
358  total_population += node.property.get_simulation().get_model().populations.get_total();
359  }
360  m_global_icu_occupancy.add_time_point(first_node_icu.get_time(i), sum / total_population);
361  }
362  }
363 
370  {
371  std::unordered_map<int, FP> regional_population;
372  for (auto& node : m_graph.nodes()) {
373  auto region_id = mio::regions::get_state_id(node.id).get();
374  regional_population[region_id] += node.property.get_simulation().get_model().populations.get_total();
375  }
376 
377  auto& first_node_sim = m_graph.nodes()[0].property.get_simulation();
378  auto& first_node_icu = first_node_sim.get_parameters().template get<ICUOccupancyHistory<FP>>();
379  Eigen::Index num_time_points = first_node_icu.get_num_time_points();
380  Eigen::Index num_elements = first_node_icu.get_num_elements();
381 
382  // For each region: vector of summed ICU values for all time points
383  std::unordered_map<int, std::vector<Eigen::VectorXd>> regional_sums;
384  for (auto const& [region_id, _] : regional_population) {
385  regional_sums[region_id] =
386  std::vector<Eigen::VectorXd>(num_time_points, Eigen::VectorXd::Zero(num_elements));
387  }
388 
389  // Sum up
390  for (auto& node : m_graph.nodes()) {
391  auto region_id = mio::regions::get_state_id(node.id).get();
392  auto& sim = node.property.get_simulation();
393  auto& icu_history = sim.get_parameters().template get<ICUOccupancyHistory<FP>>();
394  FP pop = node.property.get_simulation().get_model().populations.get_total();
395  for (Eigen::Index i = 0; i < num_time_points; ++i) {
396  regional_sums[region_id][i] += icu_history.get_value(i) * pop;
397  }
398  }
399 
400  // Initialize TimeSeries and insert values
401  m_regional_icu_occupancy.clear();
402  for (auto const& [region_id, sum_vec] : regional_sums) {
403  mio::TimeSeries<FP> ts(num_elements);
404  for (Eigen::Index i = 0; i < num_time_points; ++i) {
405  ts.add_time_point(first_node_icu.get_time(i), sum_vec[i] / regional_population[region_id]);
406  }
407  m_regional_icu_occupancy.emplace(region_id, std::move(ts));
408  }
409  }
410 
416  {
417  FP total_population = 0;
418  Eigen::VectorXd sum = Eigen::VectorXd::Zero(m_global_icu_occupancy.get_num_elements());
419  for (auto& node : m_graph.nodes()) {
420  auto& sim = node.property.get_simulation();
421  auto& icu_history = sim.get_parameters().template get<ICUOccupancyHistory<FP>>();
422  sum += icu_history.get_last_value() * node.property.get_simulation().get_model().populations.get_total();
423  total_population += node.property.get_simulation().get_model().populations.get_total();
424  }
425  m_global_icu_occupancy.add_time_point(m_t, sum / total_population);
426  }
427 
433  {
434 
435  std::unordered_map<int, FP> regional_population;
436  for (auto& [region_id, regional_data] : m_regional_icu_occupancy) {
437  Eigen::VectorXd sum = Eigen::VectorXd::Zero(regional_data.get_num_elements());
438  for (auto& node : m_graph.nodes()) {
439  if (mio::regions::get_state_id(node.id).get() == region_id) {
440  auto& sim = node.property.get_simulation();
441  auto& icu_history = sim.get_parameters().template get<ICUOccupancyHistory<FP>>();
442  sum += icu_history.get_last_value() *
443  node.property.get_simulation().get_model().populations.get_total();
444  regional_population[region_id] +=
445  node.property.get_simulation().get_model().populations.get_total();
446  }
447  }
448  regional_data.add_time_point(m_t, sum / regional_population[region_id]);
449  }
450  }
451 
457  {
458  for (auto& node : m_graph.nodes()) {
459  auto region_id = mio::regions::get_state_id(node.id).get();
460  auto& sim = node.property.get_simulation();
461  sim.set_regional_icu_occupancy(m_regional_icu_occupancy.at(region_id));
462  sim.set_global_icu_occupancy(m_global_icu_occupancy);
463  }
464  }
465 
467  FP m_t;
468  FP m_dt;
471  std::unordered_map<int, mio::TimeSeries<FP>> m_regional_icu_occupancy;
472 };
473 
474 } // namespace mio
475 #endif //MIO_MOBILITY_GRAPH_SIMULATION_H
Definition: graph_simulation.h:285
bool m_initialized
Definition: graph_simulation.h:469
Graph & get_graph()
Definition: graph_simulation.h:334
std::unordered_map< int, mio::TimeSeries< FP > > m_regional_icu_occupancy
Definition: graph_simulation.h:471
void calculate_regional_icu_occupancy()
Calculates the regional ICU occupancy for each region.
Definition: graph_simulation.h:369
void update_regional_icu_occupancy()
Updates the regional ICU occupancy for each region with the latest values.
Definition: graph_simulation.h:432
Graph m_graph
Definition: graph_simulation.h:466
mio::TimeSeries< FP > m_global_icu_occupancy
Definition: graph_simulation.h:470
void distribute_icu_data()
Distributes the calculated global and regional ICU occupancy data to each node.
Definition: graph_simulation.h:456
void calculate_global_icu_occupancy()
Calculates the global ICU occupancy based on the ICU history of all nodes.
Definition: graph_simulation.h:344
FP m_t
Definition: graph_simulation.h:467
FeedbackGraphSimulation(Graph &&g, FP t0, FP dt)
Definition: graph_simulation.h:287
void update_global_icu_occupancy()
Updates the global ICU occupancy with the latest values from all nodes.
Definition: graph_simulation.h:415
FP m_dt
Definition: graph_simulation.h:468
void advance(FP t_max)
Definition: graph_simulation.h:299
A generic feedback simulation extending existing simulations with a feedback mechanism.
Definition: feedback_simulation.h:214
abstract simulation on a graph with alternating node and edge actions
Definition: graph_simulation.h:36
Graph m_graph
Definition: graph_simulation.h:86
Graph && get_graph() &&
Definition: graph_simulation.h:78
node_function m_node_func
Definition: graph_simulation.h:87
Timepoint get_t() const
Definition: graph_simulation.h:63
edge_f edge_function
Definition: graph_simulation.h:41
edge_function m_edge_func
Definition: graph_simulation.h:88
const Graph & get_graph() const &
Definition: graph_simulation.h:73
GraphSimulationBase(Timepoint t0, Timespan dt, const Graph &g, const node_function &node_func, const edge_function &&edge_func)
Definition: graph_simulation.h:43
GraphT Graph
Definition: graph_simulation.h:38
Graph & get_graph() &
Definition: graph_simulation.h:68
node_f node_function
Definition: graph_simulation.h:40
Timepoint m_t
Definition: graph_simulation.h:84
Timespan m_dt
Definition: graph_simulation.h:85
GraphSimulationBase(Timepoint t0, Timespan dt, Graph &&g, const node_function &node_func, const edge_function &&edge_func)
Definition: graph_simulation.h:53
Definition: graph_simulation.h:129
typename Base::node_function node_function
Definition: graph_simulation.h:135
RandomNumberGenerator m_rng
Definition: graph_simulation.h:256
void advance(FP t_max)
Definition: graph_simulation.h:154
std::vector< FP > m_rates
Definition: graph_simulation.h:255
GraphSimulationStochastic(FP t0, FP dt, const Graph &g, const node_function &node_func, const edge_function &&edge_func)
Definition: graph_simulation.h:139
RandomNumberGenerator & get_rng()
Definition: graph_simulation.h:223
typename Base::edge_function edge_function
Definition: graph_simulation.h:136
FP get_cumulative_transition_rate()
Definition: graph_simulation.h:229
GraphSimulationStochastic(FP t0, FP dt, Graph &&g, const node_function &node_func, const edge_function &&edge_func)
Definition: graph_simulation.h:147
void get_rates(std::vector< FP > &rates)
Definition: graph_simulation.h:240
Definition: graph_simulation.h:96
void advance(Timepoint t_max=1.0)
Definition: graph_simulation.h:101
generic graph structure
Definition: graph.h:153
auto edges()
range of edges
Definition: graph.h:267
NodePropertyT NodeProperty
Definition: graph.h:155
auto nodes()
range of nodes
Definition: graph.h:251
EdgePropertyT EdgeProperty
Definition: graph.h:156
stores vectors of values at time points (or some other abstract variable) the value at each time poin...
Definition: time_series.h:58
Eigen::Ref< Vector > add_time_point()
add one uninitialized time point
Definition: time_series.h:221
double ScalarType
Configuration of memilio library.
Definition: memilio/config.h:30
static min_max_return_type< ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > >::type min(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &a, const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &b)
Definition: ad.hpp:2599
trait_value< T >::RETURN_TYPE & value(T &x)
Definition: ad.hpp:3308
int size(Comm comm)
Return the size of the given communicator.
Definition: miompi.cpp:75
StateId get_state_id(int county)
get the id of the state that the specified county is in.
Definition: regions.cpp:32
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
auto i
Definition: io.h:809
auto make_graph_sim_stochastic(FP t0, FP dt, Graph &&g, NodeF &&node_func, EdgeF &&edge_func)
Definition: graph_simulation.h:267
auto make_graph_sim(Timepoint t0, Timespan dt, Graph &&g, NodeF &&node_func, EdgeF &&edge_func)
Definition: graph_simulation.h:260
Definition: io.h:94
Definition: graph_simulation.h:276