Coverage Report

Created: 2025-12-14 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/misc/degree_sequence.cpp
Line
Count
Source
1
/*
2
  igraph library.
3
  Constructing realizations of degree sequences and bi-degree sequences.
4
  Copyright (C) 2018-2024  The igraph development team <igraph@igraph.org>
5
6
  This program is free software; you can redistribute it and/or modify
7
  it under the terms of the GNU General Public License as published by
8
  the Free Software Foundation; either version 2 of the License, or
9
  (at your option) any later version.
10
11
  This program is distributed in the hope that it will be useful,
12
  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
  GNU General Public License for more details.
15
16
  You should have received a copy of the GNU General Public License
17
  along with this program.  If not, see <https://www.gnu.org/licenses/>.
18
*/
19
20
#include "igraph_constructors.h"
21
22
#include "core/exceptions.h"
23
#include "math/safe_intop.h"
24
#include "misc/graphicality.h"
25
26
#include <vector>
27
#include <list>
28
#include <algorithm>
29
#include <utility>
30
#include <stack>
31
#include <cassert>
32
33
/******************************/
34
/***** Helper constructs ******/
35
/******************************/
36
37
// (vertex, degree) pair
38
struct vd_pair {
39
    igraph_int_t vertex;
40
    igraph_int_t degree;
41
42
    vd_pair() = default;
43
10.9M
    vd_pair(igraph_int_t vertex, igraph_int_t degree) : vertex(vertex), degree(degree) {}
44
};
45
46
// (indegree, outdegree)
47
typedef std::pair<igraph_int_t, igraph_int_t> bidegree;
48
49
// (vertex, bidegree) pair
50
struct vbd_pair {
51
    igraph_int_t vertex;
52
    bidegree degree;
53
54
951k
    vbd_pair(igraph_int_t vertex, bidegree degree) : vertex(vertex), degree(degree) {}
55
};
56
57
// Comparison function for vertex-degree pairs.
58
// Also used for lexicographic sorting of bi-degrees.
59
1.58G
template<typename T> inline bool degree_greater(const T &a, const T &b) {
60
1.58G
    return a.degree > b.degree;
61
1.58G
}
bool degree_greater<vbd_pair>(vbd_pair const&, vbd_pair const&)
Line
Count
Source
59
260M
template<typename T> inline bool degree_greater(const T &a, const T &b) {
60
260M
    return a.degree > b.degree;
61
260M
}
bool degree_greater<vd_pair>(vd_pair const&, vd_pair const&)
Line
Count
Source
59
1.32G
template<typename T> inline bool degree_greater(const T &a, const T &b) {
60
1.32G
    return a.degree > b.degree;
61
1.32G
}
62
63
template<typename T> inline bool degree_less(const T &a, const T &b) {
64
    return a.degree < b.degree;
65
}
66
67
68
/*************************************/
69
/***** Undirected simple graphs ******/
70
/*************************************/
71
72
// "Bucket Node" for nodes of the same degree
73
struct BNode {
74
    igraph_int_t count = 0;
75
    std::stack<vd_pair> nodes;
76
    igraph_int_t next; // next bucket (higher degree)
77
    igraph_int_t prev; // prev bucket (lower degree)
78
79
1.99M
    bool is_empty() const { return count == 0; }
80
};
81
82
struct HavelHakimiList {
83
    igraph_int_t n_buckets; // no of buckets, INCLUDING sentinels
84
    std::vector<BNode> buckets;
85
86
    // Given degree sequence, sets up linked list of BNodes (degree buckets)
87
    // sentinel BNode [0] and [N] as bookends
88
    // O(N)
89
    explicit HavelHakimiList(const igraph_vector_int_t *degseq) :
90
7.36k
        n_buckets(igraph_vector_int_size(degseq)+1), buckets(n_buckets)
91
7.36k
    {
92
7.36k
        igraph_int_t n_nodes = igraph_vector_int_size(degseq);
93
1.38M
        for (igraph_int_t i = 0; i <= n_nodes; i++) {
94
1.37M
            if (i == 0) {
95
7.36k
                buckets[i].prev = -1;
96
1.36M
            } else {
97
1.36M
                buckets[i].prev = i - 1;
98
1.36M
            }
99
100
1.37M
            if (i == n_nodes) {
101
7.36k
                buckets[i].next = -1;
102
1.36M
            } else {
103
1.36M
                buckets[i].next = i + 1;
104
1.36M
            }
105
1.37M
        }
106
107
1.37M
        for (igraph_int_t i = 0; i < n_nodes; i++) {
108
1.36M
            igraph_int_t degree = VECTOR(*degseq)[i];
109
1.36M
            buckets[degree].nodes.push(vd_pair{i, degree});
110
1.36M
            buckets[degree].count++;
111
1.36M
        }
112
7.36k
    }
113
114
    // ----- O(1) convenience functions ----- //
115
162k
    const BNode & head() const { return buckets.front(); }
116
0
    const BNode & tail() const { return buckets.back(); }
117
118
    // gets the largest non-empty bucket below 'degree',
119
    // or 0 if one does not exist
120
701k
    igraph_int_t get_prev(igraph_int_t degree) {
121
701k
        assert(0 < degree && degree <= n_buckets - 1); // upper sentinel allowed as input
122
701k
        igraph_int_t curr = buckets[degree].prev;
123
2.06M
        while (curr > 0 && buckets[curr].is_empty()) {
124
1.36M
            remove_bucket(curr);
125
1.36M
            curr = buckets[degree].prev;
126
1.36M
        }
127
701k
        return curr;
128
701k
    }
129
130
    // returns max degree non-empty bucket,
131
    // or 0 (sentinel) if all buckets are empty
132
560k
    igraph_int_t get_max_bucket() {
133
        // TODO: either change get_prev to take a BNode, or change
134
        // head()/tail() to return integers
135
560k
        return get_prev(n_buckets - 1);
136
560k
    }
137
138
    // returns min degree non-empty bucket,
139
    // or n_buckets - 1 (sentinel) if all buckets are empty
140
134k
    igraph_int_t get_min_bucket() {
141
134k
        igraph_int_t curr = head().next;
142
162k
        while (curr < n_buckets - 1 && buckets[curr].is_empty()) {
143
27.9k
            remove_bucket(curr);
144
27.9k
            curr = head().next;
145
27.9k
        }
146
134k
        return curr;
147
134k
    }
148
149
1.39M
    void remove_bucket(igraph_int_t degree) {
150
        // bounds check and prevent accidental removal of sentinels
151
1.39M
        assert(0 < degree && degree < n_buckets - 1);
152
153
1.39M
        igraph_int_t &prev_idx = buckets[degree].prev;
154
1.39M
        igraph_int_t &next_idx = buckets[degree].next;
155
1.39M
        if (prev_idx != -1) buckets[prev_idx].next = next_idx;
156
1.39M
        if (next_idx != -1) buckets[next_idx].prev = prev_idx;
157
158
1.39M
        prev_idx = -1;
159
1.39M
        next_idx = -1;
160
1.39M
    }
161
162
215k
    void insert_bucket(igraph_int_t degree) {
163
215k
        assert(0 <= degree && degree < n_buckets - 1); // can insert into zero-degree bucket
164
165
215k
        igraph_int_t &prev_idx = buckets[degree].prev;
166
215k
        igraph_int_t &next_idx = buckets[degree].next;
167
168
215k
        if (prev_idx == -1 && next_idx == -1) {
169
35.3k
            next_idx = degree + 1;
170
35.3k
            prev_idx = buckets[next_idx].prev;
171
172
35.3k
            buckets[next_idx].prev = degree;
173
35.3k
            buckets[prev_idx].next = degree;
174
35.3k
        }
175
215k
    }
176
177
215k
    void insert_node(vd_pair node) {
178
215k
        insert_bucket(node.degree); // does nothing if already exists
179
215k
        buckets[node.degree].nodes.push(vd_pair{node.vertex, node.degree});
180
215k
        buckets[node.degree].count++;
181
215k
    }
182
183
30.3k
    bool get_max_node(vd_pair &max_node) {
184
30.3k
        igraph_int_t max_bucket = get_max_bucket();
185
30.3k
        if (max_bucket <= 0) {
186
2.02k
            return false;
187
2.02k
        }
188
28.2k
        max_node = buckets[max_bucket].nodes.top();
189
28.2k
        return true;
190
30.3k
    }
191
192
28.2k
    void remove_max_node() {
193
28.2k
        igraph_int_t max_bucket = get_max_bucket();
194
28.2k
        if (max_bucket <= 0) return;
195
28.2k
        buckets[max_bucket].nodes.pop();
196
28.2k
        buckets[max_bucket].count--;
197
28.2k
    }
198
199
68.0k
    bool get_min_node(vd_pair &min_node) {
200
68.0k
        igraph_int_t min_bucket = get_min_bucket();
201
68.0k
        if (min_bucket >= n_buckets - 1) {
202
2.02k
            return false;
203
2.02k
        }
204
65.9k
        min_node = buckets[min_bucket].nodes.top();
205
65.9k
        return true;
206
68.0k
    }
207
208
65.9k
    void remove_min_node() {
209
65.9k
        igraph_int_t min_bucket = get_min_bucket();
210
65.9k
        if (min_bucket >= n_buckets - 1) return;
211
65.9k
        buckets[min_bucket].nodes.pop();
212
65.9k
        buckets[min_bucket].count--;
213
65.9k
    }
214
215
    // Given degree of selected "hub" node, returns degree many "spoke" nodes to connect to
216
    // amortized O(alpha(n))
217
    igraph_error_t get_spokes(igraph_int_t degree, const igraph_vector_int_t &seq,
218
502k
                              igraph_vector_int_t &spokes) {
219
502k
        std::stack<igraph_int_t> buckets_req; // stack of needed degree buckets
220
502k
        igraph_int_t num_nodes = 0;
221
502k
        igraph_int_t curr = get_max_bucket(); // starts with max_bucket
222
223
502k
        igraph_vector_int_clear(&spokes);
224
502k
        IGRAPH_CHECK(igraph_vector_int_reserve(&spokes, degree));
225
226
643k
        while (num_nodes < degree && curr > 0) {
227
140k
            num_nodes += buckets[curr].count;
228
140k
            buckets_req.push(curr);
229
140k
            curr = get_prev(curr); // gets next smallest NON-EMPTY bucket
230
140k
        }
231
502k
        if (num_nodes < degree) { // not enough spokes for hub degree
232
1.29k
            IGRAPH_ERROR("The given degree sequence cannot be realized as a simple graph.", IGRAPH_EINVAL);
233
1.29k
        }
234
235
500k
        igraph_int_t num_skip = num_nodes - degree;
236
640k
        while (!buckets_req.empty()) { // starting from the smallest degree
237
139k
            igraph_int_t bucket = buckets_req.top();
238
139k
            buckets_req.pop();
239
240
139k
            igraph_int_t to_get = buckets[bucket].count - num_skip;
241
361k
            while (to_get > 0) {
242
221k
                vd_pair node = buckets[bucket].nodes.top();
243
221k
                if (VECTOR(seq)[node.vertex] != 0) { // if "not marked for removal"
244
215k
                    IGRAPH_CHECK(igraph_vector_int_push_back(&spokes, node.vertex)); // add as spoke
245
246
215k
                    node.degree--;
247
215k
                    insert_node(node); // first, insert into bucket below
248
249
215k
                    buckets[bucket].count--;
250
215k
                    to_get--;
251
215k
                }
252
221k
                buckets[bucket].nodes.pop(); // then pop from original bucket
253
221k
            }
254
139k
            num_skip = 0;
255
139k
        }
256
500k
        return IGRAPH_SUCCESS;
257
500k
    }
258
};
259
260
/*
261
 * This implementation works by grouping nodes by their remaining "stubs" (degrees) into an
262
 * array of "degree buckets" (see struct HavelHakimiList above) - i.e. each bucket holds
263
 * all nodes with that degree. The array runs from index 0 to index N, with 0 and N as
264
 * sentinel buckets (since any graphical sequence for a simple graph will not have degrees
265
 * greater than N - 1, and nodes with degree 0 can be ignored). Thus, only O(V) time is
266
 * needed to allocate each node to its starting degree bucket, after which no re-sorting is
267
 * done - if a node is used up as a "hub", it is simply removed (or lazy deleted if not
268
 * immediately accessible), and if it is chosen as a "spoke", it will only shift down one
269
 * bucket (constant operation).
270
 *
271
 * Additionally, each degree bucket keeps track of its next largest and next smallest degree
272
 * bucket via their index in the array. This makes it very time efficient to find the
273
 * largest and/or smallest nodes as needed for both "hub" and "spoke" nodes (specifically,
274
 * amortized near-constant time).
275
 *
276
 * Below is an example run-through using the degree sequence [3, 2, 3, 1, 1] and the
277
 * IGRAPH_REALIZE_DEGSEQ_SMALLEST method:
278
 *
279
 * Initial list
280
 * [0][1][2][3][4][5] <- degree buckets
281
 *     4  1  2        <- node ID (index in the degseq) in each bucket
282
 *     3     0
283
 *
284
 * By the smallest first method, we must first choose a node with the smallest degree to
285
 * serve as the "hub". get_min_node() retrieves a node from bucket [1], which sentinel [0]
286
 * indicates as its next largest non-empty bucket in its .next field.
287
 * [0][1][2][3][4][5]       4 <- retrieved "hub" node. It is removed from the bucket
288
 *     3  1  2
289
 *           0
290
 *
291
 * Node 4 has degree 1, therefore we need to select 1 "spoke" node to connect it to. Using
292
 * get_max_node(), we go to retrieve a node from bucket [4], which sentinel [5] indicates
293
 * as its next smallest non-empty bucket. Finding [4] empty, the bucket is removed, and
294
 * node 2 is finally retrieved from bucket [3].
295
 * [0][1][2][3][5]          4-2 <- an edge is formed between them
296
 *     3  1  0
297
 *
298
 * After one "stub"/degree of node 2 is used up, it gets shifted down one bucket.
299
 * [0][1][2][3][5]          4-2
300
 *     3  2  0
301
 *        1
302
 *
303
 * The process repeats. We look at the smallest degree bucket for a "hub" and remove it.
304
 * [0][1][2][3][5]          4-2
305
 *        2  0              3
306
 *        1
307
 *
308
 * Node 3 has degree 1, so we pick 1 "spoke", and replace it into the bucket below.
309
 * [0][1][2][3][5]          4-2
310
 *        0                 3-0
311
 *        2
312
 *        1
313
 *
314
 * The process continues until we are unable to find either a non-zero "hub" node
315
 * (algorithm complete and sequence is graphical) or enough non-zero "spoke" nodes once a
316
 * hub has been selected (sequence is non-graphical).
317
 */
