Utopia  2
Framework for studying models of complex & adaptive systems.
Opinionet.hh
Go to the documentation of this file.
1 #ifndef UTOPIA_MODELS_OPINIONET_HH
2 #define UTOPIA_MODELS_OPINIONET_HH
3 
4 #include <functional>
5 #include <boost/graph/random.hpp>
6 
7 #include <utopia/core/model.hh>
8 #include <utopia/core/apply.hh>
9 #include <utopia/core/types.hh>
10 #include <utopia/core/graph.hh>
12 
13 #include "modes.hh"
14 #include "revision.hh"
15 
16 
17 namespace Utopia::Models::Opinionet {
18 
21 using Modes::Rewiring;
22 
24 struct Agent {
25  double opinion;
26 };
27 
29 struct Edge {
30  double weight;
31 };
32 
33 // ++ Type definitions ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
34 
36 using VertexContainer = boost::vecS;
37 
39 using EdgeContainer = boost::vecS;
40 
42 using NetworkUndirected = boost::adjacency_list<
45  boost::undirectedS,
46  Agent>;
47 
49 using NetworkDirected = boost::adjacency_list<
52  boost::bidirectionalS,
53  Agent,
54  Edge>;
55 
56 // The undirected network type
57 using NetworkUndir = boost::adjacency_list<
60  boost::undirectedS,
61  Agent
62 >;
63 
67 };
68 
71 
72 // ++ Model definition ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
73 
75 
79 template<typename NWType=NetworkUndirected>
80 class Opinionet:
81  public Model<Opinionet<NWType>, OpinionetTypes>
82 {
83 public:
86 
88  using Config = typename Base::Config;
89 
91  using DataGroup = typename Base::DataGroup;
92 
94  using DataSet = typename Base::DataSet;
95 
97  using RNG = typename Base::RNG;
98 
99 private:
100  // Base members: _time, _name, _cfg, _hdfgrp, _rng, _monitor, _log, _space
101 
106 
108  NWType _nw;
109  const double _tolerance;
110  const double _susceptibility;
111  const double _weighting;
112 
114  std::uniform_real_distribution<double> _uniform_prob_distr;
115 
116  // Datasets and Datagroups
117  const std::shared_ptr<DataGroup> _dgrp_nw;
118  const std::shared_ptr<DataSet> _dset_opinion;
119  const std::shared_ptr<DataSet> _dset_edge_weights;
120 
121 public:
123 
126  template<class ParentModel>
127  Opinionet (const std::string name,
128  ParentModel &parent)
129  :
130  // Initialize first via base model
131  Base(name, parent),
132  // Initialize modes
135  _rewire(this->initialize_rewiring()),
136  // Initialize the network
137  _nw(this->initialize_nw()),
138  // Initialize the model parameters
139  _tolerance(get_as<double>("tolerance", this->_cfg)),
140  _susceptibility(get_as<double>("susceptibility", this->_cfg)),
141  _weighting(get_as<double>(
142  "weighting", this->_cfg["network"]["edges"])),
143  _uniform_prob_distr(0., 1.),
144  // Create datagroups and datasets
145  _dgrp_nw(create_graph_group(_nw, this->_hdfgrp, "nw")),
146  _dset_opinion(this->create_dset("opinion", _dgrp_nw,
147  {boost::num_vertices(_nw)})),
149  {
150  this->_log->debug("Constructing the Opinionet Model ...");
151 
152  // Initialize the network properties
153  this->initialize_properties();
154 
155  this->_log->info(
156  "Initialized network with {} vertices and {} edges. Directed: {}",
157  boost::num_vertices(_nw),
158  boost::num_edges(_nw),
160  );
161 
162  // Mark the datasets as vertex properties and add dimensions and
163  // coordinates.
164  _dset_opinion->add_attribute("is_vertex_property", true);
165  _dset_opinion->add_attribute("dim_name__1", "vertex_idx");
166  _dset_opinion->add_attribute("coords_mode__vertex_idx", "trivial");
167 
170  this->_log->debug("Network saved.");
171  }
172 
173  else {
174  // Write the vertex data once, as it does not change with time
175  auto _dset_vertices = _dgrp_nw->open_dataset(
176  "_vertices", {boost::num_vertices(_nw)}
177  );
178  auto [v, v_end] = boost::vertices(_nw);
179  _dset_vertices->write(
180  v, v_end,
181  [&](auto vd){
182  return boost::get(boost::vertex_index_t(), _nw, vd);
183  }
184  );
185  _dset_vertices->add_attribute("dim_name__0", "vertex_idx");
186  _dset_vertices->add_attribute(
187  "coords_mode__vertex_idx", "trivial"
188  );
189  }
190 
191  }
192 
193 private:
194 
195  // .. Setup functions .....................................................
196 
198  if (get_as<std::string>("interaction_function", this->_cfg)
199  == "Deffuant")
200  {
202  }
203  else {
205  }
206  }
207 
209  if (get_as<std::string>("type", this->_cfg["opinion_space"])
210  == "discrete")
211  {
213  }
214  else {
216  }
217  }
218 
220  if (get_as<bool>("rewiring", this->_cfg["network"]["edges"])) {
221  return Rewiring::RewiringOn;
222  }
223  else {
224  return Rewiring::RewiringOff;
225  }
226  }
227 
229  this->_log->debug("Initializing the properties ...");
230 
231  // Continuous opinion space: draw opinions from a continuous interval
233  const std::pair<double, double> opinion_interval =
234  get_as<std::pair<double, double>>(
235  "interval", this->_cfg["opinion_space"]
236  );
237 
238  if (opinion_interval.first >= opinion_interval.second) {
239  throw std::invalid_argument("Error: The given opinion interval"
240  " is invalid! Specify an interval of the kind [a, b], "
241  "with a<b!");
242  }
243 
244  for (const auto v : range<IterateOver::vertices>(_nw)) {
245  _nw[v].opinion = Utils::get_rand<double>(
246  opinion_interval, *this->_rng);
247  }
248  }
249  // Discrete opinion space: draw opinions from a discrete set
250  else {
251  const int opinion_values =
252  get_as<int>("num_opinions", this->_cfg["opinion_space"]);
253 
254  for (const auto v : range<IterateOver::vertices>(_nw)) {
255  _nw[v].opinion =
256  Utils::get_rand<int>(
257  std::make_pair<int, int>(0, opinion_values-1),
258  *this->_rng
259  );
260  }
261  }
262 
263  // For directed network: Initialize weights depending on the opinion
264  // distances. In the undirected case, weights are not required.
265  if constexpr (Utils::is_directed<NWType>()) {
266  for (const auto v : range<IterateOver::vertices>(_nw)) {
267  if (boost::out_degree(v, _nw) != 0) {
269  }
270  }
271  }
272  }
273 
274  NWType initialize_nw() const {
275  this->_log->debug("Creating the network ...");
276 
277  auto g = Graph::create_graph<NWType>(this->_cfg["network"], *this->_rng);
278 
279  this->_log->debug("Network created.");
280 
281  return g;
282  }
283 
284  // Only initialize edge weight dataset for directed graphs
285  std::shared_ptr<DataSet> create_edge_weight_dset() {
286  if constexpr (Utils::is_directed<NWType>()) {
287  return this->create_dset(
288  "edge_weights", _dgrp_nw, {boost::num_edges(_nw)}
289  );
290  }
291  else {
292  return 0;
293  }
294  }
295 
296 
297 public:
298 
299  // .. Runtime functions ...................................................
300 
308  void perform_step ()
309  {
311  _nw,
313  _tolerance,
314  _weighting,
315  _interaction,
317  _rewire,
319  *this->_rng
320  );
321  }
322 
323 
325 
330  void monitor () {
331  double mean_opinion = 0.;
332  double opinion_std = 0.;
333  double min_opinion = std::numeric_limits<double>::infinity();
334  double max_opinion = -std::numeric_limits<double>::infinity();
335 
336  double temp_op;
337  for (const auto v : range<IterateOver::vertices>(_nw)) {
338  temp_op = _nw[v].opinion;
339  mean_opinion += temp_op;
340 
341  if (temp_op < min_opinion) {
342  min_opinion = temp_op;
343  }
344 
345  if (temp_op > max_opinion) {
346  max_opinion = temp_op;
347  }
348  }
349 
350  mean_opinion /= boost::num_vertices(_nw);
351 
352  for (const auto v : range<IterateOver::vertices>(_nw)) {
353  opinion_std += std::pow(_nw[v].opinion - mean_opinion, 2.);
354  }
355 
356  opinion_std = std::sqrt(opinion_std / (boost::num_vertices(_nw) - 1.));
357 
358  this->_monitor.set_entry("mean_opinion", mean_opinion);
359  this->_monitor.set_entry("opinion_std", opinion_std);
360  this->_monitor.set_entry("min_opinion", min_opinion);
361  this->_monitor.set_entry("max_opinion", max_opinion);
362  }
363 
364 
366  void write_data ()
367  {
368  // Get the vertex iterators
369  auto [v, v_end] = boost::vertices(_nw);
370 
371  // Write opinions
372  _dset_opinion->write(
373  v, v_end, [this](auto vd) { return _nw[vd].opinion; }
374  );
375 
376  // Write edges
378  // Adaptor tuple that allows to save the edge data
379  const auto get_edge_data = std::make_tuple(
380  std::make_tuple("_edges", "type",
381  std::make_tuple("source",
382  [](auto& ed, auto& _nw) {
383  return boost::get(
384  boost::vertex_index_t(), _nw,
385  boost::source(ed, _nw));
386  }
387  ),
388  std::make_tuple("target",
389  [](auto& ed, auto& _nw) {
390  return boost::get(
391  boost::vertex_index_t(), _nw,
392  boost::target(ed, _nw));
393  }
394  )
395  )
396  );
397  // Save the edge data using the current time as label.
399  _nw, _dgrp_nw, std::to_string(this->get_time()), get_edge_data
400  );
401  }
402 
403  // Write edge weights
404  if constexpr (Utils::is_directed<NWType>()) {
405 
406  auto [e, e_end] = boost::edges(_nw);
407 
408  _dset_edge_weights->write(
409  e, e_end, [this](auto ed) { return _nw[ed].weight; }
410  );
411  }
412  }
413 
414  // .. Getters and setters .................................................
415  // Add getters and setters here to interface with other model
416 };
417 
418 } // namespace Utopia::Models::Opinionet
419 
420 #endif // UTOPIA_MODELS_OPINIONET_HH
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
Monitor _monitor
The monitor.
Definition: model.hh:188
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
std::shared_ptr< DataSet > create_dset(const std::string name, const std::shared_ptr< DataGroup > &hdfgrp, std::vector< hsize_t > add_write_shape, const std::size_t compression_level=1, const std::vector< hsize_t > chunksize={})
Create a new dataset within the given group.
Definition: model.hh:752
Time get_time() const
Return the current time of this model.
Definition: model.hh:393
typename ModelTypes::DataGroup DataGroup
Data type that is used for storing datasets.
Definition: model.hh:122
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
The Opinionet model class.
Definition: Opinionet.hh:82
typename Base::Config Config
Data type that holds the configuration.
Definition: Opinionet.hh:88
Rewiring initialize_rewiring()
Definition: Opinionet.hh:219
const double _tolerance
Definition: Opinionet.hh:109
const double _susceptibility
Definition: Opinionet.hh:110
const std::shared_ptr< DataSet > _dset_edge_weights
Definition: Opinionet.hh:119
typename Base::DataSet DataSet
Data type for a dataset.
Definition: Opinionet.hh:94
NWType _nw
Network and model dynamics parameters.
Definition: Opinionet.hh:108
void write_data()
Write data.
Definition: Opinionet.hh:366
const Opinion_space_type _opinion_space
Definition: Opinionet.hh:104
void monitor()
Monitor model information.
Definition: Opinionet.hh:330
Opinion_space_type initialize_opinion_space()
Definition: Opinionet.hh:208
const std::shared_ptr< DataSet > _dset_opinion
Definition: Opinionet.hh:118
const double _weighting
Definition: Opinionet.hh:111
void initialize_properties()
Definition: Opinionet.hh:228
typename Base::DataGroup DataGroup
Data type of the group to write model data to, holding datasets.
Definition: Opinionet.hh:91
typename Base::RNG RNG
Data type of the shared RNG.
Definition: Opinionet.hh:97
std::shared_ptr< DataSet > create_edge_weight_dset()
Definition: Opinionet.hh:285
void perform_step()
Iterate a single step @detail Each step consists of an opinion update and edge rewiring....
Definition: Opinionet.hh:308
const std::shared_ptr< DataGroup > _dgrp_nw
Definition: Opinionet.hh:117
NWType initialize_nw() const
Definition: Opinionet.hh:274
Opinionet(const std::string name, ParentModel &parent)
Construct the Opinionet model.
Definition: Opinionet.hh:127
Interaction_type initialize_interaction()
Definition: Opinionet.hh:197
const Interaction_type _interaction
Modes.
Definition: Opinionet.hh:103
const Rewiring _rewire
Definition: Opinionet.hh:105
std::uniform_real_distribution< double > _uniform_prob_distr
A uniform probability distribution.
Definition: Opinionet.hh:114
ReturnType get_as(const std::string &key, const DataIO::Config &node)
This function is a wrapper around the yaml-cpp YAML::Node::as function.
Definition: cfg_utils.hh:158
std::string to_string(const Config &node)
Given a config node, returns a string representation of it.
Definition: cfg_utils.hh:110
void save_edge_properties(Graph &&g, const std::shared_ptr< HDFGroup > &nw_grp, const std::string &label, const std::tuple< Adaptors... > &adaptor_tuple)
Definition: graph_utils.hh:573
std::shared_ptr< HDFGroup > create_graph_group(const Graph &g, const std::shared_ptr< HDFGroup > &parent_grp, const std::string &name)
Definition: graph_utils.hh:291
void save_graph(const Graph &g, const std::shared_ptr< HDFGroup > &grp)
Write function for a boost::Graph.
Definition: graph_utils.hh:332
Opinion_space_type
Definition: modes.hh:13
@ discrete
Definition: modes.hh:15
@ continuous
Definition: modes.hh:14
Interaction_type
Definition: modes.hh:8
@ Deffuant
Definition: modes.hh:9
@ HegselmannKrause
Definition: modes.hh:10
Rewiring
Definition: modes.hh:18
@ RewiringOn
Definition: modes.hh:19
@ RewiringOff
Definition: modes.hh:20
void revision(NWType &nw, const double susceptibility, const double tolerance, const double weighting, const Interaction_type interaction, const Opinion_space_type opinion_space, const Rewiring rewire, std::uniform_real_distribution< double > &prob_distr, RNGType &rng)
Performs an opinion update and edge rewiring (if enabled).
Definition: revision.hh:149
constexpr bool is_directed()
Check whether the network type allows for directed edges.
Definition: utils.hh:35
void set_and_normalize_weights(const VertexDescType v, NWType &nw, const double weighting)
Set and normalize weights according to opinion difference.
Definition: utils.hh:102
Definition: modes.hh:4
boost::adjacency_list< EdgeContainer, VertexContainer, boost::undirectedS, Agent > NetworkUndirected
The undirected network type.
Definition: Opinionet.hh:46
boost::adjacency_list< EdgeContainer, VertexContainer, boost::bidirectionalS, Agent, Edge > NetworkDirected
The directed network type.
Definition: Opinionet.hh:54
boost::vecS VertexContainer
The vertex container type.
Definition: Opinionet.hh:36
boost::vecS EdgeContainer
The edge container type.
Definition: Opinionet.hh:39
InteractionMode
Definition: Opinionet.hh:64
@ hegselmann_krause
Definition: Opinionet.hh:66
@ deffuant
Definition: Opinionet.hh:65
boost::adjacency_list< EdgeContainer, VertexContainer, boost::undirectedS, Agent > NetworkUndir
Definition: Opinionet.hh:62
Wrapper struct for defining model class data types.
Definition: model.hh:92
Each node in the network accomodates a single agent.
Definition: Opinionet.hh:24
double opinion
Definition: Opinionet.hh:25
Each network edge has a weight representing an interaction probability.
Definition: Opinionet.hh:29
double weight
Definition: Opinionet.hh:30