Create a Klemm-Eguíluz scale-free small-world highly-clustered graph.
This function generates a graph using the Klemm-Eguíluz model (Klemm & Eguíluz 2002). The algorithm starts with a small spawning network to which new vertices are added one at a time. Each new vertex receives a connection to mean_degree existing vertices with a probability that is proportional to the number of links of the corresponding vertex. With probability mu, links are instead rewired to a (possibly non-active) vertex, chosen with a probability that is proportional to its degree. Thus, for mu=1 we obtain the Barabasi-Albert linear preferential attachment model.
248{
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;
252
253 if ( mu > 1. or mu < 0.) {
254 throw std::invalid_argument("The parameter 'mu' must be a probability!");
255 }
256 else if ( mean_degree <= 2 ) {
257 throw std::invalid_argument("This algorithm requires a mean degree of"
258 " 3 or more!");
259 }
260
261
262 if (mean_degree >= num_vertices-1) {
263 return create_complete_graph<Graph>(num_vertices);
264 }
265
266
267 std::uniform_real_distribution<double> distr(0, 1);
268
269
270
271 Graph g{num_vertices};
272
273
274
275
276
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);
281 a *= -0.5;
282 a += num_vertices - 0.5;
283
284 return std::round(a);
285 }
286 else {
287 return std::round(
288 num_vertices * static_cast<double>(mean_degree) / (2*(num_vertices-1))
289 );
290 }
291 }();
292
293
294
295 const auto log = spdlog::get("core");
296 if (not log) {
297 throw std::runtime_error(
298 "Logger 'core' was not set up but is needed for "
299 "Utopia::Graph::create_KlemmEguiluz_graph !"
300 );
301 }
302
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;
306 }
307 else {
308 return (2.0*m*(m-1)+2.0*m*(num_vertices-m))/num_vertices;
309 }
310 };
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());
313
314
315
316 std::vector<vertex_desc_type> actives;
317 actives.reserve(m);
318
319
320
321 std::vector<std::pair<size_t, size_t>> degrees_and_num;
322
323
324
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>());
328
329
330
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) {
335 }
336 }
337 else {
338 for (vertex_size_type k = 1; k < m; ++k) {
340 }
341 }
342
343
344
345 if (mu == 1) {
346 if (is_undirected(g)){
347 vertices_by_deg[m-1].emplace_back(
vertex(i, g));
348 }
349 else {
350 vertices_by_deg[2*(m-1)].emplace_back(
vertex(i, g));
351 }
352 }
353 else {
354 actives.emplace_back(
vertex(i, g));
355 }
356 }
357
358
359
360
361
362
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));
366 }
368 degrees_and_num.emplace_back(std::make_pair(m, 0));
369 degrees_and_num.emplace_back(std::make_pair(2*(m-1), m));
370 }
371
372
373
374 auto get_degree = [&](const size_t norm) -> std::pair<std::size_t, std::size_t>
375 {
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};
383 break;
384 }
385 }
386 return res;
387 };
388
389
390 if (mu == 1) {
391
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) {
396
397
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];
405 }
406 add_edge(v, w, g);
407
408
409
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)
413 {
414 ++degrees_and_num[d.second+1].second;
415 }
416 else {
417 degrees_and_num.emplace_back(std::make_pair(d.first+1, 1));
418 std::sort(
419 degrees_and_num.begin(), degrees_and_num.end(),
420 [](const auto& a, const auto& b) -> bool {
421 return a.first < b.first;
422 }
423 );
424 }
425
426
427
428
429 norm += 1;
430 vertices_by_deg[d.first].erase(vertices_by_deg[d.first].begin()+idx);
431 vertices_by_deg[d.first+1].emplace_back(w);
432 }
433
434
435
436
437 norm += m;
438 vertices_by_deg[m].emplace_back(v);
439
440
441 if (is_undirected(g)) {
442 degrees_and_num[1].second++;
443 }
444 else {
445 degrees_and_num[0].second++;
446 }
447 }
448 }
449
450 else {
451
452 std::size_t norm = 0;
453
454
455 for (vertex_size_type n = m; n < num_vertices; ++n) {
456 const auto v =
vertex(n, g);
457
458
459
460 if (mu == 0) {
461 for (auto const& a : actives) {
462 add_edge(v, a, g);
463 }
464 }
465
466
467
468 else {
469 for (auto const& a : actives) {
470 if (distr(rng) < mu and n != m) {
471
472
473
474
475 std::size_t max_attempts = n-m+2;
476
477
478
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];
486 --max_attempts;
487 }
488
489
490
491
492 if (max_attempts > 0) {
493 add_edge(v, w, g);
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)
497 {
498 ++degrees_and_num[d.second+1].second;
499 }
500 else {
501 degrees_and_num.emplace_back(std::make_pair(d.first+1, 1));
502 std::sort(
503 degrees_and_num.begin(), degrees_and_num.end(),
504 [](const auto& a, const auto& b) -> bool {
505 return a.first < b.first;
506 }
507 );
508 }
509
510
511
512
513 norm += 1;
514 vertices_by_deg[d.first].erase(vertices_by_deg[d.first].begin()+idx);
515 vertices_by_deg[d.first+1].emplace_back(w);
516 }
517 else {
518 add_edge(v, a, g);
519 }
520 }
521 else {
522 add_edge(v, a, g);
523 }
524 }
525 }
526
527
528 double gamma = 0;
529 for (const auto& a : actives) {
530 gamma += in_degree(a, g);
531 }
532
533
534
535 const double prob_to_drop = distr(rng) * gamma;
536 double sum_of_probs = 0;
537
538 for (vertex_size_type i = 0; i<actives.size(); ++i) {
539 sum_of_probs += in_degree(actives[i], g);
540
541 if (sum_of_probs >= prob_to_drop) {
542 bool deg_in_array = false;
543
544
545
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;
549 deg_in_array = true;
550 break;
551 }
552 }
553
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;
559 });
560 }
561
562
563 norm += degree(actives[i], g);
564
565
566
567 vertices_by_deg[degree(actives[i], g)].emplace_back(actives[i]);
568
569
570 actives[i] = v;
571 break;
572 }
573 }
574 }
575 }
576
577
578 return g;
579
580}
constexpr bool is_directed()
Check whether the network type allows for directed edges.
Definition utils.hh:35