318
static igraph_error_t igraph_i_havel_hakimi(const igraph_vector_int_t *degseq,
319
                                            igraph_vector_int_t *edges,
320
7.92k
                                            igraph_realize_degseq_t method) {
321
7.92k
    igraph_int_t n_nodes = igraph_vector_int_size(degseq);
322
323
    // ----- upfront error/graphicality checks ----- //
324
7.92k
    if (n_nodes == 0 || (n_nodes == 1 && VECTOR(*degseq)[0] == 0)) {
325
12
        return IGRAPH_SUCCESS;
326
12
    }
327
328
1.37M
    for (igraph_int_t i = 0; i < n_nodes; i++) {
329
1.36M
        igraph_int_t deg = VECTOR(*degseq)[i];
330
1.36M
        if (deg >= n_nodes) {
331
555
            IGRAPH_ERROR("The given degree sequence cannot be realized as a simple graph.", IGRAPH_EINVAL);
332
555
        }
333
1.36M
    }
334
335
    // ----- main Havel-Hakimi loop ----- //
336
    // O(V + alpha(V) * E)
337
    // O(V + E) for the LARGEST_FIRST method
338
7.36k
    igraph_vector_int_t seq;
339
7.36k
    IGRAPH_CHECK(igraph_vector_int_init_copy(&seq, degseq));
340
7.36k
    IGRAPH_FINALLY(igraph_vector_int_destroy, &seq);
341
342
7.36k
    HavelHakimiList vault(&seq);
343
344
7.36k
    igraph_int_t n_edges_added = 0;
345
7.36k
    igraph_vector_int_t spokes;
346
7.36k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&spokes, 0);
347
348
508k
    for (igraph_int_t i = 0; i < n_nodes; i++) {
349
        // hub node selection
350
506k
        vd_pair hub;
351
506k
        if (method == IGRAPH_REALIZE_DEGSEQ_SMALLEST) {
352
68.0k
            if (!vault.get_min_node(/* out param */hub)) break;
353
65.9k
            vault.remove_min_node();
354
65.9k
        }
355
438k
        else if (method == IGRAPH_REALIZE_DEGSEQ_LARGEST) {
356
30.3k
            if (!vault.get_max_node(/* out param */hub)) break;
357
28.2k
            vault.remove_max_node();
358
28.2k
        }
359
408k
        else if (method == IGRAPH_REALIZE_DEGSEQ_INDEX) {
360
408k
            igraph_int_t degree = VECTOR(seq)[i];
361
408k
            hub = vd_pair{i, degree};
362
408k
            vault.buckets[degree].count--;
363
408k
        } else {
364
            // The fatal error is effectively an assertion that this line
365
            // should not be reachable:
366
0
            IGRAPH_FATAL("Invalid degree sequence realization method.");
367
0
        }
368
502k
        VECTOR(seq)[hub.vertex] = 0;
369
370
        // spoke nodes selection
371
502k
        IGRAPH_CHECK(vault.get_spokes(hub.degree, seq, spokes));
372
373
500k
        igraph_int_t n_spokes = igraph_vector_int_size(&spokes);
374
716k
        for (igraph_int_t j = 0; j < n_spokes; j++) {
375
215k
            igraph_int_t spoke_idx = VECTOR(spokes)[j];
376
215k
            VECTOR(*edges)[2*n_edges_added] = hub.vertex;
377
215k
            VECTOR(*edges)[2*n_edges_added + 1] = spoke_idx;
378
215k
            n_edges_added++;
379
380
215k
            VECTOR(seq)[spoke_idx]--;
381
215k
        }
382
500k
    }
