1 /* -*- mode: C -*- */
2 /* vim:set ts=4 sw=4 sts=4 et: */
3 /*
4 IGraph library.
5 Copyright (C) 2003-2021 The igraph development team
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA
21
22 */
23
24 #include "igraph_games.h"
25
26 #include "igraph_adjlist.h"
27 #include "igraph_constructors.h"
28 #include "igraph_conversion.h"
29 #include "igraph_graphicality.h"
30 #include "igraph_memory.h"
31 #include "igraph_random.h"
32 #include "igraph_vector_ptr.h"
33
34 #include "core/interruption.h"
35 #include "core/set.h"
36
igraph_i_degree_sequence_game_simple(igraph_t * graph,const igraph_vector_t * out_seq,const igraph_vector_t * in_seq)37 static int igraph_i_degree_sequence_game_simple(igraph_t *graph,
38 const igraph_vector_t *out_seq,
39 const igraph_vector_t *in_seq) {
40
41 long int outsum = 0, insum = 0;
42 igraph_bool_t directed = (in_seq != 0 && igraph_vector_size(in_seq) != 0);
43 igraph_bool_t degseq_ok;
44 long int no_of_nodes, no_of_edges;
45 long int *bag1 = 0, *bag2 = 0;
46 long int bagp1 = 0, bagp2 = 0;
47 igraph_vector_t edges = IGRAPH_VECTOR_NULL;
48 long int i, j;
49
50 IGRAPH_CHECK(igraph_is_graphical(out_seq, in_seq, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, °seq_ok));
51 if (!degseq_ok) {
52 IGRAPH_ERROR(in_seq ? "No directed graph can realize the given degree sequences" :
53 "No undirected graph can realize the given degree sequence", IGRAPH_EINVAL);
54 }
55
56 outsum = (long int) igraph_vector_sum(out_seq);
57 if (directed) {
58 insum = (long int) igraph_vector_sum(in_seq);
59 }
60
61 no_of_nodes = igraph_vector_size(out_seq);
62 no_of_edges = directed ? outsum : outsum / 2;
63
64 bag1 = IGRAPH_CALLOC(outsum, long int);
65 if (bag1 == 0) {
66 IGRAPH_ERROR("degree sequence game (simple)", IGRAPH_ENOMEM);
67 }
68 IGRAPH_FINALLY(igraph_free, bag1);
69
70 for (i = 0; i < no_of_nodes; i++) {
71 for (j = 0; j < VECTOR(*out_seq)[i]; j++) {
72 bag1[bagp1++] = i;
73 }
74 }
75 if (directed) {
76 bag2 = IGRAPH_CALLOC(insum, long int);
77 if (bag2 == 0) {
78 IGRAPH_ERROR("degree sequence game (simple)", IGRAPH_ENOMEM);
79 }
80 IGRAPH_FINALLY(igraph_free, bag2);
81 for (i = 0; i < no_of_nodes; i++) {
82 for (j = 0; j < VECTOR(*in_seq)[i]; j++) {
83 bag2[bagp2++] = i;
84 }
85 }
86 }
87
88 IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
89 IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2));
90
91 RNG_BEGIN();
92
93 if (directed) {
94 for (i = 0; i < no_of_edges; i++) {
95 long int from = RNG_INTEGER(0, bagp1 - 1);
96 long int to = RNG_INTEGER(0, bagp2 - 1);
97 igraph_vector_push_back(&edges, bag1[from]); /* safe, already reserved */
98 igraph_vector_push_back(&edges, bag2[to]); /* ditto */
99 bag1[from] = bag1[bagp1 - 1];
100 bag2[to] = bag2[bagp2 - 1];
101 bagp1--; bagp2--;
102 }
103 } else {
104 for (i = 0; i < no_of_edges; i++) {
105 long int from = RNG_INTEGER(0, bagp1 - 1);
106 long int to;
107 igraph_vector_push_back(&edges, bag1[from]); /* safe, already reserved */
108 bag1[from] = bag1[bagp1 - 1];
109 bagp1--;
110 to = RNG_INTEGER(0, bagp1 - 1);
111 igraph_vector_push_back(&edges, bag1[to]); /* ditto */
112 bag1[to] = bag1[bagp1 - 1];
113 bagp1--;
114 }
115 }
116
117 RNG_END();
118
119 IGRAPH_FREE(bag1);
120 IGRAPH_FINALLY_CLEAN(1);
121 if (directed) {
122 IGRAPH_FREE(bag2);
123 IGRAPH_FINALLY_CLEAN(1);
124 }
125
126 IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
127 directed));
128 igraph_vector_destroy(&edges);
129 IGRAPH_FINALLY_CLEAN(1);
130
131 return 0;
132 }
133
igraph_i_degree_sequence_game_no_multiple_undirected(igraph_t * graph,const igraph_vector_t * seq)134 static int igraph_i_degree_sequence_game_no_multiple_undirected(
135 igraph_t *graph, const igraph_vector_t *seq) {
136
137 igraph_vector_t stubs = IGRAPH_VECTOR_NULL;
138 igraph_vector_int_t *neis;
139 igraph_vector_t residual_degrees = IGRAPH_VECTOR_NULL;
140 igraph_set_t incomplete_vertices;
141 igraph_adjlist_t al;
142 igraph_bool_t finished, failed;
143 igraph_integer_t from, to, dummy;
144 long int i, j, k;
145 long int no_of_nodes, outsum = 0;
146 igraph_bool_t degseq_ok;
147
148 IGRAPH_CHECK(igraph_is_graphical(seq, 0, IGRAPH_SIMPLE_SW, °seq_ok));
149 if (!degseq_ok) {
150 IGRAPH_ERROR("No simple undirected graph can realize the given degree sequence",
151 IGRAPH_EINVAL);
152 }
153
154 outsum = (long int) igraph_vector_sum(seq);
155 no_of_nodes = igraph_vector_size(seq);
156
157 /* Allocate required data structures */
158 IGRAPH_CHECK(igraph_adjlist_init_empty(&al, (igraph_integer_t) no_of_nodes));
159 IGRAPH_FINALLY(igraph_adjlist_destroy, &al);
160 IGRAPH_VECTOR_INIT_FINALLY(&stubs, 0);
161 IGRAPH_CHECK(igraph_vector_reserve(&stubs, outsum));
162 IGRAPH_VECTOR_INIT_FINALLY(&residual_degrees, no_of_nodes);
163 IGRAPH_CHECK(igraph_set_init(&incomplete_vertices, 0));
164 IGRAPH_FINALLY(igraph_set_destroy, &incomplete_vertices);
165
166 /* Start the RNG */
167 RNG_BEGIN();
168
169 /* Outer loop; this will try to construct a graph several times from scratch
170 * until it finally succeeds. */
171 finished = 0;
172 while (!finished) {
173 IGRAPH_ALLOW_INTERRUPTION();
174
175 /* Be optimistic :) */
176 failed = 0;
177
178 /* Clear the adjacency list to get rid of the previous attempt (if any) */
179 igraph_adjlist_clear(&al);
180
181 /* Initialize the residual degrees from the degree sequence */
182 IGRAPH_CHECK(igraph_vector_update(&residual_degrees, seq));
183
184 /* While there are some unconnected stubs left... */
185 while (!finished && !failed) {
186 /* Construct the initial stub vector */
187 igraph_vector_clear(&stubs);
188 for (i = 0; i < no_of_nodes; i++) {
189 for (j = 0; j < VECTOR(residual_degrees)[i]; j++) {
190 igraph_vector_push_back(&stubs, i);
191 }
192 }
193
194 /* Clear the skipped stub counters and the set of incomplete vertices */
195 igraph_vector_null(&residual_degrees);
196 igraph_set_clear(&incomplete_vertices);
197
198 /* Shuffle the stubs in-place */
199 igraph_vector_shuffle(&stubs);
200
201 /* Connect the stubs where possible */
202 k = igraph_vector_size(&stubs);
203 for (i = 0; i < k; ) {
204 from = (igraph_integer_t) VECTOR(stubs)[i++];
205 to = (igraph_integer_t) VECTOR(stubs)[i++];
206
207 if (from > to) {
208 dummy = from; from = to; to = dummy;
209 }
210
211 neis = igraph_adjlist_get(&al, from);
212 if (from == to || igraph_vector_int_binsearch(neis, to, &j)) {
213 /* Edge exists already */
214 VECTOR(residual_degrees)[from]++;
215 VECTOR(residual_degrees)[to]++;
216 IGRAPH_CHECK(igraph_set_add(&incomplete_vertices, from));
217 IGRAPH_CHECK(igraph_set_add(&incomplete_vertices, to));
218 } else {
219 /* Insert the edge */
220 IGRAPH_CHECK(igraph_vector_int_insert(neis, j, to));
221 }
222 }
223
224 finished = igraph_set_empty(&incomplete_vertices);
225
226 if (!finished) {
227 /* We are not done yet; check if the remaining stubs are feasible. This
228 * is done by enumerating all possible pairs and checking whether at
229 * least one feasible pair is found. */
230 i = 0;
231 failed = 1;
232 while (failed && igraph_set_iterate(&incomplete_vertices, &i, &from)) {
233 j = 0;
234 while (igraph_set_iterate(&incomplete_vertices, &j, &to)) {
235 if (from == to) {
236 /* This is used to ensure that each pair is checked once only */
237 break;
238 }
239 if (from > to) {
240 dummy = from; from = to; to = dummy;
241 }
242 neis = igraph_adjlist_get(&al, from);
243 if (!igraph_vector_int_binsearch(neis, to, 0)) {
244 /* Found a suitable pair, so we can continue */
245 failed = 0;
246 break;
247 }
248 }
249 }
250 }
251 }
252 }
253
254 /* Finish the RNG */
255 RNG_END();
256
257 /* Clean up */
258 igraph_set_destroy(&incomplete_vertices);
259 igraph_vector_destroy(&residual_degrees);
260 igraph_vector_destroy(&stubs);
261 IGRAPH_FINALLY_CLEAN(3);
262
263 /* Create the graph. We cannot use IGRAPH_ALL here for undirected graphs
264 * because we did not add edges in both directions in the adjacency list.
265 * We will use igraph_to_undirected in an extra step. */
266 IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1));
267 IGRAPH_CHECK(igraph_to_undirected(graph, IGRAPH_TO_UNDIRECTED_EACH, 0));
268
269 /* Clear the adjacency list */
270 igraph_adjlist_destroy(&al);
271 IGRAPH_FINALLY_CLEAN(1);
272
273 return IGRAPH_SUCCESS;
274 }
275
igraph_i_degree_sequence_game_no_multiple_directed(igraph_t * graph,const igraph_vector_t * out_seq,const igraph_vector_t * in_seq)276 static int igraph_i_degree_sequence_game_no_multiple_directed(igraph_t *graph,
277 const igraph_vector_t *out_seq, const igraph_vector_t *in_seq) {
278 igraph_adjlist_t al;
279 igraph_bool_t deg_seq_ok, failed, finished;
280 igraph_vector_t in_stubs = IGRAPH_VECTOR_NULL;
281 igraph_vector_t out_stubs = IGRAPH_VECTOR_NULL;
282 igraph_vector_int_t *neis;
283 igraph_vector_t residual_in_degrees = IGRAPH_VECTOR_NULL;
284 igraph_vector_t residual_out_degrees = IGRAPH_VECTOR_NULL;
285 igraph_set_t incomplete_in_vertices;
286 igraph_set_t incomplete_out_vertices;
287 igraph_integer_t from, to;
288 long int i, j, k;
289 long int no_of_nodes, outsum;
290
291 IGRAPH_CHECK(igraph_is_graphical(out_seq, in_seq, IGRAPH_SIMPLE_SW, °_seq_ok));
292 if (!deg_seq_ok) {
293 IGRAPH_ERROR("No simple directed graph can realize the given degree sequence",
294 IGRAPH_EINVAL);
295 }
296
297 outsum = (long int) igraph_vector_sum(out_seq);
298 no_of_nodes = igraph_vector_size(out_seq);
299
300 /* Allocate required data structures */
301 IGRAPH_CHECK(igraph_adjlist_init_empty(&al, (igraph_integer_t) no_of_nodes));
302 IGRAPH_FINALLY(igraph_adjlist_destroy, &al);
303 IGRAPH_VECTOR_INIT_FINALLY(&out_stubs, 0);
304 IGRAPH_CHECK(igraph_vector_reserve(&out_stubs, outsum));
305 IGRAPH_VECTOR_INIT_FINALLY(&in_stubs, 0);
306 IGRAPH_CHECK(igraph_vector_reserve(&in_stubs, outsum));
307 IGRAPH_VECTOR_INIT_FINALLY(&residual_out_degrees, no_of_nodes);
308 IGRAPH_VECTOR_INIT_FINALLY(&residual_in_degrees, no_of_nodes);
309 IGRAPH_CHECK(igraph_set_init(&incomplete_out_vertices, 0));
310 IGRAPH_FINALLY(igraph_set_destroy, &incomplete_out_vertices);
311 IGRAPH_CHECK(igraph_set_init(&incomplete_in_vertices, 0));
312 IGRAPH_FINALLY(igraph_set_destroy, &incomplete_in_vertices);
313
314 /* Start the RNG */
315 RNG_BEGIN();
316
317 /* Outer loop; this will try to construct a graph several times from scratch
318 * until it finally succeeds. */
319 finished = 0;
320 while (!finished) {
321 IGRAPH_ALLOW_INTERRUPTION();
322
323 /* Be optimistic :) */
324 failed = 0;
325
326 /* Clear the adjacency list to get rid of the previous attempt (if any) */
327 igraph_adjlist_clear(&al);
328
329 /* Initialize the residual degrees from the degree sequences */
330 IGRAPH_CHECK(igraph_vector_update(&residual_out_degrees, out_seq));
331 IGRAPH_CHECK(igraph_vector_update(&residual_in_degrees, in_seq));
332
333 /* While there are some unconnected stubs left... */
334 while (!finished && !failed) {
335 /* Construct the initial stub vectors */
336 igraph_vector_clear(&out_stubs);
337 igraph_vector_clear(&in_stubs);
338 for (i = 0; i < no_of_nodes; i++) {
339 for (j = 0; j < VECTOR(residual_out_degrees)[i]; j++) {
340 igraph_vector_push_back(&out_stubs, i);
341 }
342 for (j = 0; j < VECTOR(residual_in_degrees)[i]; j++) {
343 igraph_vector_push_back(&in_stubs, i);
344 }
345 }
346
347 /* Clear the skipped stub counters and the set of incomplete vertices */
348 igraph_vector_null(&residual_out_degrees);
349 igraph_vector_null(&residual_in_degrees);
350 igraph_set_clear(&incomplete_out_vertices);
351 igraph_set_clear(&incomplete_in_vertices);
352
353 /* Shuffle the out-stubs in-place */
354 igraph_vector_shuffle(&out_stubs);
355
356 /* Connect the stubs where possible */
357 k = igraph_vector_size(&out_stubs);
358 for (i = 0; i < k; i++) {
359 from = (igraph_integer_t) VECTOR(out_stubs)[i];
360 to = (igraph_integer_t) VECTOR(in_stubs)[i];
361
362 neis = igraph_adjlist_get(&al, from);
363 if (from == to || igraph_vector_int_binsearch(neis, to, &j)) {
364 /* Edge exists already */
365 VECTOR(residual_out_degrees)[from]++;
366 VECTOR(residual_in_degrees)[to]++;
367 IGRAPH_CHECK(igraph_set_add(&incomplete_out_vertices, from));
368 IGRAPH_CHECK(igraph_set_add(&incomplete_in_vertices, to));
369 } else {
370 /* Insert the edge */
371 IGRAPH_CHECK(igraph_vector_int_insert(neis, j, to));
372 }
373 }
374
375 /* Are we finished? */
376 finished = igraph_set_empty(&incomplete_out_vertices);
377
378 if (!finished) {
379 /* We are not done yet; check if the remaining stubs are feasible. This
380 * is done by enumerating all possible pairs and checking whether at
381 * least one feasible pair is found. */
382 i = 0;
383 failed = 1;
384 while (failed && igraph_set_iterate(&incomplete_out_vertices, &i, &from)) {
385 j = 0;
386 while (igraph_set_iterate(&incomplete_in_vertices, &j, &to)) {
387 neis = igraph_adjlist_get(&al, from);
388 if (from != to && !igraph_vector_int_binsearch(neis, to, 0)) {
389 /* Found a suitable pair, so we can continue */
390 failed = 0;
391 break;
392 }
393 }
394 }
395 }
396 }
397 }
398
399 /* Finish the RNG */
400 RNG_END();
401
402 /* Clean up */
403 igraph_set_destroy(&incomplete_in_vertices);
404 igraph_set_destroy(&incomplete_out_vertices);
405 igraph_vector_destroy(&residual_in_degrees);
406 igraph_vector_destroy(&residual_out_degrees);
407 igraph_vector_destroy(&in_stubs);
408 igraph_vector_destroy(&out_stubs);
409 IGRAPH_FINALLY_CLEAN(6);
410
411 /* Create the graph */
412 IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1));
413
414 /* Clear the adjacency list */
415 igraph_adjlist_destroy(&al);
416 IGRAPH_FINALLY_CLEAN(1);
417
418 return IGRAPH_SUCCESS;
419 }
420
421 /* swap two elements of a vector_int */
422 #define SWAP_INT_ELEM(vec, i, j) \
423 { \
424 igraph_integer_t temp; \
425 temp = VECTOR(vec)[i]; \
426 VECTOR(vec)[i] = VECTOR(vec)[j]; \
427 VECTOR(vec)[j] = temp; \
428 }
429
igraph_i_degree_sequence_game_no_multiple_undirected_uniform(igraph_t * graph,const igraph_vector_t * degseq)430 static int igraph_i_degree_sequence_game_no_multiple_undirected_uniform(igraph_t *graph, const igraph_vector_t *degseq) {
431 igraph_vector_int_t stubs;
432 igraph_vector_t edges;
433 igraph_bool_t degseq_ok;
434 igraph_vector_ptr_t adjlist;
435 long i, j;
436 long vcount, ecount, stub_count;
437
438 IGRAPH_CHECK(igraph_is_graphical(degseq, NULL, IGRAPH_SIMPLE_SW, °seq_ok));
439 if (!degseq_ok) {
440 IGRAPH_ERROR("No simple undirected graph can realize the given degree sequence", IGRAPH_EINVAL);
441 }
442
443 stub_count = (long) igraph_vector_sum(degseq);
444 ecount = stub_count / 2;
445 vcount = igraph_vector_size(degseq);
446
447 IGRAPH_VECTOR_INT_INIT_FINALLY(&stubs, stub_count);
448 IGRAPH_VECTOR_INIT_FINALLY(&edges, stub_count);
449
450 /* Fill stubs vector. */
451 {
452 long k = 0;
453 for (i = 0; i < vcount; ++i) {
454 long deg = (long) VECTOR(*degseq)[i];
455 for (j = 0; j < deg; ++j) {
456 VECTOR(stubs)[k++] = i;
457 }
458 }
459 }
460
461 /* Build an adjacency list in terms of sets; used to check for multi-edges. */
462 IGRAPH_CHECK(igraph_vector_ptr_init(&adjlist, vcount));
463 IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&adjlist, igraph_set_destroy);
464 IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &adjlist);
465 for (i = 0; i < vcount; ++i) {
466 igraph_set_t *set = IGRAPH_CALLOC(1, igraph_set_t);
467 if (! set) {
468 IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM);
469 }
470 IGRAPH_CHECK(igraph_set_init(set, 0));
471 VECTOR(adjlist)[i] = set;
472 IGRAPH_CHECK(igraph_set_reserve(set, (long) VECTOR(*degseq)[i]));
473 }
474
475 RNG_BEGIN();
476
477 for (;;) {
478 igraph_bool_t success = 1;
479
480 /* Shuffle stubs vector with Fisher-Yates and check for self-loops and multi-edges as we go. */
481 for (i = 0; i < ecount; ++i) {
482 long k, from, to;
483
484 k = RNG_INTEGER(2*i, stub_count-1);
485 SWAP_INT_ELEM(stubs, 2*i, k);
486
487 k = RNG_INTEGER(2*i+1, stub_count-1);
488 SWAP_INT_ELEM(stubs, 2*i+1, k);
489
490 from = VECTOR(stubs)[2*i];
491 to = VECTOR(stubs)[2*i+1];
492
493 /* self-loop, fail */
494 if (from == to) {
495 success = 0;
496 break;
497 }
498
499 /* multi-edge, fail */
500 if (igraph_set_contains((igraph_set_t *) VECTOR(adjlist)[to], from)) {
501 success = 0;
502 break;
503 }
504
505 /* sets are already reserved */
506 igraph_set_add((igraph_set_t *) VECTOR(adjlist)[to], from);
507 igraph_set_add((igraph_set_t *) VECTOR(adjlist)[from], to);
508
509 /* register edge */
510 VECTOR(edges)[2 * i] = from;
511 VECTOR(edges)[2 * i + 1] = to;
512 }
513
514 if (success) {
515 break;
516 }
517
518 /* Clear adjacency list. */
519 for (j = 0; j < vcount; ++j) {
520 igraph_set_clear((igraph_set_t *) VECTOR(adjlist)[j]);
521 }
522
523 IGRAPH_ALLOW_INTERRUPTION();
524 }
525
526 RNG_END();
527
528 igraph_vector_ptr_destroy_all(&adjlist);
529 igraph_vector_int_destroy(&stubs);
530 IGRAPH_FINALLY_CLEAN(2);
531
532 IGRAPH_CHECK(igraph_create(graph, &edges, vcount, /* directed = */ 0));
533
534 igraph_vector_destroy(&edges);
535 IGRAPH_FINALLY_CLEAN(1);
536
537 return IGRAPH_SUCCESS;
538 }
539
540
igraph_i_degree_sequence_game_no_multiple_directed_uniform(igraph_t * graph,const igraph_vector_t * out_deg,const igraph_vector_t * in_deg)541 static int igraph_i_degree_sequence_game_no_multiple_directed_uniform(
542 igraph_t *graph, const igraph_vector_t *out_deg, const igraph_vector_t *in_deg) {
543 igraph_vector_int_t out_stubs, in_stubs;
544 igraph_vector_t edges;
545 igraph_bool_t degseq_ok;
546 igraph_vector_ptr_t adjlist;
547 long i, j;
548 long vcount, ecount;
549
550 IGRAPH_CHECK(igraph_is_graphical(out_deg, in_deg, IGRAPH_SIMPLE_SW, °seq_ok));
551 if (!degseq_ok) {
552 IGRAPH_ERROR("No simple directed graph can realize the given degree sequence", IGRAPH_EINVAL);
553 }
554
555 ecount = (long) igraph_vector_sum(out_deg);
556 vcount = igraph_vector_size(out_deg);
557
558 IGRAPH_VECTOR_INT_INIT_FINALLY(&out_stubs, ecount);
559 IGRAPH_VECTOR_INT_INIT_FINALLY(&in_stubs, ecount);
560 IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * ecount);
561
562 /* Fill in- and out-stubs vectors. */
563 {
564 long k = 0, l = 0;
565 for (i = 0; i < vcount; ++i) {
566 long dout, din;
567
568 dout = (long) VECTOR(*out_deg)[i];
569 for (j = 0; j < dout; ++j) {
570 VECTOR(out_stubs)[k++] = i;
571 }
572
573 din = (long) VECTOR(*in_deg)[i];
574 for (j = 0; j < din; ++j) {
575 VECTOR(in_stubs)[l++] = i;
576 }
577 }
578 }
579
580 /* Build an adjacency list in terms of sets; used to check for multi-edges. */
581 IGRAPH_CHECK(igraph_vector_ptr_init(&adjlist, vcount));
582 IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&adjlist, igraph_set_destroy);
583 IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &adjlist);
584 for (i = 0; i < vcount; ++i) {
585 igraph_set_t *set = IGRAPH_CALLOC(1, igraph_set_t);
586 if (! set) {
587 IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM);
588 }
589 IGRAPH_CHECK(igraph_set_init(set, 0));
590 VECTOR(adjlist)[i] = set;
591 IGRAPH_CHECK(igraph_set_reserve(set, (long) VECTOR(*out_deg)[i]));
592 }
593
594 RNG_BEGIN();
595
596 for (;;) {
597 igraph_bool_t success = 1;
598
599 /* Shuffle out-stubs vector with Fisher-Yates and check for self-loops and multi-edges as we go. */
600 for (i = 0; i < ecount; ++i) {
601 long k, from, to;
602 igraph_set_t *set;
603
604 k = RNG_INTEGER(i, ecount-1);
605 SWAP_INT_ELEM(out_stubs, i, k);
606
607 from = VECTOR(out_stubs)[i];
608 to = VECTOR(in_stubs)[i];
609
610 /* self-loop, fail */
611 if (to == from) {
612 success = 0;
613 break;
614 }
615
616 /* multi-edge, fail */
617 set = (igraph_set_t *) VECTOR(adjlist)[from];
618 if (igraph_set_contains(set, to)) {
619 success = 0;
620 break;
621 }
622
623 /* sets are already reserved */
624 igraph_set_add(set, to);
625
626 /* register edge */
627 VECTOR(edges)[2 * i] = from;
628 VECTOR(edges)[2 * i + 1] = to;
629 }
630
631 if (success) {
632 break;
633 }
634
635 /* Clear adjacency list. */
636 for (j = 0; j < vcount; ++j) {
637 igraph_set_clear((igraph_set_t *) VECTOR(adjlist)[j]);
638 }
639
640 IGRAPH_ALLOW_INTERRUPTION();
641 }
642
643 RNG_END();
644
645 igraph_vector_ptr_destroy_all(&adjlist);
646 igraph_vector_int_destroy(&out_stubs);
647 igraph_vector_int_destroy(&in_stubs);
648 IGRAPH_FINALLY_CLEAN(3);
649
650 IGRAPH_CHECK(igraph_create(graph, &edges, vcount, /* directed = */ 1));
651
652 igraph_vector_destroy(&edges);
653 IGRAPH_FINALLY_CLEAN(1);
654
655 return IGRAPH_SUCCESS;
656 }
657
658 #undef SWAP_INT_ELEM
659
660
661 /* This is in gengraph_mr-connected.cpp */
662
663 int igraph_degree_sequence_game_vl(igraph_t *graph,
664 const igraph_vector_t *out_seq,
665 const igraph_vector_t *in_seq);
666
667 /**
668 * \ingroup generators
669 * \function igraph_degree_sequence_game
670 * \brief Generates a random graph with a given degree sequence.
671 *
672 * \param graph Pointer to an uninitialized graph object.
673 * \param out_deg The degree sequence for an undirected graph (if
674 * \p in_seq is \c NULL or of length zero), or the out-degree
675 * sequence of a directed graph (if \p in_deq is not
676 * of length zero).
677 * \param in_deg It is either a zero-length vector or
678 * \c NULL (if an undirected
679 * graph is generated), or the in-degree sequence.
680 * \param method The method to generate the graph. Possible values:
681 * \clist
682 * \cli IGRAPH_DEGSEQ_SIMPLE
683 * This method implements the configuration model.
684 * For undirected graphs, it puts all vertex IDs in a bag
685 * such that the multiplicity of a vertex in the bag is the same as
686 * its degree. Then it draws pairs from the bag until the bag becomes
687 * empty. This method may generate both loop (self) edges and multiple
688 * edges. For directed graphs, the algorithm is basically the same,
689 * but two separate bags are used for the in- and out-degrees.
690 * Undirected graphs are generated with probability proportional to
691 * <code>(\prod_{i<j} A_{ij} ! \prod_i A_{ii} !!)^{-1}</code>,
692 * where \c A denotes the adjacency matrix and <code>!!</code> denotes
693 * the double factorial. Here \c A is assumed to have twice the number of
694 * self-loops on its diagonal.
695 * The corresponding expression for directed graphs is
696 * <code>(\prod_{i,j} A_{ij}!)^{-1}</code>.
697 * Thus the probability of all simple graphs (which only have 0s and 1s
698 * in the adjacency matrix) is the same, while that of
699 * non-simple ones depends on their edge and self-loop multiplicities.
700 * \cli IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE
701 * This method generates simple graphs.
702 * It is similar to \c IGRAPH_DEGSEQ_SIMPLE
703 * but tries to avoid multiple and loop edges and restarts the
704 * generation from scratch if it gets stuck. It can generate all simple
705 * realizations of a degree sequence, but it is not guaranteed
706 * to sample them uniformly. This method is relatively fast and it will
707 * eventually succeed if the provided degree sequence is graphical,
708 * but there is no upper bound on the number of iterations.
709 * \cli IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM
710 * This method is identical to \c IGRAPH_DEGSEQ_SIMPLE, but if the
711 * generated graph is not simple, it rejects it and re-starts the
712 * generation. It generates all simple graphs with the same probability.
713 * \cli IGRAPH_DEGSEQ_VL
714 * This method samples undirected \em connected graphs approximately
715 * uniformly. It is a Monte Carlo method based on degree-preserving
716 * edge swaps.
717 * This generator should be favoured if undirected and connected
718 * graphs are to be generated and execution time is not a concern.
719 * igraph uses the original implementation of Fabien Viger; for the algorithm,
720 * see https://www-complexnetworks.lip6.fr/~latapy/FV/generation.html
721 * and the paper https://arxiv.org/abs/cs/0502085
722 * \endclist
723 * \return Error code:
724 * \c IGRAPH_ENOMEM: there is not enough
725 * memory to perform the operation.
726 * \c IGRAPH_EINVAL: invalid method parameter, or
727 * invalid in- and/or out-degree vectors. The degree vectors
728 * should be non-negative, \p out_deg should sum
729 * up to an even integer for undirected graphs; the length
730 * and sum of \p out_deg and
731 * \p in_deg
732 * should match for directed graphs.
733 *
734 * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges
735 * for \c IGRAPH_DEGSEQ_SIMPLE. The time complexity of the
736 * other modes is not known.
737 *
738 * \sa \ref igraph_barabasi_game(), \ref igraph_erdos_renyi_game(),
739 * \ref igraph_is_graphical()
740 *
741 * \example examples/simple/igraph_degree_sequence_game.c
742 */
743
igraph_degree_sequence_game(igraph_t * graph,const igraph_vector_t * out_deg,const igraph_vector_t * in_deg,igraph_degseq_t method)744 int igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_t *out_deg,
745 const igraph_vector_t *in_deg,
746 igraph_degseq_t method) {
747 if (in_deg && igraph_vector_empty(in_deg) && !igraph_vector_empty(out_deg)) {
748 in_deg = 0;
749 }
750
751 switch (method) {
752 case IGRAPH_DEGSEQ_SIMPLE:
753 return igraph_i_degree_sequence_game_simple(graph, out_deg, in_deg);
754
755 case IGRAPH_DEGSEQ_VL:
756 return igraph_degree_sequence_game_vl(graph, out_deg, in_deg);
757
758 case IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE:
759 if (in_deg == 0) {
760 return igraph_i_degree_sequence_game_no_multiple_undirected(graph, out_deg);
761 } else {
762 return igraph_i_degree_sequence_game_no_multiple_directed(graph, out_deg, in_deg);
763 }
764
765 case IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM:
766 if (in_deg == 0) {
767 return igraph_i_degree_sequence_game_no_multiple_undirected_uniform(graph, out_deg);
768 } else {
769 return igraph_i_degree_sequence_game_no_multiple_directed_uniform(graph, out_deg, in_deg);
770 }
771
772 default:
773 IGRAPH_ERROR("Invalid degree sequence game method", IGRAPH_EINVAL);
774 }
775 }
776