graph.h Source File

CPP API: graph.h Source File
graph.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 MEmilio
3 *
4 * Authors: Daniel Abele, Martin J. Kuehn
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 GRAPH_H
21 #define GRAPH_H
22 
26 #include "memilio/utils/date.h"
28 #include "memilio/utils/stl_util.h"
30 
31 #include <algorithm>
32 #include <iostream>
33 #include <concepts>
34 
35 #include "boost/filesystem.hpp"
36 
37 //is used to provide some paths as function arguments
38 namespace fs = boost::filesystem;
39 
40 namespace mio
41 {
42 
43 struct OutEdgeBase {
44  OutEdgeBase(size_t start)
45  : start_node_idx(start)
46  {
47  }
49 };
50 struct InEdgeBase {
51  InEdgeBase(size_t end)
52  : end_node_idx(end)
53  {
54  }
55  size_t end_node_idx;
56 };
57 struct EdgeBase : public OutEdgeBase, public InEdgeBase {
58  EdgeBase(size_t start, size_t end)
59  : OutEdgeBase(start)
60  , InEdgeBase(end)
61  {
62  }
63 };
64 
69 template <class NodePropertyT>
70 struct Node {
71  template <class... Args>
72  Node(int node_id, Args&&... args)
73  : id{node_id}
74  , property(std::forward<Args>(args)...)
75  {
76  }
77  int id;
78  NodePropertyT property;
79 };
80 
84 template <class EdgePropertyT>
85 struct Edge : public EdgeBase {
86  template <class... Args>
87  Edge(size_t start, size_t end, Args&&... args)
88  : EdgeBase{start, end}
89  , property(std::forward<Args>(args)...)
90  {
91  }
92 
93  EdgePropertyT property;
94 };
95 
99 template <std::equality_comparable T>
100 bool operator==(const Node<T>& n1, const Node<T>& n2)
101 {
102  return n1.id == n2.id && n1.property == n2.property;
103 }
104 template <std::equality_comparable T>
105 bool operator!=(const Node<T>& n1, const Node<T>& n2)
106 {
107  return !(n1 == n2);
108 }
109 
113 template <std::equality_comparable T>
114 bool operator==(const Edge<T>& e1, const Edge<T>& e2)
115 {
116  return e1.start_node_idx == e2.start_node_idx && e1.end_node_idx == e2.end_node_idx && e1.property == e2.property;
117 }
118 template <std::equality_comparable T>
119 bool operator!=(const Edge<T>& e1, const Edge<T>& e2)
120 {
121  return !(e1 == e2);
122 }
123 
127 template <class T>
128  requires HasOstreamOperator<T>
129 std::ostream& operator<<(std::ostream& os, const Edge<T>& e)
130 {
131  os << e.start_node_idx << " > " << e.end_node_idx << " : " << e.property;
132  return os;
133 }
134 
138 template <class T>
139  requires(!HasOstreamOperator<T>)
140 std::ostream& operator<<(std::ostream& os, const Edge<T>& e)
141 {
142  os << e.start_node_idx << " > " << e.end_node_idx;
143  return os;
144 }
145 
149 template <class NodePropertyT, class EdgePropertyT>
150 class Graph
151  //ensure correct std::is_copy_constructible; it's not correct by default because the nodes and edges are stored in std::vector
152  : not_copyable_if_t<!(std::is_copy_constructible_v<NodePropertyT> && std::is_copy_constructible_v<EdgePropertyT>)>
153 {
154 public:
155  using NodeProperty = NodePropertyT;
156  using EdgeProperty = EdgePropertyT;
157 
161  Graph(const std::vector<int>& node_ids, const std::vector<NodePropertyT>& node_properties)
162  {
163  assert(node_ids.size() == node_properties.size());
164 
165  for (auto i = size_t(0); i < node_ids.size(); ++i) {
166  add_node(node_ids[i], node_properties[i]);
167  }
168  }
169 
173  Graph(std::vector<NodePropertyT>& node_properties)
174  {
175  for (auto i = size_t(0); i < node_properties.size(); ++i) {
176  add_node(i, node_properties[i]);
177  }
178  }
179 
183  template <class... Args>
184  requires std::constructible_from<NodePropertyT, Args...>
185  Graph(const std::vector<int>& node_ids, Args&&... node_args)
186  {
187  for (int id : node_ids) {
188  add_node(id, std::forward<Args>(node_args)...);
189  }
190  }
191 
195  template <class... Args>
196  requires std::constructible_from<NodePropertyT, Args...>
197  Graph(const int number_of_nodes, Args&&... args)
198  {
199  for (int id = 0; id < number_of_nodes; ++id) {
200  add_node(id, std::forward<Args>(args)...);
201  }
202  }
203 
204  Graph() = default;
205 
209  Graph(std::vector<Node<NodePropertyT>>&& nodes, std::vector<Edge<EdgePropertyT>>&& edges)
210  : m_nodes(std::move(nodes))
211  , m_edges(std::move(edges))
212  {
213  }
214 
221  template <class... Args>
222  requires std::constructible_from<NodePropertyT, Args...>
223  void add_node(int id, Args&&... args)
224  {
225  m_nodes.emplace_back(id, std::forward<Args>(args)...);
226  }
227 
236  template <class... Args>
237  requires std::constructible_from<EdgePropertyT, Args...>
238  void add_edge(size_t start_node_idx, size_t end_node_idx, Args&&... args)
239  {
240  assert(m_nodes.size() > start_node_idx && m_nodes.size() > end_node_idx);
241  insert_sorted_replace(m_edges, Edge<EdgePropertyT>(start_node_idx, end_node_idx, std::forward<Args>(args)...),
242  [](auto&& e1, auto&& e2) {
243  return e1.start_node_idx == e2.start_node_idx ? e1.end_node_idx < e2.end_node_idx
244  : e1.start_node_idx < e2.start_node_idx;
245  });
246  }
247 
251  auto nodes()
252  {
253  return Range(m_nodes);
254  }
255 
259  auto nodes() const
260  {
261  return Range(m_nodes);
262  };
263 
267  auto edges()
268  {
269  return Range(m_edges);
270  }
271 
275  auto edges() const
276  {
277  return Range(m_edges);
278  }
279 
283  auto out_edges(size_t node_idx)
284  {
285  return out_edges(begin(m_edges), end(m_edges), node_idx);
286  }
287 
291  auto out_edges(size_t node_idx) const
292  {
293  return out_edges(begin(m_edges), end(m_edges), node_idx);
294  }
295 
296 private:
297  template <typename Iter>
298  static auto out_edges(Iter b, Iter e, size_t idx)
299  {
300  return Range(std::equal_range(b, e, OutEdgeBase(idx), [](auto&& e1, auto&& e2) {
301  return e1.start_node_idx < e2.start_node_idx;
302  }));
303  }
304 
305 private:
306  std::vector<Node<NodePropertyT>> m_nodes;
307  std::vector<Edge<EdgePropertyT>> m_edges;
308 }; // namespace mio
309 
330 template <typename FP, class TestAndTrace, class ContactPattern, class Model, class MobilityParams, class Parameters,
331  class ReadFunction, class NodeIdFunction>
332 IOResult<void> set_nodes(const Parameters& params, Date start_date, Date end_date, const fs::path& data_dir,
333  const std::string& population_data_path, bool is_node_for_county,
334  Graph<Model, MobilityParams>& params_graph, ReadFunction&& read_func,
335  NodeIdFunction&& node_func, const std::vector<FP>& scaling_factor_inf, FP scaling_factor_icu,
336  FP tnt_capacity_factor, int num_days = 0, bool export_time_series = false,
337  bool rki_age_groups = true)
338 
339 {
340  BOOST_OUTCOME_TRY(auto&& node_ids, node_func(population_data_path, is_node_for_county, rki_age_groups));
341  std::vector<Model> nodes(node_ids.size(), Model(int(size_t(params.get_num_groups()))));
342  for (auto& node : nodes) {
343  node.parameters = params;
344  }
345 
346  BOOST_OUTCOME_TRY(read_func(nodes, start_date, node_ids, scaling_factor_inf, scaling_factor_icu, data_dir.string(),
347  num_days, export_time_series));
348 
349  for (size_t node_idx = 0; node_idx < nodes.size(); ++node_idx) {
350 
351  auto tnt_capacity = nodes[node_idx].populations.get_total() * tnt_capacity_factor;
352 
353  //local parameters
354  auto& tnt_value = nodes[node_idx].parameters.template get<TestAndTrace>();
355  tnt_value = UncertainValue<FP>(0.5 * (1.2 * tnt_capacity + 0.8 * tnt_capacity));
356  tnt_value.set_distribution(mio::ParameterDistributionUniform(0.8 * tnt_capacity, 1.2 * tnt_capacity));
357 
358  auto id = 0;
359  if (is_node_for_county) {
360  id = int(regions::CountyId(node_ids[node_idx]));
361  }
362  else {
363  id = int(regions::DistrictId(node_ids[node_idx]));
364  }
365  //holiday periods
366  auto holiday_periods = regions::get_holidays(regions::get_state_id(id), start_date, end_date);
367  auto& contacts = nodes[node_idx].parameters.template get<ContactPattern>();
368  contacts.get_school_holidays() =
369  std::vector<std::pair<mio::SimulationTime<FP>, mio::SimulationTime<FP>>>(holiday_periods.size());
370  std::transform(
371  holiday_periods.begin(), holiday_periods.end(), contacts.get_school_holidays().begin(), [=](auto& period) {
372  return std::make_pair(mio::SimulationTime<FP>(mio::get_offset_in_days(period.first, start_date)),
373  mio::SimulationTime<FP>(mio::get_offset_in_days(period.second, start_date)));
374  });
375 
376  //uncertainty in populations
377  for (auto i = mio::AgeGroup(0); i < params.get_num_groups(); i++) {
378  for (auto j = Index<typename Model::Compartments>(0); j < Model::Compartments::Count; ++j) {
379  auto& compartment_value = nodes[node_idx].populations[{i, j}];
380  compartment_value =
381  UncertainValue<FP>(0.5 * (1.1 * compartment_value.value() + 0.9 * compartment_value.value()));
382  compartment_value.set_distribution(mio::ParameterDistributionUniform(0.9 * compartment_value.value(),
383  1.1 * compartment_value.value()));
384  }
385  }
386 
387  params_graph.add_node(node_ids[node_idx], nodes[node_idx]);
388  }
389  return success();
390 }
391 
403 template <typename FP, class ContactLocation, class Model, class MobilityParams, class MobilityCoefficientGroup,
404  class InfectionState, class ReadFunction>
405 IOResult<void> set_edges(const fs::path& mobility_data_file, Graph<Model, MobilityParams>& params_graph,
406  std::initializer_list<InfectionState>& mobile_compartments, size_t contact_locations_size,
407  ReadFunction&& read_func, std::vector<FP> commuting_weights,
408  std::vector<std::vector<size_t>> indices_of_saved_edges = {})
409 {
410  // mobility between nodes
411  BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, read_func(mobility_data_file.string()));
412  if (mobility_data_commuter.rows() != Eigen::Index(params_graph.nodes().size()) ||
413  mobility_data_commuter.cols() != Eigen::Index(params_graph.nodes().size())) {
415  "Mobility matrices do not have the correct size. You may need to run "
416  "transformMobilitydata.py from pycode memilio epidata package.");
417  }
418 
419  for (size_t county_idx_i = 0; county_idx_i < params_graph.nodes().size(); ++county_idx_i) {
420  for (size_t county_idx_j = 0; county_idx_j < params_graph.nodes().size(); ++county_idx_j) {
421  auto& populations = params_graph.nodes()[county_idx_i].property.populations;
422  // mobility coefficients have the same number of components as the contact matrices.
423  // so that the same NPIs/dampings can be used for both (e.g. more home office => fewer commuters)
424  auto mobility_coeffs = MobilityCoefficientGroup(contact_locations_size, populations.numel());
425  auto num_age_groups = (size_t)params_graph.nodes()[county_idx_i].property.parameters.get_num_groups();
426  commuting_weights =
427  (commuting_weights.size() == 0 ? std::vector<FP>(num_age_groups, 1.0) : commuting_weights);
428  //commuters
429  FP working_population = 0.0;
430  for (auto age = AgeGroup(0); age < populations.template size<mio::AgeGroup>(); ++age) {
431  working_population += populations.get_group_total(age) * commuting_weights[size_t(age)];
432  }
433  auto commuter_coeff_ij = mobility_data_commuter(county_idx_i, county_idx_j) /
434  working_population; //data is absolute numbers, we need relative
435  for (auto age = AgeGroup(0); age < populations.template size<mio::AgeGroup>(); ++age) {
436  for (auto compartment : mobile_compartments) {
437  auto coeff_index = populations.get_flat_index({age, compartment});
438  mobility_coeffs[size_t(ContactLocation::Work)].get_baseline()[coeff_index] =
439  commuter_coeff_ij * commuting_weights[size_t(age)];
440  }
441  }
442  //only add edges with mobility above thresholds for performance
443  //thresholds are chosen empirically so that more than 99% of mobility is covered, approx. 1/3 of the edges
444  if (commuter_coeff_ij > 4e-5) {
445  params_graph.add_edge(county_idx_i, county_idx_j, std::move(mobility_coeffs), indices_of_saved_edges);
446  }
447  }
448  }
449 
450  return success();
451 }
452 
453 template <class T>
454  requires(!HasOstreamOperator<T>)
455 void print_graph_object(std::ostream& os, size_t idx, const T&)
456 {
457  os << idx;
458 }
459 
460 template <class T>
461  requires HasOstreamOperator<T>
462 void print_graph_object(std::ostream& os, size_t idx, const T& o)
463 {
464  os << idx << " [" << o << "]";
465 }
466 
467 template <class Graph>
468 void print_graph(std::ostream& os, const Graph& g)
469 {
470  auto nodes = g.nodes();
471  for (size_t i = 0; i < nodes.size(); ++i) {
472  os << "NODE ";
473  print_graph_object(os, i, nodes[i]);
474  os << '\n';
475  }
476 
477  auto edges = g.edges();
478  for (size_t i = 0; i < edges.size(); ++i) {
479  auto& e = edges[i];
480  os << "EDGE ";
481  print_graph_object(os, i, e.property);
482  os << " FROM NODE ";
483  print_graph_object(os, e.start_node_idx, nodes[e.start_node_idx]);
484  os << " TO ";
485  print_graph_object(os, e.end_node_idx, nodes[e.end_node_idx]);
486  os << '\n';
487  }
488 }
489 
490 } // namespace mio
491 
492 #endif //GRAPH_H
generic graph structure
Definition: graph.h:153
Graph()=default
static auto out_edges(Iter b, Iter e, size_t idx)
Definition: graph.h:298
auto edges() const
range of edges
Definition: graph.h:275
auto out_edges(size_t node_idx)
range of edges going out from a specific node
Definition: graph.h:283
auto edges()
range of edges
Definition: graph.h:267
requires std::constructible_from< EdgePropertyT, Args... > void add_edge(size_t start_node_idx, size_t end_node_idx, Args &&... args)
add an edge to the graph.
Definition: graph.h:238
auto out_edges(size_t node_idx) const
range of edges going out from a specific node
Definition: graph.h:291
std::vector< Node< NodePropertyT > > m_nodes
Definition: graph.h:306
requires std::constructible_from< NodePropertyT, Args... > void add_node(int id, Args &&... args)
add a node to the graph.
Definition: graph.h:223
NodePropertyT NodeProperty
Definition: graph.h:155
requires std::constructible_from< NodePropertyT, Args... > Graph(const std::vector< int > &node_ids, Args &&... node_args)
Construct graph without edges, creating a node for each id in node_ids from the same node_args.
Definition: graph.h:185
std::vector< Edge< EdgePropertyT > > m_edges
Definition: graph.h:307
requires std::constructible_from< NodePropertyT, Args... > Graph(const int number_of_nodes, Args &&... args)
Construct graph without edges, creating each node from the same node_args with default node ids [0,...
Definition: graph.h:197
auto nodes() const
range of nodes
Definition: graph.h:259
Graph(const std::vector< int > &node_ids, const std::vector< NodePropertyT > &node_properties)
Construct graph without edges from pairs of node_ids and node_properties.
Definition: graph.h:161
auto nodes()
range of nodes
Definition: graph.h:251
Graph(std::vector< NodePropertyT > &node_properties)
Construct graph without edges from node_properties with default node ids [0, 1, .....
Definition: graph.h:173
EdgePropertyT EdgeProperty
Definition: graph.h:156
Graph(std::vector< Node< NodePropertyT >> &&nodes, std::vector< Edge< EdgePropertyT >> &&edges)
Construct graph containing the given nodes and edges.
Definition: graph.h:209
An Index with more than one template parameter combines several Index objects.
Definition: index.h:181
Definition: parameter_distributions.h:440
double simulation time.
Definition: damping.h:58
The Model of the Simulation.
Definition: abm/model.h:54
Parameters of the simulation that are the same everywhere within the Model.
Definition: abm/parameters.h:764
size_t get_num_groups() const
Get the number of the age groups.
Definition: abm/parameters.h:783
InfectionState
InfectionState in ABM.
Definition: abm/infection_state.h:35
int size(Comm comm)
Return the size of the given communicator.
Definition: miompi.cpp:75
Range< std::vector< std::pair< Date, Date > >::const_iterator > get_holidays(StateId state)
get the holidays in a german state.
Definition: regions.cpp:37
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
requires details::IsElementReference< M > RowMajorIterator< M, false > end(M &m)
create a non-const end iterator for the matrix m.
Definition: eigen_util.h:449
Range(std::pair< I, S > iterator_pair) -> Range< I, S >
Deduction guide to correctly deduce range type when constructed from a pair.
typename not_copyable_if< Cond >::type not_copyable_if_t
equivalent to not_copyable_if<Cond>::type.
Definition: metaprogramming.h:78
requires HasOstreamOperator< T > std::ostream & operator<<(std::ostream &os, const Edge< T > &e)
out stream operator for edges if edge property type has stream operator defined
Definition: graph.h:129
auto failure(const IOStatus &s)
Create an object that is implicitly convertible to an error IOResult<T>.
Definition: io.h:380
bool operator==(const Node< T > &n1, const Node< T > &n2)
comparison operator if node property type is equality comparable
Definition: graph.h:100
auto i
Definition: io.h:809
IOResult< void > set_edges(const fs::path &mobility_data_file, Graph< Model, MobilityParams > &params_graph, std::initializer_list< InfectionState > &mobile_compartments, size_t contact_locations_size, ReadFunction &&read_func, std::vector< FP > commuting_weights, std::vector< std::vector< size_t >> indices_of_saved_edges={})
Sets the graph edges.
Definition: graph.h:405
void print_graph(std::ostream &os, const Graph &g)
Definition: graph.h:468
requires(!std::is_trivial_v< T >) void BinarySerializerObject
Definition: binary_serializer.h:333
requires HasOstreamOperator< T > void print_graph_object(std::ostream &os, size_t idx, const T &o)
Definition: graph.h:462
auto success()
Create an object that is implicitly convertible to a succesful IOResult<void>.
Definition: io.h:359
requires details::IsElementReference< M > RowMajorIterator< M, false > begin(M &m)
create a non-const iterator to first element of the matrix m.
Definition: eigen_util.h:421
IOResult< void > set_nodes(const Parameters &params, Date start_date, Date end_date, const fs::path &data_dir, const std::string &population_data_path, bool is_node_for_county, Graph< Model, MobilityParams > &params_graph, ReadFunction &&read_func, NodeIdFunction &&node_func, const std::vector< FP > &scaling_factor_inf, FP scaling_factor_icu, FP tnt_capacity_factor, int num_days=0, bool export_time_series=false, bool rki_age_groups=true)
Sets the graph nodes for counties or districts.
Definition: graph.h:332
DampingMatrixExpressionGroup< FP, MobilityCoefficients< FP > > MobilityCoefficientGroup
sum of time dependent mobility coefficients.
Definition: metapopulation_mobility_instant.h:115
bool operator!=(const Node< T > &n1, const Node< T > &n2)
Definition: graph.h:105
boost::outcome_v2::unchecked< T, IOStatus > IOResult
Value-or-error type for operations that return a value but can fail.
Definition: io.h:353
void insert_sorted_replace(std::vector< T > &vec, T const &item, Pred pred)
inserts element in a sorted vector, replacing items that are equal precondition: elements in the vect...
Definition: stl_util.h:66
Definition: io.h:94
The AgeGroup struct is used as a dynamically sized tag for all age dependent categories.
Definition: age_group.h:32
Simple date representation as year, month, and day.
Definition: date.h:50
Definition: graph.h:57
EdgeBase(size_t start, size_t end)
Definition: graph.h:58
represents an edge of the graph
Definition: graph.h:85
Edge(size_t start, size_t end, Args &&... args)
Definition: graph.h:87
EdgePropertyT property
Definition: graph.h:93
Definition: graph.h:50
InEdgeBase(size_t end)
Definition: graph.h:51
size_t end_node_idx
Definition: graph.h:55
represents a node of the graph
Definition: graph.h:70
int id
Definition: graph.h:77
NodePropertyT property
Definition: graph.h:78
Node(int node_id, Args &&... args)
Definition: graph.h:72
Definition: graph.h:43
size_t start_node_idx
Definition: graph.h:48
OutEdgeBase(size_t start)
Definition: graph.h:44