383
6.06k
    igraph_vector_int_destroy(&spokes);
384
6.06k
    igraph_vector_int_destroy(&seq);
385
6.06k
    IGRAPH_FINALLY_CLEAN(2);
386
6.06k
    return IGRAPH_SUCCESS;
387
7.36k
}
388
389
/***********************************/
390
/***** Undirected multigraphs ******/
391
/***********************************/
392
393
// Given a sequence that is sorted, except for its first element,
394
// move the first element to the correct position fully sort the sequence.
395
template<typename It, typename Compare>
396
631k
static void bubble_up(It first, It last, Compare comp) {
397
631k
    if (first == last) {
398
0
        return;
399
0
    }
400
631k
    It it = first;
401
631k
    it++;
402
19.6M
    while (it != last) {
403
19.3M
        if (comp(*first, *it)) {
404
334k
            break;
405
18.9M
        } else {
406
18.9M
            std::swap(*first, *it);
407
18.9M
        }
408
18.9M
        first = it;
409
18.9M
        it++;
410
18.9M
    }
411
631k
}
412
413
// In each step, choose a vertex (the largest degree one if largest=true,
414
// the smallest degree one otherwise) and connect it to the largest remaining degree vertex.
415
// This will create a connected loopless multigraph, if one exists.
416
// If loops=true, and a loopless multigraph does not exist, complete the procedure
417
// by adding loops on the last vertex.
418
// If largest=false, and the degree sequence was potentially connected, the resulting
419
// graph will be connected.
420
// O(V * E + V log V)
421
12.8k
static igraph_error_t igraph_i_realize_undirected_multi(const igraph_vector_int_t *deg, igraph_vector_int_t *edges, bool loops, bool largest) {
422
12.8k
    igraph_int_t vcount = igraph_vector_int_size(deg);
423
424
12.8k
    if (vcount == 0) {
425
8
        return IGRAPH_SUCCESS;
426
8
    }
427
428
12.8k
    std::vector<vd_pair> vertices;
429
12.8k
    vertices.reserve(vcount);
430
2.21M
    for (igraph_int_t i = 0; i < vcount; ++i) {
431
2.20M
        igraph_int_t d = VECTOR(*deg)[i];
432
2.20M
        vertices.push_back(vd_pair(i, d));
433
2.20M
    }
434
435
    // Initial sort in non-increasing order.
436
    // O (V log V)
437
12.8k
    std::stable_sort(vertices.begin(), vertices.end(), degree_greater<vd_pair>);
438
439
12.8k
    igraph_int_t ec = 0;
440
2.63M
    while (! vertices.empty()) {
441
        // Remove any zero degrees, and error on negative ones.
442
443
2.62M
        vd_pair &w = vertices.back();
444
445
2.62M
        if (w.degree == 0) {
446
2.20M
            vertices.pop_back();
447
2.20M
            continue;
448
2.20M
        }
449
450
        // If only one vertex remains, then the degree sequence cannot be realized as
451
        // a loopless multigraph. We either complete the graph by adding loops on this vertex
452
        // or throw an error, depending on the 'loops' setting.
453
423k
        if (vertices.size() == 1) {
454
2.04k
            if (loops) {
455
41.4k
                for (igraph_int_t i = 0; i < w.degree / 2; ++i) {
456
40.1k
                    VECTOR(*edges)[2 * ec]   = w.vertex;
457
40.1k
                    VECTOR(*edges)[2 * ec + 1] = w.vertex;
458
40.1k
                    ec++;
459
40.1k
                }
460
1.36k
                break;
461
1.36k
            } else {
462
682
                IGRAPH_ERROR("The given degree sequence cannot be realized as a loopless multigraph.", IGRAPH_EINVAL);
463
682
            }
464
2.04k
        }
465
466
        // At this point we are guaranteed to have at least two remaining vertices.
467
468
421k
        vd_pair *u, *v;
469
421k
        if (largest) {
470
210k
            u = &vertices[0];
471
210k
            v = &vertices[1];
472
210k
        } else {
473
210k
            u = &vertices.front();
474
210k
            v = &vertices.back();
475
210k
        }
476
477
421k
        u->degree -= 1;
478
421k
        v->degree -= 1;
479
480
421k
        VECTOR(*edges)[2*ec]   = u->vertex;
481
421k
        VECTOR(*edges)[2*ec+1] = v->vertex;
482
421k
        ec++;
483
484
        // Now the first element may be out of order.
485
        // If largest=true, the first two elements may be out of order.
486
        // Restore the sorted order using a single step of bubble sort.
487
421k
        if (largest) {
488
210k
            bubble_up(vertices.begin() + 1, vertices.end(), degree_greater<vd_pair>);
489
210k
        }
490
421k
        bubble_up(vertices.begin(), vertices.end(), degree_greater<vd_pair>);
491
421k
    }
492
493
12.1k
    return IGRAPH_SUCCESS;
494
12.8k
}
495
496
// O(V * E + V log V)
497
6.41k
static igraph_error_t igraph_i_realize_undirected_multi_index(const igraph_vector_int_t *deg, igraph_vector_int_t *edges, bool loops) {
498
6.41k
    igraph_int_t vcount = igraph_vector_int_size(deg);
499
500
6.41k
    if (vcount == 0) {
501
4
        return IGRAPH_SUCCESS;
502
4
    }
503
504
6.41k
    typedef std::list<vd_pair> vlist;
505
6.41k
    vlist vertices;
506
1.10M
    for (igraph_int_t i = 0; i < vcount; ++i) {
507
1.10M
        vertices.push_back(vd_pair(i, VECTOR(*deg)[i]));
508
1.10M
    }
509
510
6.41k
    std::vector<vlist::iterator> pointers;
511
6.41k
    pointers.reserve(vcount);
512
1.10M
    for (auto it = vertices.begin(); it != vertices.end(); ++it) {
513
1.10M
        pointers.push_back(it);
514
1.10M
    }
515
516
    // Initial sort
517
6.41k
    vertices.sort(degree_greater<vd_pair>);
518
519
6.41k
    igraph_int_t ec = 0;
520
1.02M
    for (const auto &pt : pointers) {
521
1.02M
        vd_pair vd = *pt;
522
1.02M
        vertices.erase(pt);
523
524
1.23M
        while (vd.degree > 0) {
525
211k
            auto uit = vertices.begin();
526
527
211k
            if (vertices.empty() || uit->degree == 0) {
528
                // We are out of non-zero degree vertices to connect to.
529
1.02k
                if (loops) {
530
20.7k
                    for (igraph_int_t i = 0; i < vd.degree / 2; ++i) {
531
20.0k
                        VECTOR(*edges)[2 * ec]   = vd.vertex;
532
20.0k
                        VECTOR(*edges)[2 * ec + 1] = vd.vertex;
533
20.0k
                        ec++;
534
20.0k
                    }
535
682
                    return IGRAPH_SUCCESS;
536
682
                } else {
537
341
                    IGRAPH_ERROR("The given degree sequence cannot be realized as a loopless multigraph.", IGRAPH_EINVAL);
538
341
                }
539
1.02k
            }
540
541
210k
            vd.degree   -= 1;
542
210k
            uit->degree -= 1;
543
544
210k
            VECTOR(*edges)[2*ec]   = vd.vertex;
545
210k
            VECTOR(*edges)[2*ec+1] = uit->vertex;
546
210k
            ec++;
547
548
            // If there are at least two elements, and the first two are not in order,
549
            // re-sort the list. A possible optimization would be a version of
550
            // bubble_up() that can exchange list nodes instead of swapping their values.
551
210k
            if (vertices.size() > 1) {
552
205k
                auto wit = uit;
553
205k
                ++wit;
554
555
205k
                if (wit->degree > uit->degree) {
556
137k
                    vertices.sort(degree_greater<vd_pair>);
557
137k
                }
558
205k
            }
559
210k
        }
560
1.02M
    }
561
562
5.39k
    return IGRAPH_SUCCESS;
563
6.41k
}
564
565
566
/***********************************/
567
/***** Directed simple graphs ******/
568
/***********************************/
569
570
576k
inline bool is_nonzero_outdeg(const vbd_pair &vd) {
571
576k
    return (vd.degree.second != 0);
572
576k
}
573
574
575
// The below implementations of the Kleitman-Wang algorithm follow the description in https://arxiv.org/abs/0905.4913
576
577
// Realize bi-degree sequence as edge list
578
// If smallest=true, always choose the vertex with "smallest" bi-degree for connecting up next,
579
// otherwise choose the "largest" (based on lexicographic bi-degree ordering).
580
// O(E + V^2 log V)
581
3.67k
static igraph_error_t igraph_i_kleitman_wang(const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, igraph_vector_int_t *edges, bool smallest) {
582
3.67k
    igraph_int_t n = igraph_vector_int_size(indeg); // number of vertices
583
584
3.67k
    igraph_int_t ec = 0; // number of edges added so far
585
586
3.67k
    std::vector<vbd_pair> vertices;
587
3.67k
    vertices.reserve(n);
588
638k
    for (igraph_int_t i = 0; i < n; ++i) {
589
634k
        vertices.push_back(vbd_pair(i, bidegree(VECTOR(*indeg)[i], VECTOR(*outdeg)[i])));
590
634k
    }
591
592
72.2k
    while (true) {
593
        // sort vertices by (in, out) degree pairs in decreasing order
594
        // O(V log V)
595
72.2k
        std::stable_sort(vertices.begin(), vertices.end(), degree_greater<vbd_pair>);
596
597
        // remove (0,0)-degree vertices
598
700k
        while (!vertices.empty() && vertices.back().degree == bidegree(0, 0)) {
599
627k
            vertices.pop_back();
600
627k
        }
601
602
        // if no vertices remain, stop
603
72.2k
        if (vertices.empty()) {
604
2.56k
            break;
605
2.56k
        }
606
607
        // choose a vertex the out-stubs of which will be connected
608
        // note: a vertex with non-zero out-degree is guaranteed to exist
609
        // because there are _some_ non-zero degrees and the sum of in- and out-degrees
610
        // is the same
611
69.6k
        vbd_pair *vdp;
612
        // O(V)
613
69.6k
        if (smallest) {
614
35.7k
            vdp = &*std::find_if(vertices.rbegin(), vertices.rend(), is_nonzero_outdeg);
615
35.7k
        } else {
616
33.9k
            vdp = &*std::find_if(vertices.begin(), vertices.end(), is_nonzero_outdeg);
617
33.9k
        }
618
619
        // are there a sufficient number of other vertices to connect to?
620
69.6k
        if (static_cast<igraph_int_t>(vertices.size()) - 1 < vdp->degree.second) {
621
992
            goto fail;
622
992
        }
623
624
        // create the connections
625
68.6k
        igraph_int_t k = 0;
626
68.6k
        for (auto it = vertices.begin();
627
175k
             k < vdp->degree.second;
628
106k
             ++it) {
629
106k
            if (it->vertex == vdp->vertex) {
630
11.4k
                continue;    // do not create a self-loop
631
11.4k
            }
632
95.2k
            if (--(it->degree.first) < 0) {
633
118
                goto fail;
634
118
            }
635
636
95.1k
            VECTOR(*edges)[2 * (ec + k)] = vdp->vertex;
637
95.1k
            VECTOR(*edges)[2 * (ec + k) + 1] = it->vertex;
638
639
95.1k
            k++;
640
95.1k
        }
641
642
68.5k
        ec += vdp->degree.second;
643
68.5k
        vdp->degree.second = 0;
644
68.5k
    }
645
646
2.56k
    return IGRAPH_SUCCESS;
647
648
1.11k
fail:
649
1.11k
    IGRAPH_ERROR("The given directed degree sequences cannot be realized as a simple graph.", IGRAPH_EINVAL);
650
1.11k
}
651
652
653
// Choose vertices in the order of their IDs.
654
// O(E + V^2 log V)
655
1.83k
static igraph_error_t igraph_i_kleitman_wang_index(const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, igraph_vector_int_t *edges) {
656
1.83k
    igraph_int_t n = igraph_vector_int_size(indeg); // number of vertices
657
658
1.83k
    igraph_int_t ec = 0; // number of edges added so far
659
660
1.83k
    typedef std::list<vbd_pair> vlist;
661
1.83k
    vlist vertices;
662
319k
    for (igraph_int_t i = 0; i < n; ++i) {
663
317k
        vertices.push_back(vbd_pair(i, bidegree(VECTOR(*indeg)[i], VECTOR(*outdeg)[i])));
664
317k
    }
665
666
1.83k
    std::vector<vlist::iterator> pointers;
667
1.83k
    pointers.reserve(n);
668
319k
    for (auto it = vertices.begin(); it != vertices.end(); ++it) {
669
317k
        pointers.push_back(it);
670
317k
    }
671
672
268k
    for (const auto &pt : pointers) {
673
        // sort vertices by (in, out) degree pairs in decreasing order
674
        // note: std::list::sort does a stable sort
675
268k
        vertices.sort(degree_greater<vbd_pair>);
676
677
        // choose a vertex the out-stubs of which will be connected
678
268k
        vbd_pair &vd = *pt;
679
680
268k
        if (vd.degree.second == 0) {
681
234k
            continue;
682
234k
        }
683
684
34.7k
        igraph_int_t k = 0;
685
34.7k
        vlist::iterator it;
686
34.7k
        for (it = vertices.begin();
687
85.3k
             k != vd.degree.second && it != vertices.end();
688
51.0k
             ++it) {
689
51.0k
            if (it->vertex == vd.vertex) {
690
1.67k
                continue;
691
1.67k
            }
692
693
49.4k
            if (--(it->degree.first) < 0) {
694
434
                goto fail;
695
434
            }
696
697
48.9k
            VECTOR(*edges)[2 * (ec + k)] = vd.vertex;
698
48.9k
            VECTOR(*edges)[2 * (ec + k) + 1] = it->vertex;
699
700
48.9k
            ++k;
701
48.9k
        }
702
34.2k
        if (it == vertices.end() && k < vd.degree.second) {
703
121
            goto fail;
704
121
        }
705
706
34.1k
        ec += vd.degree.second;
707
34.1k
        vd.degree.second = 0;
708
34.1k
    }
709
710
1.28k
    return IGRAPH_SUCCESS;
711
712
555
fail:
713
555
    IGRAPH_ERROR("The given directed degree sequences cannot be realized as a simple graph.", IGRAPH_EINVAL);
714
555
}
715
716
717
/**************************/
718
/***** Main functions *****/
719
/**************************/
720
721
static igraph_error_t igraph_i_realize_undirected_degree_sequence(
722
    igraph_t *graph,
723
    const igraph_vector_int_t *deg,
724
    igraph_edge_type_sw_t allowed_edge_types,
725
27.1k
    igraph_realize_degseq_t method) {
726
27.1k
    igraph_int_t node_count = igraph_vector_int_size(deg);
727
27.1k
    igraph_int_t deg_sum;
728
729
27.1k
    IGRAPH_CHECK(igraph_i_safe_vector_int_sum(deg, &deg_sum));
730
731
27.1k
    if (deg_sum % 2 != 0) {
732
0
        IGRAPH_ERROR("The sum of degrees must be even for an undirected graph.", IGRAPH_EINVAL);
733
0
    }
734
735
27.1k
    if (node_count > 0 && igraph_vector_int_min(deg) < 0) {
736
0
        IGRAPH_ERROR("Vertex degrees must be non-negative.", IGRAPH_EINVAL);
737
0
    }
738
739
27.1k
    igraph_vector_int_t edges;
740
27.1k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, deg_sum);
741
742
27.1k
    IGRAPH_HANDLE_EXCEPTIONS_BEGIN;
