1 #ifndef UTOPIA_CORE_GRAPH_CREATION_HH
2 #define UTOPIA_CORE_GRAPH_CREATION_HH
8 #include <boost/graph/adjacency_list.hpp>
9 #include <boost/graph/adjacency_matrix.hpp>
10 #include <boost/graph/small_world_generator.hpp>
11 #include <boost/graph/random.hpp>
12 #include <boost/property_map/dynamic_property_map.hpp>
14 #include <spdlog/spdlog.h>
42 template <
typename Graph>
45 using vertices_size_type =
typename boost::graph_traits<Graph>::vertices_size_type;
46 using namespace boost;
53 if (is_undirected(g)){
54 for (vertices_size_type v = 0; v < n; ++v){
55 for (vertices_size_type k = v+1; k < n; ++k) {
56 add_edge(vertex(v, g), vertex(k, g), g);
62 for (vertices_size_type v = 0; v < n; ++v) {
63 for (vertices_size_type k = 1; k < n; ++k) {
64 add_edge(vertex(v, g), vertex((v+k)%n, g), g);
103 template<
typename Graph,
typename RNG>
105 const std::size_t mean_degree,
114 const std::size_t num_edges = [&](){
116 return num_vertices * mean_degree;
119 return num_vertices * mean_degree / 2;
125 boost::generate_random_graph(g,
153 template <
typename Graph>
158 using vertices_size_type =
typename boost::graph_traits<Graph>::vertices_size_type;
159 using namespace boost;
162 return create_complete_graph<Graph>(n);
168 if (is_undirected(g) && k % 2){
169 throw std::invalid_argument(
"For undirected regular graphs, the mean "
170 "degree needs to be even!");
174 throw std::invalid_argument(
"For directed regular graphs, the mean "
175 "degree can only be uneven if the graph is oriented! Set "
176 "'oriented = true', or choose an even mean degree.");
187 if (is_undirected(g)){
188 for (vertices_size_type v = 0; v < n; ++v) {
189 for (vertices_size_type i = 1; i <= k/2; ++i) {
190 add_edge(vertex(v, g), vertex((v + i) % n, g), g);
198 for (vertices_size_type v = 0; v < n; ++v) {
199 for (vertices_size_type i = 1; i <= k/2; ++i) {
201 add_edge(vertex(v, g), vertex((v + i) % n, g), g);
203 add_edge(vertex(v, g), vertex((v - i + n) % n, g), g);
209 for (vertices_size_type v = 0; v < n; ++v) {
210 for (vertices_size_type i = 1; i <= k; ++i) {
211 add_edge(vertex(v, g), vertex((v + i) % n, g), g);
243 template <
typename Graph,
typename RNG>
245 const std::size_t mean_degree,
249 using vertex_size_type =
typename boost::graph_traits<Graph>::vertices_size_type;
250 using vertex_desc_type =
typename boost::graph_traits<Graph>::vertex_descriptor;
251 using namespace boost;
253 if ( mu > 1. or mu < 0.) {
254 throw std::invalid_argument(
"The parameter 'mu' must be a probability!");
256 else if ( mean_degree <= 2 ) {
257 throw std::invalid_argument(
"This algorithm requires a mean degree of"
262 if (mean_degree >= num_vertices-1) {
263 return create_complete_graph<Graph>(num_vertices);
267 std::uniform_real_distribution<double> distr(0, 1);
271 Graph g{num_vertices};
277 const std::size_t m = [&]() -> std::size_t {
278 if (is_undirected(g)) {
279 double a = sqrt(4.0*pow(num_vertices, 2)
280 - 4.0*num_vertices*(mean_degree+1.0) +1.0);
282 a += num_vertices - 0.5;
284 return std::round(a);
288 num_vertices *
static_cast<double>(mean_degree) / (2*(num_vertices-1))
295 const auto log = spdlog::get(
"core");
297 throw std::runtime_error(
298 "Logger 'core' was not set up but is needed for "
299 "Utopia::Graph::create_KlemmEguiluz_graph !"
303 auto actual_mean_degree = [&](){
304 if (is_undirected(g)) {
305 return (1.0*m*(m-1)+2.0*m*(num_vertices-m))/num_vertices;
308 return (2.0*m*(m-1)+2.0*m*(num_vertices-m))/num_vertices;
311 log->info(
"The desired mean degree of this graph is {}; the actual mean"
312 " degree of this graph will be {}.", mean_degree, actual_mean_degree());
316 std::vector<vertex_desc_type> actives;
321 std::vector<std::pair<size_t, size_t>> degrees_and_num;
325 const size_t num_deg = is_undirected(g) ? num_vertices : 2*(num_vertices);
326 std::vector<std::deque<vertex_desc_type>> vertices_by_deg(num_deg,
327 std::deque<vertex_desc_type>());
331 for (vertex_size_type i = 0; i < m; ++i) {
332 if (is_undirected(g)) {
333 for (vertex_size_type k = i+1; k < m; ++k) {
334 add_edge(vertex(i, g), vertex(k, g), g);
338 for (vertex_size_type k = 1; k < m; ++k) {
339 add_edge(vertex(i, g), vertex((i+k)%m, g), g);
346 if (is_undirected(g)){
347 vertices_by_deg[m-1].emplace_back(vertex(i, g));
350 vertices_by_deg[2*(m-1)].emplace_back(vertex(i, g));
354 actives.emplace_back(vertex(i, g));
363 if (mu == 1 and is_undirected(g)) {
364 degrees_and_num.emplace_back(std::make_pair(m-1, m));
365 degrees_and_num.emplace_back(std::make_pair(m, 0));
368 degrees_and_num.emplace_back(std::make_pair(m, 0));
369 degrees_and_num.emplace_back(std::make_pair(2*(m-1), m));
374 auto get_degree = [&](
const size_t norm) -> std::pair<std::size_t, std::size_t>
376 const double prob = distr(
rng) * norm;
377 double cumulative_prob = 0;
378 std::pair<std::size_t, size_t> res = {0, 0};
379 for (std::size_t i = 0; i<degrees_and_num.size(); ++i) {
380 cumulative_prob += degrees_and_num[i].first * degrees_and_num[i].second;
381 if (cumulative_prob >= prob) {
382 res = {degrees_and_num[i].first, i};
392 std::size_t norm = is_undirected(g) ? m*(m-1) : m*2*(m-1);
393 for (vertex_size_type n = m; n < num_vertices; ++n) {
394 const auto v = vertex(n, g);
395 for (std::size_t i = 0; i < m; ++i) {
398 auto d = get_degree(norm);
399 std::size_t idx = distr(
rng) * (vertices_by_deg[d.first].size());
400 auto w = vertices_by_deg[d.first][idx];
401 while (w == v or edge(v, w, g).second){
402 d = get_degree(norm);
403 idx = distr(
rng) * (vertices_by_deg[d.first].size());
404 w = vertices_by_deg[d.first][idx];
410 --degrees_and_num[d.second].second;
411 if (d.second < degrees_and_num.size()-1
412 and degrees_and_num[d.second+1].first == d.first+1)
414 ++degrees_and_num[d.second+1].second;
417 degrees_and_num.emplace_back(std::make_pair(d.first+1, 1));
419 degrees_and_num.begin(), degrees_and_num.end(),
420 [](
const auto& a,
const auto& b) ->
bool {
421 return a.first < b.first;
430 vertices_by_deg[d.first].erase(vertices_by_deg[d.first].begin()+idx);
431 vertices_by_deg[d.first+1].emplace_back(w);
438 vertices_by_deg[m].emplace_back(v);
441 if (is_undirected(g)) {
442 degrees_and_num[1].second++;
445 degrees_and_num[0].second++;
452 std::size_t norm = 0;
455 for (vertex_size_type n = m; n < num_vertices; ++n) {
456 const auto v = vertex(n, g);
461 for (
auto const& a : actives) {
469 for (
auto const& a : actives) {
470 if (distr(
rng) < mu and n != m) {
475 std::size_t max_attempts = n-m+2;
479 auto d = get_degree(norm);
480 std::size_t idx = distr(
rng) * (vertices_by_deg[d.first].size());
481 auto w = vertices_by_deg[d.first][idx];
482 while (edge(v, w, g).second and max_attempts > 0){
483 d = get_degree(norm);
484 idx = distr(
rng) * (vertices_by_deg[d.first].size());
485 w = vertices_by_deg[d.first][idx];
492 if (max_attempts > 0) {
494 --degrees_and_num[d.second].second;
495 if (d.second < degrees_and_num.size()-1
496 and degrees_and_num[d.second+1].first == d.first+1)
498 ++degrees_and_num[d.second+1].second;
501 degrees_and_num.emplace_back(std::make_pair(d.first+1, 1));
503 degrees_and_num.begin(), degrees_and_num.end(),
504 [](
const auto& a,
const auto& b) ->
bool {
505 return a.first < b.first;
514 vertices_by_deg[d.first].erase(vertices_by_deg[d.first].begin()+idx);
515 vertices_by_deg[d.first+1].emplace_back(w);
529 for (
const auto& a : actives) {
530 gamma += in_degree(a, g);
535 const double prob_to_drop = distr(
rng) * gamma;
536 double sum_of_probs = 0;
538 for (vertex_size_type i = 0; i<actives.size(); ++i) {
539 sum_of_probs += in_degree(actives[i], g);
541 if (sum_of_probs >= prob_to_drop) {
542 bool deg_in_array =
false;
546 for (std::size_t k = 0; k<degrees_and_num.size(); ++k) {
547 if (degrees_and_num[k].first == degree(actives[i], g)) {
548 ++degrees_and_num[k].second;
554 if (not deg_in_array) {
555 degrees_and_num.emplace_back(std::make_pair(degree(actives[i], g), 1));
556 sort(degrees_and_num.begin(), degrees_and_num.end(),
557 [](
const auto& a,
const auto& b) ->
bool {
558 return a.first < b.first;
563 norm += degree(actives[i], g);
567 vertices_by_deg[degree(actives[i], g)].emplace_back(actives[i]);
604 template <
typename Graph,
typename RNG>
606 std::size_t mean_degree,
611 typename boost::graph_traits<Graph>::vertex_descriptor;
617 const std::size_t num_new_edges_per_step = mean_degree / 2;
623 for (
unsigned v0 = 0; v0 < mean_degree; ++v0){
624 boost::add_vertex(g);
625 for (
unsigned v1 = 0; v1 < v0; ++v1){
626 boost::add_edge(boost::vertex(v0, g), boost::vertex(v1, g), g);
631 std::vector<VertexDesc> target_vertices (boost::vertices(g).first,
632 boost::vertices(g).second);
635 std::vector<VertexDesc> repeated_vertices{};
638 repeated_vertices.reserve(num_vertices * num_new_edges_per_step * 2);
642 std::size_t counter = boost::num_vertices(g);
645 while (counter < num_vertices)
647 const auto new_vertex = boost::add_vertex(g);
651 for (
auto target : target_vertices){
652 boost::add_edge(new_vertex, target, g);
657 repeated_vertices.push_back(target);
658 repeated_vertices.push_back(new_vertex);
664 target_vertices.clear();
667 std::back_inserter(target_vertices),
668 num_new_edges_per_step,
698 template <
typename Graph,
typename RNG>
700 std::size_t mean_degree,
706 return create_KlemmEguiluz_graph<Graph>(num_vertices, mean_degree, 1.0,
rng);
714 throw std::runtime_error(
"This scale-free generator algorithm "
715 "only works for undirected graphs! "
716 "But the provided graph is directed.");
718 }
else if (num_vertices < mean_degree){
719 throw std::invalid_argument(
"The mean degree has to be smaller than "
720 "the total number of vertices!");
722 else if (mean_degree % 2){
723 throw std::invalid_argument(
"The mean degree needs to be even but "
724 "is not an even number!");
727 return BarabasiAlbert_parallel_generator<Graph>(num_vertices,
763 template <
typename Graph,
typename RNG>
776 if (std::fabs(alpha + beta + gamma - 1.)
777 > std::numeric_limits<double>::epsilon()) {
778 throw std::invalid_argument(
"The probabilities alpha, beta and gamma "
779 "have to add up to 1!");
782 throw std::runtime_error(
"This algorithm only works for directed "
783 "graphs but the graph type specifies an "
784 "undirected graph!");
787 throw std::invalid_argument(
"The probability beta must not be 1!");
791 const auto v0 = boost::add_vertex(g);
792 const auto v1 = boost::add_vertex(g);
793 const auto v2 = boost::add_vertex(g);
794 boost::add_edge(v0, v1, g);
795 boost::add_edge(v1, v2, g);
796 boost::add_edge(v2, v0, g);
798 std::uniform_real_distribution<> distr(0, 1);
801 auto num_edges = boost::num_edges(g);
809 while (boost::num_vertices(g) < num_vertices) {
811 auto v = boost::vertex(0, g);
812 auto w = boost::vertex(0, g);
815 norm_in = num_edges + del_in * boost::num_vertices(g);
816 norm_out = num_edges + del_out * boost::num_vertices(g);
817 const auto rand_num = distr(
rng);
819 if (rand_num < alpha) {
825 const auto r = distr(
rng);
827 for (
auto [p, p_end] = boost::vertices(g); p!=p_end; ++p) {
829 prob_sum += (boost::in_degree(*p, g) + del_in) / norm_in;
835 v = boost::add_vertex(g);
838 else if (rand_num < alpha + beta) {
843 auto prob_sum_in = 0.;
844 auto prob_sum_out = 0.;
845 const auto r_in = distr(
rng);
846 const auto r_out = distr(
rng);
849 for (
auto [p, p_end] = boost::vertices(g); p!=p_end; ++p) {
851 prob_sum_out += (boost::out_degree(*p, g) + del_out)/norm_out;
852 if (r_out < prob_sum_out) {
858 for (
auto [p, p_end] = boost::vertices(g); p!=p_end; ++p) {
860 prob_sum_in += (boost::in_degree(*p, g) + del_in)/norm_in;
861 if (r_in < prob_sum_in) {
862 if (v!=*p and (not boost::edge(v,*p, g).second)) {
880 double prob_sum = 0.;
881 const auto r = distr(
rng);
883 for (
auto [p, p_end] = boost::vertices(g); p!=p_end; ++p) {
885 prob_sum += (boost::out_degree(*p, g) + del_out)/norm_out;
891 w = boost::add_vertex(g);
895 boost::add_edge(v, w, g);
923 template <
typename Graph,
typename RNG>
926 const double p_rewire,
930 using namespace boost;
931 using vertices_size_type =
typename boost::graph_traits<Graph>::vertices_size_type;
935 return create_complete_graph<Graph>(n);
938 std::uniform_real_distribution<double> distr(0, 1);
941 std::uniform_int_distribution<vertices_size_type> random_vertex_id(0, n-1);
946 if (is_undirected(g) && k % 2){
947 throw std::invalid_argument(
"For undirected Watts-Strogatz graphs, the "
948 "mean degree needs to be even!");
952 throw std::invalid_argument(
"For directed Watts-Strogatz graphs, the mean "
953 "degree can only be uneven if the graph is oriented! Set "
954 "'oriented = true', or choose an even mean degree.");
959 else if (p_rewire == 0) {
960 return create_regular_graph<Graph>(n, k, oriented);
964 auto add_edges = [&](vertices_size_type v,
965 const std::size_t limit,
966 const vertices_size_type& lower,
967 const vertices_size_type& upper,
970 for (vertices_size_type i = 1; i <= limit; ++i) {
971 vertices_size_type w;
972 if (distr(
rng) <= p_rewire) {
973 w = random_vertex_id(
rng);
975 ((forward && (w != (v + i) % n ))
976 or (!forward && (w != (v - i + n) % n)))
978 ((upper > lower && (w >= lower && w <= upper))
979 or (upper < lower && (w >= lower or w <= upper))
980 or (edge(vertex(v, g), vertex(w, g), g).second)))
982 w = random_vertex_id(
rng);
984 add_edge(vertex(v, g), vertex(w, g), g);
987 w = forward ? (v + i) % n : (v - i + n) % n;
988 add_edge(vertex(v, g), vertex(w, g), g);
1004 vertices_size_type lower;
1005 vertices_size_type upper;
1008 if (is_undirected(g)){
1009 const std::size_t limit = k/2;
1010 for (vertices_size_type v = 0; v < n; ++v) {
1012 lower = (v - limit + n) % n;
1013 upper = (v + limit) % n;
1014 add_edges(v, limit, lower, upper,
true);
1022 const std::size_t limit = k/2;
1023 for (vertices_size_type v = 0; v < n; ++v) {
1025 lower = (v + n - limit) % n;
1026 upper = (v + limit) % n;
1027 add_edges(v, limit, lower, upper,
true);
1030 lower = (v + n - limit) % n;
1032 add_edges(v, limit, lower, upper,
false);
1038 const std::size_t limit = k;
1039 for (vertices_size_type v = 0; v < n; ++v) {
1042 upper = (v + limit) % n;
1043 add_edges(v, limit, lower, upper,
true);
1073 template<
typename Graph,
typename RNG>
1076 boost::dynamic_properties pmaps
1077 = {boost::ignore_other_properties})
1080 const std::string model = get_as<std::string>(
"model", cfg);
1085 if (model ==
"complete")
1087 return create_complete_graph<Graph>(
1088 get_as<std::size_t>(
"num_vertices", cfg));
1091 else if (model ==
"regular")
1095 const auto& cfg_r = get_as<Config>(
"regular", cfg);
1097 return create_regular_graph<Graph>(
1098 get_as<std::size_t>(
"num_vertices", cfg),
1099 get_as<std::size_t>(
"mean_degree", cfg),
1100 get_as<bool>(
"oriented", cfg_r));
1102 else if (model ==
"ErdosRenyi")
1105 const auto& cfg_ER = get_as<Config>(
"ErdosRenyi", cfg);
1107 return create_ErdosRenyi_graph<Graph>(
1108 get_as<std::size_t>(
"num_vertices", cfg),
1109 get_as<std::size_t>(
"mean_degree", cfg),
1110 get_as<bool>(
"parallel", cfg_ER),
1111 get_as<bool>(
"self_edges", cfg_ER),
1114 else if (model ==
"KlemmEguiluz")
1117 const auto& cfg_KE = get_as<Config>(
"KlemmEguiluz", cfg);
1119 return create_KlemmEguiluz_graph<Graph>(
1120 get_as<std::size_t>(
"num_vertices", cfg),
1121 get_as<std::size_t>(
"mean_degree", cfg),
1122 get_as<double>(
"mu", cfg_KE),
1125 else if (model ==
"WattsStrogatz")
1128 const auto& cfg_WS = get_as<Config>(
"WattsStrogatz", cfg);
1130 return create_WattsStrogatz_graph<Graph>(
1131 get_as<std::size_t>(
"num_vertices", cfg),
1132 get_as<std::size_t>(
"mean_degree", cfg),
1133 get_as<double>(
"p_rewire", cfg_WS),
1134 get_as<bool>(
"oriented", cfg_WS),
1137 else if (model ==
"BarabasiAlbert")
1140 const auto& cfg_BA = get_as<Config>(
"BarabasiAlbert", cfg);
1142 return create_BarabasiAlbert_graph<Graph>(
1143 get_as<std::size_t>(
"num_vertices", cfg),
1144 get_as<std::size_t>(
"mean_degree", cfg),
1145 get_as<bool>(
"parallel", cfg_BA),
1148 else if (model ==
"BollobasRiordan")
1151 const auto& cfg_BR = get_as<Config>(
"BollobasRiordan", cfg);
1153 return create_BollobasRiordan_graph<Graph>(
1154 get_as<std::size_t>(
"num_vertices", cfg),
1155 get_as<double>(
"alpha", cfg_BR),
1156 get_as<double>(
"beta", cfg_BR),
1157 get_as<double>(
"gamma", cfg_BR),
1158 get_as<double>(
"del_in", cfg_BR),
1159 get_as<double>(
"del_out", cfg_BR),
1162 else if (model ==
"load_from_file")
1165 const auto& cfg_lff = get_as<Config>(
"load_from_file", cfg);
1168 return Utopia::DataIO::GraphLoad::load_graph<Graph>(cfg_lff, pmaps);
1172 throw std::invalid_argument(
"The given graph model '" + model +
1173 "'does not exist! Valid options are: "
1174 "'complete', 'regular', 'ErdosRenyi', "
1175 "'WattsStrogatz', 'BarabasiAlbert', "
1176 "'KlemmEguiluz','BollobasRiordan', "
1177 "'load_from_file'.");
Graph create_BarabasiAlbert_graph(std::size_t num_vertices, std::size_t mean_degree, bool parallel, RNG &rng)
Create a Barabási-Albert scale-free graph.
Definition: creation.hh:699
Graph create_complete_graph(const std::size_t n)
Create a complete graph.
Definition: creation.hh:43
Graph create_graph(const Config &cfg, RNG &rng, boost::dynamic_properties pmaps={boost::ignore_other_properties})
Create a graph from a configuration node.
Definition: creation.hh:1074
Graph create_BollobasRiordan_graph(std::size_t num_vertices, double alpha, double beta, double gamma, double del_in, double del_out, RNG &rng)
Create a scale-free directed graph.
Definition: creation.hh:764
Graph create_KlemmEguiluz_graph(const std::size_t num_vertices, const std::size_t mean_degree, const double mu, RNG &rng)
Create a Klemm-Eguíluz scale-free small-world highly-clustered graph.
Definition: creation.hh:244
Graph BarabasiAlbert_parallel_generator(std::size_t num_vertices, std::size_t mean_degree, RNG &rng)
Generate a Barabási-Albert scale-free graph with parallel edges.
Definition: creation.hh:605
Graph create_WattsStrogatz_graph(const std::size_t n, const std::size_t k, const double p_rewire, const bool oriented, RNG &rng)
Create a Watts-Strogatz small-world graph.
Definition: creation.hh:924
Graph create_regular_graph(const std::size_t n, const std::size_t k, const bool oriented)
Create a regular lattice graph.
Definition: creation.hh:154
Graph create_ErdosRenyi_graph(const std::size_t num_vertices, const std::size_t mean_degree, bool allow_parallel, bool self_edges, RNG &rng)
Create a Erdös-Rényi random graph.
Definition: creation.hh:104
std::mt19937 rng
– Type definitions ----------------------------------------------------—
Definition: test_revision.cc:17
constexpr bool is_directed()
Check whether the network type allows for directed edges.
Definition: utils.hh:35
DataIO::Config Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition: types.hh:80