743
27.1k
    if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) && (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW ) )
744
10.6k
    {
745
10.6k
        switch (method) {
746
3.55k
        case IGRAPH_REALIZE_DEGSEQ_SMALLEST:
747
3.55k
            IGRAPH_CHECK(igraph_i_realize_undirected_multi(deg, &edges, true, false));
748
3.55k
            break;
749
3.55k
        case IGRAPH_REALIZE_DEGSEQ_LARGEST:
750
3.55k
            IGRAPH_CHECK(igraph_i_realize_undirected_multi(deg, &edges, true, true));
751
3.55k
            break;
752
3.55k
        case IGRAPH_REALIZE_DEGSEQ_INDEX:
753
3.55k
            IGRAPH_CHECK(igraph_i_realize_undirected_multi_index(deg, &edges, true));
754
3.55k
            break;
755
3.55k
        default:
756
0
            IGRAPH_ERROR("Invalid degree sequence realization method.", IGRAPH_EINVAL);
757
10.6k
        }
758
10.6k
    }
759
16.5k
    else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) )
760
8.58k
    {
761
8.58k
        switch (method) {
762
2.86k
        case IGRAPH_REALIZE_DEGSEQ_SMALLEST:
763
2.86k
            IGRAPH_CHECK(igraph_i_realize_undirected_multi(deg, &edges, false, false));
764
2.51k
            break;
765
2.86k
        case IGRAPH_REALIZE_DEGSEQ_LARGEST:
766
2.86k
            IGRAPH_CHECK(igraph_i_realize_undirected_multi(deg, &edges, false, true));
767
2.51k
            break;
768
2.86k
        case IGRAPH_REALIZE_DEGSEQ_INDEX:
769
2.86k
            IGRAPH_CHECK(igraph_i_realize_undirected_multi_index(deg, &edges, false));
770
2.51k
            break;
771
2.51k
        default:
772
0
            IGRAPH_ERROR("Invalid degree sequence realization method.", IGRAPH_EINVAL);
773
8.58k
        }
774
8.58k
    }
775
7.92k
    else if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) )
776
0
    {
777
0
        IGRAPH_ERROR("Graph realization with at most one self-loop per vertex is not implemented.", IGRAPH_UNIMPLEMENTED);
778
0
    }
779
7.92k
    else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) )
780
7.92k
    {
781
7.92k
        switch (method) {
782
2.64k
        case IGRAPH_REALIZE_DEGSEQ_SMALLEST:
783
2.64k
            IGRAPH_CHECK(igraph_i_havel_hakimi(deg, &edges, IGRAPH_REALIZE_DEGSEQ_SMALLEST));
784
2.02k
            break;
785
2.64k
        case IGRAPH_REALIZE_DEGSEQ_LARGEST:
786
2.64k
            IGRAPH_CHECK(igraph_i_havel_hakimi(deg, &edges, IGRAPH_REALIZE_DEGSEQ_LARGEST));
787
2.02k
            break;
788
2.64k
        case IGRAPH_REALIZE_DEGSEQ_INDEX:
789
2.64k
            IGRAPH_CHECK(igraph_i_havel_hakimi(deg, &edges, IGRAPH_REALIZE_DEGSEQ_INDEX));
790
2.02k
            break;
791
2.02k
        default:
792
0
            IGRAPH_ERROR("Invalid degree sequence realization method.", IGRAPH_EINVAL);
793
7.92k
        }
794
7.92k
    }
795
0
    else
796
0
    {
797
        /* Remaining cases:
798
         *  - At most one self-loop per vertex but multi-edges between distinct vertices allowed.
799
         *  - At most one edge between distinct vertices but multi-self-loops allowed.
800
         * These cases cannot currently be requested through the documented API,
801
         * so no explanatory error message for now. */
802
0
        return IGRAPH_UNIMPLEMENTED;
803
0
    }
804
27.1k
    IGRAPH_HANDLE_EXCEPTIONS_END;
805
806
24.3k
    IGRAPH_CHECK(igraph_create(graph, &edges, node_count, false));
807
808
24.3k
    igraph_vector_int_destroy(&edges);
809
24.3k
    IGRAPH_FINALLY_CLEAN(1);
810
811
24.3k
    return IGRAPH_SUCCESS;
812
24.3k
}
813
814
815
static igraph_error_t igraph_i_realize_directed_degree_sequence(
816
    igraph_t *graph,
817
    const igraph_vector_int_t *outdeg,
818
    const igraph_vector_int_t *indeg,
819
    igraph_edge_type_sw_t allowed_edge_types,
820
5.50k
    igraph_realize_degseq_t method) {
821
5.50k
    igraph_int_t node_count = igraph_vector_int_size(outdeg);
822
5.50k
    igraph_int_t edge_count, edge_count2, indeg_sum;
823
824
5.50k
    IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outdeg, &edge_count));
825
826
5.50k
    if (igraph_vector_int_size(indeg) != node_count) {
827
0
        IGRAPH_ERROR("In- and out-degree sequences must have the same length.", IGRAPH_EINVAL);
828
0
    }
829
830
5.50k
    IGRAPH_CHECK(igraph_i_safe_vector_int_sum(indeg, &indeg_sum));
831
5.50k
    if (indeg_sum != edge_count) {
832
0
        IGRAPH_ERROR("In- and out-degree sequences do not sum to the same value.", IGRAPH_EINVAL);
833
0
    }
834
835
5.50k
    if (node_count > 0 && (igraph_vector_int_min(outdeg) < 0 || igraph_vector_int_min(indeg) < 0)) {
836
0
        IGRAPH_ERROR("Vertex degrees must be non-negative.", IGRAPH_EINVAL);
837
0
    }
838
839
    /* TODO implement loopless and loopy multigraph case */
840
5.50k
    if (allowed_edge_types != IGRAPH_SIMPLE_SW) {
841
0
        IGRAPH_ERROR("Realizing directed degree sequences as non-simple graphs is not implemented.", IGRAPH_UNIMPLEMENTED);
842
0
    }
843
844
5.50k
    igraph_vector_int_t edges;
845
5.50k
    IGRAPH_SAFE_MULT(edge_count, 2, &edge_count2);
846
5.50k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, edge_count2);
847
848
5.50k
    IGRAPH_HANDLE_EXCEPTIONS_BEGIN;
849
5.50k
    switch (method) {
850
1.83k
    case IGRAPH_REALIZE_DEGSEQ_SMALLEST:
851
1.83k
        IGRAPH_CHECK(igraph_i_kleitman_wang(outdeg, indeg, &edges, true));
852
1.28k
        break;
853
1.83k
    case IGRAPH_REALIZE_DEGSEQ_LARGEST:
854
1.83k
        IGRAPH_CHECK(igraph_i_kleitman_wang(outdeg, indeg, &edges, false));
855
1.28k
        break;
856
1.83k
    case IGRAPH_REALIZE_DEGSEQ_INDEX:
857
1.83k
        IGRAPH_CHECK(igraph_i_kleitman_wang_index(outdeg, indeg, &edges));
858
1.28k
        break;
859
1.28k
    default:
860
0
        IGRAPH_ERROR("Invalid directed degree sequence realization method.", IGRAPH_EINVAL);
861
5.50k
    }
862
5.50k
    IGRAPH_HANDLE_EXCEPTIONS_END;
863
864
3.84k
    IGRAPH_CHECK(igraph_create(graph, &edges, node_count, true));
865
866
3.84k
    igraph_vector_int_destroy(&edges);
867
3.84k
    IGRAPH_FINALLY_CLEAN(1);
868
869
3.84k
    return IGRAPH_SUCCESS;
870
3.84k
}
871
872
873
/**
874
 * \ingroup generators
875
 * \function igraph_realize_degree_sequence
876
 * \brief Generates a graph with the given degree sequence.
877
 *
878
 * This function generates an undirected graph that realizes a given degree
879
 * sequence, or a directed graph that realizes a given pair of out- and
880
 * in-degree sequences.
881
 *
882
 * </para><para>
883
 * Simple undirected graphs are constructed using the Havel-Hakimi algorithm
884
 * (undirected case), or the analogous Kleitman-Wang algorithm (directed case).
885
 * These algorithms work by choosing an arbitrary vertex and connecting all its
886
 * stubs to other vertices of highest degree.  In the directed case, the
887
 * "highest" (in, out) degree pairs are determined based on lexicographic
888
 * ordering. This step is repeated until all degrees have been connected up.
889
 *
890
 * </para><para>
891
 * Loopless multigraphs are generated using an analogous algorithm: an arbitrary
892
 * vertex is chosen, and it is connected with a single connection to a highest
893
 * remaining degee vertex. If self-loops are also allowed, the same algorithm
894
 * is used, but if a non-zero vertex remains at the end of the procedure, the
895
 * graph is completed by adding self-loops to it. Thus, the result will contain
896
 * at most one vertex with self-loops.
897
 *
898
 * </para><para>
899
 * The \c method parameter controls the order in which the vertices to be
900
 * connected are chosen. In the undirected case, \c IGRAPH_REALIZE_DEGSEQ_SMALLEST
901
 * produces a connected graph when one exists. This makes this method suitable
902
 * for constructing trees with a given degree sequence.
903
 *
904
 * </para><para>
905
 * For a undirected simple graph, the time complexity is O(V + alpha(V) * E).
906
 * For an undirected multi graph, the time complexity is O(V * E + V log V).
907
 * For a directed graph, the time complexity is O(E + V^2 log V).
908
 *
909
 * </para><para>
910
 * References:
911
 *
912
 * </para><para>
913
 * V. Havel:
914
 * Poznámka o existenci konečných grafů (A remark on the existence of finite graphs),
915
 * Časopis pro pěstování matematiky 80, 477-480 (1955).
916
 * http://eudml.org/doc/19050
917
 *
918
 * </para><para>
919
 * S. L. Hakimi:
920
 * On Realizability of a Set of Integers as Degrees of the Vertices of a Linear Graph,
921
 * Journal of the SIAM 10, 3 (1962).
922
 * https://www.jstor.org/stable/2098770
923
 *
924
 * </para><para>
925
 * D. J. Kleitman and D. L. Wang:
926
 * Algorithms for Constructing Graphs and Digraphs with Given Valences and Factors,
927
 * Discrete Mathematics 6, 1 (1973).
928
 * https://doi.org/10.1016/0012-365X%2873%2990037-X
929
 *
930
 * P. L. Erdős, I. Miklós, Z. Toroczkai:
931
 * A simple Havel-Hakimi type algorithm to realize graphical degree sequences of directed graphs,
932
 * The Electronic Journal of Combinatorics 17.1 (2010).
933
 * http://eudml.org/doc/227072
934
 *
935
 * </para><para>
936
 * Sz. Horvát and C. D. Modes:
937
 * Connectedness matters: construction and exact random sampling of connected networks (2021).
938
 * https://doi.org/10.1088/2632-072X/abced5
939
 *
940
 * \param graph Pointer to an uninitialized graph object.
941
 * \param outdeg The degree sequence of an undirected graph (if \p indeg is NULL),
942
 *    or the out-degree sequence of a directed graph (if \p indeg is given).
943
 * \param indeg The in-degree sequence of a directed graph. Pass \c NULL to
944
 *    generate an undirected graph.
945
 * \param allowed_edge_types The types of edges to allow in the graph. See \ref
946
 *    igraph_edge_type_sw_t. For directed graphs, only \c IGRAPH_SIMPLE_SW is
947
 *    implemented at this moment.
948
 *    For undirected graphs, the following values are valid:
949
 *        \clist
950
 *          \cli IGRAPH_SIMPLE_SW
951
 *          simple graphs (i.e. no self-loops or multi-edges allowed).
952
 *          \cli IGRAPH_LOOPS_SW
953
 *          single self-loops are allowed, but not multi-edges; currently not implemented.
954
 *          \cli IGRAPH_MULTI_SW
955
 *          multi-edges are allowed, but not self-loops.
956
 *          \cli IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW
957
 *          both self-loops and multi-edges are allowed.
958
 *        \endclist
959
 * \param method The method to generate the graph. Possible values:
960
 *        \clist
961
 *          \cli IGRAPH_REALIZE_DEGSEQ_SMALLEST
962
 *          The vertex with smallest remaining degree is selected first. The
963
 *          result is usually a graph with high negative degree assortativity.
964
 *          In the undirected case, this method is guaranteed to generate a
965
 *          connected graph, regardless of whether multi-edges are allowed,
966
 *          provided that a connected realization exists (see Horvát and Modes,
967
 *          2021, as well as http://szhorvat.net/pelican/hh-connected-graphs.html).
968
 *          This method can be used to construct a tree from its degrees.
969
 *          In the directed case it tends to generate weakly connected graphs,
970
 *          but this is not guaranteed.
971
 *          \cli IGRAPH_REALIZE_DEGSEQ_LARGEST
972
 *          The vertex with the largest remaining degree is selected first. The
973
 *          result is usually a graph with high positive degree assortativity, and
974
 *          is often disconnected.
975
 *          \cli IGRAPH_REALIZE_DEGSEQ_INDEX
976
 *          The vertices are selected in order of their index (i.e. their position
977
 *          in the degree vector). Note that sorting the degree vector and using
978
 *          the \c INDEX method is not equivalent to the \c SMALLEST method above,
979
 *          as \c SMALLEST uses the smallest \em remaining degree for selecting
980
 *          vertices, not the smallest \em initial degree.
981
 *        \endclist
982
 * \return Error code:
983
 *          \clist
984
 *          \cli IGRAPH_UNIMPLEMENTED
985
 *           The requested method is not implemented.
986
 *          \cli IGRAPH_ENOMEM
987
 *           There is not enough memory to perform the operation.
988
 *          \cli IGRAPH_EINVAL
989
 *           Invalid method parameter, or invalid in- and/or out-degree vectors.
990
 *           The degree vectors should be non-negative, the length
991
 *           and sum of \p outdeg and \p indeg should match for directed graphs.
992
 *          \endclist
993
 *
994
 * \sa \ref igraph_is_graphical() to test graphicality without generating a graph;
995
 *     \ref igraph_realize_bipartite_degree_sequence() to create bipartite graphs
996
 *          from two degree sequence;
997
 *     \ref igraph_degree_sequence_game() to generate random graphs with a given
998
 *          degree sequence;
999
 *     \ref igraph_k_regular_game() to generate random regular graphs;
1000
 *     \ref igraph_rewire() to randomly rewire the edges of a graph while
1001
 *          preserving its degree sequence.
1002
 *
1003
 * \example examples/simple/igraph_realize_degree_sequence.c
1004
 */
1005
1006
igraph_error_t igraph_realize_degree_sequence(
1007
        igraph_t *graph,
1008
        const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg,
1009
        igraph_edge_type_sw_t allowed_edge_types,
1010
        igraph_realize_degseq_t method)
1011
32.6k
{
1012
32.6k
    bool directed = indeg != NULL;
1013
1014
32.6k
    if (directed) {
1015
5.50k
        return igraph_i_realize_directed_degree_sequence(graph, outdeg, indeg, allowed_edge_types, method);
1016
27.1k
    } else {
1017
27.1k
        return igraph_i_realize_undirected_degree_sequence(graph, outdeg, allowed_edge_types, method);
1018
27.1k
    }
1019
32.6k
}
1020
1021
1022
// Uses index order to construct an undirected bipartite graph.
1023
// degree1 is considered to range from index [0, len(degree1)[,
1024
// so for this implementation degree1 is always the source degree
1025
// sequence and degree2 is always the dest degree sequence.
1026
static igraph_error_t igraph_i_realize_undirected_bipartite_index(
1027
    igraph_t *graph,
1028
    const igraph_vector_int_t *degree1, const igraph_vector_int_t *degree2,
1029
    igraph_bool_t multiedges
1030
3.67k
) {
1031
3.67k
    igraph_int_t ec = 0; // The number of edges added so far
1032
3.67k
    igraph_int_t n1 = igraph_vector_int_size(degree1);
1033
3.67k
    igraph_int_t n2 = igraph_vector_int_size(degree2);
1034
3.67k
    igraph_vector_int_t edges;
1035
3.67k
    igraph_int_t ds1_sum;
1036
3.67k
    igraph_int_t ds2_sum;
1037
1038
3.67k
    std::vector<vd_pair> vertices1;
1039
3.67k
    std::vector<vd_pair> vertices2;
1040
3.67k
    std::vector<vd_pair> *src_vs = &vertices1;
1041
3.67k
    std::vector<vd_pair> *dest_vs = &vertices2;
1042
1043
3.67k
    IGRAPH_CHECK(igraph_i_safe_vector_int_sum(degree1, &ds1_sum));
1044
3.67k
    IGRAPH_CHECK(igraph_i_safe_vector_int_sum(degree2, &ds2_sum));
1045
1046
3.67k
    if (ds1_sum != ds2_sum) {
1047
0
        goto fail;
1048
0
    }
1049
1050
    // If both degree sequences are empty, it's bigraphical
1051
3.67k
    if (!(n1 == 0 && n2 == 0)) {
1052
3.66k
        if (igraph_vector_int_min(degree1) < 0 || igraph_vector_int_min(degree2) < 0) {
1053
0
            goto fail;
1054
0
        }
1055
3.66k
    }
1056
1057
3.67k
    vertices1.reserve(n1);
1058
3.67k
    vertices2.reserve(n2);
1059
1060
638k
    for (igraph_int_t i = 0; i < n1; i++) {
1061
634k
        vertices1.push_back(vd_pair(i, VECTOR(*degree1)[i]));
1062
634k
    }
1063
638k
    for (igraph_int_t i = 0; i < n2; i++) {
1064
634k
        vertices2.push_back(vd_pair(i + n1, VECTOR(*degree2)[i]));
1065
634k
    }
1066
1067
3.67k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, ds1_sum + ds2_sum);
1068
1069
637k
    while (!vertices1.empty() && !vertices2.empty()) {
1070
        // Go by index, so we start in ds1, so ds2 needs to be sorted.
1071
634k
        std::stable_sort(vertices2.begin(), vertices2.end(), degree_greater<vd_pair>);
1072
        // No sorting of ds1 needed for index case
1073
634k
        vd_pair vd_src = vertices1.front();
1074
        // No multiedges - Take the first vertex, connect to the largest delta in opposite partition
1075
634k
        if (!multiedges) {
1076
            // Remove the source degrees
1077
283k
            src_vs->erase(src_vs->begin());
1078
1079
283k
            if (vd_src.degree == 0) {
1080
248k
                continue;
1081
248k
            }
1082
1083
35.1k
            if (dest_vs->size() < size_t(vd_src.degree)) {
1084
154
                goto fail;
1085
154
            }
1086
1087
84.5k
            for (igraph_int_t i = 0; i < vd_src.degree; i++) {
1088
49.8k
                if ((*dest_vs)[i].degree == 0) {
1089
                    // Not enough non-zero remaining degree vertices in opposite partition.
1090
                    // Not graphical.
1091
248
                    goto fail;
1092
248
                }
1093
1094
49.6k
                (*dest_vs)[i].degree--;
1095
1096
49.6k
                VECTOR(edges)[2*(ec + i)]     = vd_src.vertex;
1097
49.6k
                VECTOR(edges)[2*(ec + i) + 1] = (*dest_vs)[i].vertex;
1098
49.6k
            }
1099
34.7k
            ec += vd_src.degree;
1100
34.7k
        }
1101
        // If multiedges are allowed
1102
350k
        else {
1103
            // If this is the last edge to be created from this vertex, we remove it.
1104
350k
            if (src_vs->front().degree <= 1) {
1105
317k
                src_vs->erase(src_vs->begin());
1106
317k
            } else {
1107
33.5k
                src_vs->front().degree--;
1108
33.5k
            }
1109
1110
350k
            if (vd_src.degree == 0) {
1111
280k
                continue;
1112
280k
            }
1113
1114
69.7k
            if (dest_vs->size() < size_t(1)) {
1115
0
                goto fail;
1116
0
            }
1117
            // We should never decrement below zero, but check just in case.
1118
69.7k
            IGRAPH_ASSERT((*dest_vs)[0].degree - 1 >= 0);
1119
1120
            // Connect to the opposite partition
1121
69.7k
            (*dest_vs)[0].degree--;
1122
1123
69.7k
            VECTOR(edges)[2 * ec] = vd_src.vertex;
1124
69.7k
            VECTOR(edges)[2 * ec + 1] = (*dest_vs)[0].vertex;
1125
69.7k
            ec++;
1126
69.7k
        }
1127
634k
    }
1128
3.26k
    IGRAPH_CHECK(igraph_create(graph, &edges, n1 + n2, false));
1129
1130
3.26k
    igraph_vector_int_destroy(&edges);
1131
3.26k
    IGRAPH_FINALLY_CLEAN(1);
1132
1133
3.26k
    return IGRAPH_SUCCESS;
1134
1135
402
fail:
1136
402
    IGRAPH_ERRORF("The given bidegree sequence cannot be realized as a bipartite %sgraph.",
1137
402
                  IGRAPH_EINVAL, multiedges ? "multi" : "simple ");
1138
402
}
1139
1140
/**
1141
 * \function igraph_realize_bipartite_degree_sequence
1142
 * \brief Generates a bipartite graph with the given bidegree sequence.
1143
 *
1144
 * This function generates a bipartite graph with the given bidegree sequence,
1145
 * using a Havel-Hakimi-like construction algorithm. The order in which vertices
1146
 * are connected up is controlled by the \p method parameter. When using the
1147
 * \c IGRAPH_REALIZE_DEGSEQ_SMALLEST method, it is ensured that the graph will be
1148
 * connected if and only if the given bidegree sequence is potentially connected.
1149
 *
1150
 * </para><para>
1151
 * The vertices of the graph will be ordered so that those having \p degrees1
1152
 * come first, followed by \p degrees2.
1153
 *
1154
 * \param graph Pointer to an uninitialized graph object.
1155
 * \param degrees1 The degree sequence of the first partition.
1156
 * \param degrees2 The degree sequence of the second partition.
1157
 * \param allowed_edge_types The types of edges to allow in the graph.
1158
 *        \clist
1159
 *          \cli IGRAPH_SIMPLE_SW
1160
 *          simple graph (i.e. no multi-edges allowed).
1161
 *          \cli IGRAPH_MULTI_SW
1162
 *          multi-edges are allowed
1163
 *        \endclist
1164
 * \param method Controls the order in which vertices are selected for connection.
1165
 *        Possible values:
1166
 *        \clist
1167
 *          \cli IGRAPH_REALIZE_DEGSEQ_SMALLEST
1168
 *          The vertex with smallest remaining degree is selected first, from either
1169
 *          partition. The result is usually a graph with high negative degree
1170
 *          assortativity. This method is guaranteed to generate a connected graph,
1171
 *          if one exists.
1172
 *          \cli IGRAPH_REALIZE_DEGSEQ_LARGEST
1173
 *          The vertex with the largest remaining degree is selected first, from
1174
 *          either parition. The result is usually a graph with high positive degree
1175
 *          assortativity, and is often disconnected.
1176
 *          \cli IGRAPH_REALIZE_DEGSEQ_INDEX
1177
 *          The vertices are selected in order of their index.
1178
 *         \endclist
1179
 * \return Error code.
1180
 * \sa \ref igraph_is_bigraphical() to test bigraphicality without generating a graph.
1181
 */
1182
1183
igraph_error_t igraph_realize_bipartite_degree_sequence(
1184
    igraph_t *graph,
1185
    const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2,
1186
    const igraph_edge_type_sw_t allowed_edge_types, const igraph_realize_degseq_t method
1187
11.0k
) {
1188
11.0k
    IGRAPH_HANDLE_EXCEPTIONS_BEGIN;
1189
1190
11.0k
    igraph_int_t ec = 0; // The number of edges added so far
1191
11.0k
    igraph_int_t n1 = igraph_vector_int_size(degrees1);
1192
11.0k
    igraph_int_t n2 = igraph_vector_int_size(degrees2);
1193
11.0k
    igraph_vector_int_t edges;
1194
11.0k
    igraph_int_t ds1_sum;
1195
11.0k
    igraph_int_t ds2_sum;
1196
11.0k
    igraph_bool_t multiedges;
1197
11.0k
    igraph_bool_t largest;
1198
11.0k
    std::vector<vd_pair> vertices1;
1199
11.0k
    std::vector<vd_pair> vertices2;
1200
1201
    // Bipartite graphs can't have self loops, so we ignore those.
1202
11.0k
    if (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) {
1203
        // Multiedges allowed
1204
5.50k
        multiedges = true;
1205
5.50k
    } else {
1206
        // No multiedges
1207
5.50k
        multiedges = false;
1208
5.50k
    }
1209
1210
11.0k
    switch (method) {
1211
3.67k
    case IGRAPH_REALIZE_DEGSEQ_SMALLEST:
1212
3.67k
        largest = false;
1213
3.67k
        break;
1214
3.67k
    case IGRAPH_REALIZE_DEGSEQ_LARGEST:
1215
3.67k
        largest = true;
1216
3.67k
        break;
1217
3.67k
    case IGRAPH_REALIZE_DEGSEQ_INDEX:
1218
3.67k
        return igraph_i_realize_undirected_bipartite_index(graph, degrees1, degrees2, multiedges);
1219
0
    default:
1220
0
        IGRAPH_ERROR("Invalid bipartite degree sequence realization method.", IGRAPH_EINVAL);
1221
11.0k
    }
1222
1223
7.34k
    IGRAPH_CHECK(igraph_i_safe_vector_int_sum(degrees1, &ds1_sum));
1224
7.34k
    IGRAPH_CHECK(igraph_i_safe_vector_int_sum(degrees2, &ds2_sum));
1225
1226
    // Degree sequences of the two partitions must sum to the same value
1227
7.34k
    if (ds1_sum != ds2_sum) {
1228
0
        goto fail;
1229
0
    }
1230
1231
    // If both degree sequences are empty, it's bigraphical
1232
7.34k
    if (!(n1 == 0 && n2 == 0)) {
1233
7.33k
        if (igraph_vector_int_min(degrees1) < 0 || igraph_vector_int_min(degrees2) < 0) {
1234
0
            goto fail;
1235
0
        }
1236
7.33k
    }
1237
1238
7.34k
    vertices1.reserve(n1);
1239
7.34k
    vertices2.reserve(n2);
1240
1241
1.27M
    for (igraph_int_t i = 0; i < n1; i++) {
1242
1.26M
        vertices1.push_back(vd_pair(i, VECTOR(*degrees1)[i]));
1243
1.26M
    }
1244
1.27M
    for (igraph_int_t i = 0; i < n2; i++) {
1245
1.26M
        vertices2.push_back(vd_pair(i + n1, VECTOR(*degrees2)[i]));
1246
1.26M
    }
1247
1248
7.34k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, ds1_sum + ds2_sum);
1249
1250
1251
7.34k
    std::vector<vd_pair> *src_vs;
1252
7.34k
    std::vector<vd_pair> *dest_vs;
1253
1254
1.89M
    while (!vertices1.empty() && !vertices2.empty()) {
1255
        // Sort in non-increasing order.
1256
        // Note: for the smallest method, we can skip sorting the smaller ds, minor optimization.
1257
        // (i.e., we only need to sort the dest partition, since we always just remove the back of the min partition)
1258
1.89M
        std::stable_sort(vertices1.begin(), vertices1.end(), degree_greater<vd_pair>);
1259
1.89M
        std::stable_sort(vertices2.begin(), vertices2.end(), degree_greater<vd_pair>);
1260
1261
1.89M
        vd_pair vd_src(-1, -1);
1262
1263
1.89M
        if (!largest) {
1264
1.26M
            vd_pair min1 = vertices1.back();
1265
1.26M
            vd_pair min2 = vertices2.back();
1266
1.26M
            if (min1.degree <= min2.degree) {
1267
651k
                src_vs = &vertices1;
1268
651k
                dest_vs = &vertices2;
1269
651k
            } else {
1270
608k
                src_vs = &vertices2;
1271
608k
                dest_vs = &vertices1;
1272
608k
            }
1273
1274
1.26M
            vd_src = src_vs->back();
1275
1276
1.26M
        } else {
1277
629k
            vd_pair max1 = vertices1.front();
1278
629k
            vd_pair max2 = vertices2.front();
1279
1280
629k
            if (max1.degree >= max2.degree) {
1281
615k
                src_vs = &vertices1;
1282
615k
                dest_vs = &vertices2;
1283
615k
            } else {
1284
13.7k
                src_vs = &vertices2;
1285
13.7k
                dest_vs = &vertices1;
1286
13.7k
            }
1287
1288
629k
            vd_src = src_vs->front();
1289
629k
        }
1290
1291
1.89M
        IGRAPH_ASSERT(vd_src.degree >= 0);
1292
1293
1.89M
        if (!multiedges) {
1294
            // Remove the smallest element
1295
892k
            if (!largest) {
1296
619k
                src_vs->pop_back();
1297
619k
            } else {
1298
                // Remove the largest element.
1299
273k
                src_vs->erase(src_vs->begin());
1300
273k
            }
1301
1302
892k
            if (vd_src.degree == 0) {
1303
816k
                continue;
1304
816k
            }
1305
76.3k
            if (dest_vs->size() < size_t(vd_src.degree)) {
1306
566
                goto fail;
1307
566
            }
1308
175k
            for (igraph_int_t i = 0; i < vd_src.degree; i++) {
1309
                // Decrement the degree of the delta largest vertices in the opposite partition
1310
1311
100k
                if ((*dest_vs)[i].degree == 0) {
1312
                    // Not enough non-zero remaining degree vertices in opposite partition.
1313
                    // Not graphical.
1314
238
                    goto fail;
1315
238
                }
1316
1317
100k
                (*dest_vs)[i].degree--;
1318
1319
100k
                VECTOR(edges)[2 * (ec + i)]     = vd_src.vertex;
1320
100k
                VECTOR(edges)[2 * (ec + i) + 1] = (*dest_vs)[i].vertex;
1321
100k
            }
1322
75.5k
            ec += vd_src.degree;
1323
75.5k
        }
1324
        // If multiedges are allowed
1325
997k
        else {
1326
            // The smallest degree is in the back, and we know it is in vertices1
1327
            // If this is the last edge to be created from this vertex, we remove it.
1328
997k
            if (!largest) {
1329
640k
                if (src_vs->back().degree <= 1) {
1330
620k
                    src_vs->pop_back();
1331
620k
                } else {
1332
                    // Otherwise we decrement its degrees by 1 for the edge we are about to create.
1333
20.2k
                    src_vs->back().degree--;
1334
20.2k
                }
1335
640k
            } else {
1336
356k
                if (src_vs->front().degree <= 1) {
1337
317k
                    src_vs->erase(src_vs->begin());
1338
317k
                } else {
1339
39.4k
                    src_vs->front().degree--;
1340
39.4k
                }
1341
356k
            }
1342
1343
997k
            if (vd_src.degree == 0) {
1344
857k
                continue;
1345
857k
            }
1346
1347
139k
            if (dest_vs->size() < size_t(1)) {
1348
0
                goto fail;
1349
0
            }
1350
            // We should never decrement below zero, but check just in case.
1351
139k
            IGRAPH_ASSERT((*dest_vs)[0].degree - 1 >= 0);
1352
1353
            // Connect to the opposite partition
1354
139k
            (*dest_vs)[0].degree--;
1355
1356
139k
            VECTOR(edges)[2 * ec] = vd_src.vertex;
1357
139k
            VECTOR(edges)[2 * ec + 1] = (*dest_vs)[0].vertex;
1358
139k
            ec++;
1359
139k
        }
1360
1.89M
    }
1361
6.53k
    IGRAPH_CHECK(igraph_create(graph, &edges, n1 + n2, IGRAPH_UNDIRECTED));
1362
1363
6.53k
    igraph_vector_int_destroy(&edges);
1364
6.53k
    IGRAPH_FINALLY_CLEAN(1);
1365
1366
6.53k
    return IGRAPH_SUCCESS;
1367
1368
804
fail:
1369
804
    IGRAPH_ERRORF("The given bidegree sequence cannot be realized as a bipartite %sgraph.",
1370
804
                  IGRAPH_EINVAL, multiedges ? "multi" : "simple ");
1371
1372
804
    IGRAPH_HANDLE_EXCEPTIONS_END;
1373
0
}