Coverage Report

Created: 2025-11-24 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/misc/conversion.c
Line
Count
Source
1
/*
2
   igraph library.
3
   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
4
   334 Harvard street, Cambridge, MA 02139 USA
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, write to the Free Software
18
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
   02110-1301 USA
20
21
*/
22
23
#include "igraph_conversion.h"
24
25
#include "igraph_iterators.h"
26
#include "igraph_interface.h"
27
#include "igraph_attributes.h"
28
#include "igraph_constructors.h"
29
#include "igraph_structural.h"
30
#include "igraph_sparsemat.h"
31
#include "igraph_random.h"
32
33
#include "core/fixed_vectorlist.h"
34
#include "graph/attributes.h"
35
#include "math/safe_intop.h"
36
37
0
#define WEIGHT_OF(eid) (weights ? VECTOR(*weights)[eid] : 1)
38
39
/**
40
 * \ingroup conversion
41
 * \function igraph_get_adjacency
42
 * \brief The adjacency matrix of a graph.
43
 *
44
 * </para><para>
45
 * The result is an adjacency matrix. Entry i, j of the matrix
46
 * contains the number of edges connecting vertex i to vertex j in the unweighted
47
 * case, or the total weight of edges connecting vertex i to vertex j in the
48
 * weighted case.
49
 *
50
 * \param graph Pointer to the graph to convert
51
 * \param res Pointer to an initialized matrix object, it will be
52
 *        resized if needed.
53
 * \param type Constant specifying the type of the adjacency matrix to
54
 *        create for undirected graphs. It is ignored for directed
55
 *        graphs. Possible values:
56
 *        \clist
57
 *        \cli IGRAPH_GET_ADJACENCY_UPPER
58
 *          the upper right triangle of the matrix is used.
59
 *        \cli IGRAPH_GET_ADJACENCY_LOWER
60
 *          the lower left triangle of the matrix is used.
61
 *        \cli IGRAPH_GET_ADJACENCY_BOTH
62
 *          the whole matrix is used, a symmetric matrix is returned
63
 *          if the graph is undirected.
64
 *        \endclist
65
 * \param weights An optional vector containing the weight of each edge
66
 *        in the graph. Supply a null pointer here to make all edges have
67
 *        the same weight of 1.
68
 * \param loops Constant specifying how loop edges should be handled.
69
 *        Possible values:
70
 *        \clist
71
 *        \cli IGRAPH_NO_LOOPS
72
 *          loop edges are ignored and the diagonal of the matrix will contain
73
 *          zeros only
74
 *        \cli IGRAPH_LOOPS_ONCE
75
 *          loop edges are counted once, i.e. a vertex with a single unweighted
76
 *          loop edge will have 1 in the corresponding diagonal entry
77
 *        \cli IGRAPH_LOOPS_TWICE
78
 *          loop edges are counted twice in \em undirected graphs, i.e. a vertex
79
 *          with a single unweighted loop edge in an undirected graph will have
80
 *          2 in the corresponding diagonal entry. Loop edges in directed graphs
81
 *          are still counted as 1. Essentially, this means that the function is
82
 *          counting the incident edge \em stems , which makes more sense when
83
 *          using the adjacency matrix in linear algebra.
84
 *        \endclist
85
 * \return Error code:
86
 *        \c IGRAPH_EINVAL invalid type argument.
87
 *
88
 * \sa \ref igraph_get_adjacency_sparse() if you want a sparse matrix representation
89
 *
90
 * Time complexity: O(|V||V|), |V| is the number of vertices in the graph.
91
 */
92
93
igraph_error_t igraph_get_adjacency(
94
    const igraph_t *graph, igraph_matrix_t *res, igraph_get_adjacency_t type,
95
    const igraph_vector_t *weights, igraph_loops_t loops
96
0
) {
97
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
98
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
99
0
    igraph_bool_t directed = igraph_is_directed(graph);
100
0
    igraph_int_t i, from, to;
101
102
0
    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes));
103
0
    igraph_matrix_null(res);
104
105
0
    if (directed) {
106
0
        for (i = 0; i < no_of_edges; i++) {
107
0
            from = IGRAPH_FROM(graph, i);
108
0
            to = IGRAPH_TO(graph, i);
109
0
            if (from != to || loops != IGRAPH_NO_LOOPS) {
110
0
                MATRIX(*res, from, to) += WEIGHT_OF(i);
111
0
            }
112
0
        }
113
0
    } else if (type == IGRAPH_GET_ADJACENCY_UPPER) {
114
0
        for (i = 0; i < no_of_edges; i++) {
115
0
            from = IGRAPH_FROM(graph, i);
116
0
            to = IGRAPH_TO(graph, i);
117
0
            if (to < from) {
118
0
                MATRIX(*res, to, from) += WEIGHT_OF(i);
119
0
            } else {
120
0
                MATRIX(*res, from, to) += WEIGHT_OF(i);
121
0
            }
122
0
            if (to == from && loops == IGRAPH_LOOPS_TWICE) {
123
0
                MATRIX(*res, to, to) += WEIGHT_OF(i);
124
0
            }
125
0
        }
126
0
    } else if (type == IGRAPH_GET_ADJACENCY_LOWER) {
127
0
        for (i = 0; i < no_of_edges; i++) {
128
0
            from = IGRAPH_FROM(graph, i);
129
0
            to = IGRAPH_TO(graph, i);
130
0
            if (to < from) {
131
0
                MATRIX(*res, from, to) += WEIGHT_OF(i);
132
0
            } else {
133
0
                MATRIX(*res, to, from) += WEIGHT_OF(i);
134
0
            }
135
0
            if (to == from && loops == IGRAPH_LOOPS_TWICE) {
136
0
                MATRIX(*res, to, to) += WEIGHT_OF(i);
137
0
            }
138
0
        }
139
0
    } else if (type == IGRAPH_GET_ADJACENCY_BOTH) {
140
0
        for (i = 0; i < no_of_edges; i++) {
141
0
            from = IGRAPH_FROM(graph, i);
142
0
            to = IGRAPH_TO(graph, i);
143
0
            MATRIX(*res, from, to) += WEIGHT_OF(i);
144
0
            if (from != to || loops == IGRAPH_LOOPS_TWICE) {
145
0
                MATRIX(*res, to, from) += WEIGHT_OF(i);
146
0
            }
147
0
        }
148
0
    } else {
149
0
        IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL);
150
0
    }
151
152
    /* Erase the diagonal if we don't need loop edges */
153
0
    if (loops == IGRAPH_NO_LOOPS) {
154
0
        for (i = 0; i < no_of_nodes; i++) {
155
0
            MATRIX(*res, i, i) = 0;
156
0
        }
157
0
    }
158
159
0
    return IGRAPH_SUCCESS;
160
0
}
161
162
/**
163
 * \function igraph_get_adjacency_sparse
164
 * \brief Returns the adjacency matrix of a graph in a sparse matrix format.
165
 *
166
 * \param graph The input graph.
167
 * \param res Pointer to an \em initialized sparse matrix. The result
168
 *    will be stored here. The matrix will be resized as needed.
169
 * \param type Constant specifying the type of the adjacency matrix to
170
 *        create for undirected graphs. It is ignored for directed
171
 *        graphs. Possible values:
172
 *        \clist
173
 *        \cli IGRAPH_GET_ADJACENCY_UPPER
174
 *          the upper right triangle of the matrix is used.
175
 *        \cli IGRAPH_GET_ADJACENCY_LOWER
176
 *          the lower left triangle of the matrix is used.
177
 *        \cli IGRAPH_GET_ADJACENCY_BOTH
178
 *          the whole matrix is used, a symmetric matrix is returned
179
 *          if the graph is undirected.
180
 *        \endclist
181
 * \param weights An optional vector containing the weight of each edge
182
 *        in the graph. Supply a null pointer here to make all edges have
183
 *        the same weight of 1.
184
 * \param loops Constant specifying how loop edges should be handled.
185
 *        Possible values:
186
 *        \clist
187
 *        \cli IGRAPH_NO_LOOPS
188
 *          loop edges are ignored and the diagonal of the matrix will contain
189
 *          zeros only
190
 *        \cli IGRAPH_LOOPS_ONCE
191
 *          loop edges are counted once, i.e. a vertex with a single unweighted
192
 *          loop edge will have 1 in the corresponding diagonal entry
193
 *        \cli IGRAPH_LOOPS_TWICE
194
 *          loop edges are counted twice in \em undirected graphs, i.e. a vertex
195
 *          with a single unweighted loop edge in an undirected graph will have
196
 *          2 in the corresponding diagonal entry. Loop edges in directed graphs
197
 *          are still counted as 1. Essentially, this means that the function is
198
 *          counting the incident edge \em stems , which makes more sense when
199
 *          using the adjacency matrix in linear algebra.
200
 *        \endclist
201
 * \return Error code:
202
 *        \c IGRAPH_EINVAL invalid type argument.
203
 *
204
 * \sa \ref igraph_get_adjacency(), the dense version of this function.
205
 *
206
 * Time complexity: TODO.
207
 */
208
209
igraph_error_t igraph_get_adjacency_sparse(
210
    const igraph_t *graph, igraph_sparsemat_t *res, igraph_get_adjacency_t type,
211
    const igraph_vector_t *weights, igraph_loops_t loops
212
0
) {
213
214
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
215
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
216
0
    igraph_bool_t directed = igraph_is_directed(graph);
217
0
    igraph_int_t nzmax = directed ? no_of_edges : no_of_edges * 2;
218
0
    igraph_int_t i, from, to;
219
220
0
    IGRAPH_CHECK(igraph_sparsemat_resize(res, no_of_nodes, no_of_nodes, nzmax));
221
222
0
    if (directed) {
223
0
        for (i = 0; i < no_of_edges; i++) {
224
0
            from = IGRAPH_FROM(graph, i);
225
0
            to = IGRAPH_TO(graph, i);
226
0
            if (from != to || loops != IGRAPH_NO_LOOPS) {
227
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i)));
228
0
            }
229
0
        }
230
0
    } else if (type == IGRAPH_GET_ADJACENCY_UPPER) {
231
0
        for (i = 0; i < no_of_edges; i++) {
232
0
            from = IGRAPH_FROM(graph, i);
233
0
            to = IGRAPH_TO(graph, i);
234
0
            if (to < from) {
235
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i)));
236
0
            } else if (to == from) {
237
0
                switch (loops) {
238
0
                    case IGRAPH_LOOPS_ONCE:
239
0
                        IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i)));
240
0
                        break;
241
0
                    case IGRAPH_LOOPS_TWICE:
242
0
                        IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i)));
243
0
                        break;
244
0
                    case IGRAPH_NO_LOOPS:
245
0
                    default:
246
0
                        break;
247
0
                }
248
0
            } else {
249
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i)));
250
0
            }
251
0
        }
252
0
    } else if (type == IGRAPH_GET_ADJACENCY_LOWER) {
253
0
        for (i = 0; i < no_of_edges; i++) {
254
0
            from = IGRAPH_FROM(graph, i);
255
0
            to = IGRAPH_TO(graph, i);
256
0
            if (to < from) {
257
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i)));
258
0
            } else if (to == from) {
259
0
                switch (loops) {
260
0
                    case IGRAPH_LOOPS_ONCE:
261
0
                        IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i)));
262
0
                        break;
263
0
                    case IGRAPH_LOOPS_TWICE:
264
0
                        IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i)));
265
0
                        break;
266
0
                    case IGRAPH_NO_LOOPS:
267
0
                    default:
268
0
                        break;
269
0
                }
270
0
            } else {
271
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i)));
272
0
            }
273
0
        }
274
0
    } else if (type == IGRAPH_GET_ADJACENCY_BOTH) {
275
0
        for (i = 0; i < no_of_edges; i++) {
276
0
            from = IGRAPH_FROM(graph, i);
277
0
            to = IGRAPH_TO(graph, i);
278
0
            if (to == from) {
279
0
                switch (loops) {
280
0
                    case IGRAPH_LOOPS_ONCE:
281
0
                        IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i)));
282
0
                        break;
283
0
                    case IGRAPH_LOOPS_TWICE:
284
0
                        IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i)));
285
0
                        break;
286
0
                    case IGRAPH_NO_LOOPS:
287
0
                    default:
288
0
                        break;
289
0
                }
290
0
            } else {
291
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i)));
292
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i)));
293
0
            }
294
0
        }
295
0
    } else {
296
0
        IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL);
297
0
    }
298
299
0
    return IGRAPH_SUCCESS;
300
0
}
301
302
#undef WEIGHT_OF
303
304
305
/**
306
 * \ingroup conversion
307
 * \function igraph_get_edgelist
308
 * \brief The list of edges in a graph.
309
 *
310
 * The order of the edges is given by the edge IDs.
311
 *
312
 * \param graph Pointer to the graph object
313
 * \param res Pointer to an initialized vector object, it will be
314
 *        resized.
315
 * \param bycol Boolean constant. If true, the edges will be returned
316
 *        columnwise, e.g. the first edge is
317
 *        <code>res[0]->res[|E|]</code>, the second is
318
 *        <code>res[1]->res[|E|+1]</code>, etc. Supply false to get
319
 *        the edge list in a format compatible with \ref igraph_add_edges().
320
 * \return Error code.
321
 *
322
 * \sa \ref igraph_edges() to return the result only for some edge IDs.
323
 *
324
 * Time complexity: O(|E|), the number of edges in the graph.
325
 */
326
327
7.68k
igraph_error_t igraph_get_edgelist(const igraph_t *graph, igraph_vector_int_t *res, igraph_bool_t bycol) {
328
7.68k
    return igraph_edges(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), res, bycol);
329
7.68k
}
330
331
/**
332
 * \function igraph_to_directed
333
 * \brief Convert an undirected graph to a directed one.
334
 *
335
 * If the supplied graph is directed, this function does nothing.
336
 *
337
 * \param graph The graph object to convert.
338
 * \param mode Constant, specifies the details of how exactly the
339
 *        conversion is done. Possible values:
340
 *        \clist
341
 *        \cli IGRAPH_TO_DIRECTED_ARBITRARY
342
 *        The number of edges in the
343
 *        graph stays the same, an arbitrarily directed edge is
344
 *        created for each undirected edge.
345
 *        \cli IGRAPH_TO_DIRECTED_MUTUAL
346
 *        Two directed edges are
347
 *        created for each undirected edge, one in each direction.
348
 *        \cli IGRAPH_TO_DIRECTED_RANDOM
349
 *        Each undirected edge is converted to a randomly oriented
350
 *        directed one.
351
 *        \cli IGRAPH_TO_DIRECTED_ACYCLIC
352
 *        Each undirected edge is converted to a directed edge oriented
353
 *        from a lower index vertex to a higher index one. If no self-loops
354
 *        were present, then the result is a directed acyclic graph.
355
 *        \endclist
356
 * \return Error code.
357
 *
358
 * Time complexity: O(|V|+|E|), the number of vertices plus the number
359
 * of edges.
360
 */
361
362
igraph_error_t igraph_to_directed(igraph_t *graph,
363
0
                       igraph_to_directed_t mode) {
364
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
365
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
366
367
0
    if (igraph_is_directed(graph)) {
368
0
        return IGRAPH_SUCCESS;
369
0
    }
370
371
0
    switch (mode) {
372
0
    case IGRAPH_TO_DIRECTED_ARBITRARY:
373
0
    case IGRAPH_TO_DIRECTED_RANDOM:
374
0
    case IGRAPH_TO_DIRECTED_ACYCLIC:
375
0
      {
376
0
        igraph_t newgraph;
377
0
        igraph_vector_int_t edges;
378
0
        igraph_int_t size = no_of_edges * 2;
379
380
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, size);
381
0
        IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
382
383
0
        if (mode == IGRAPH_TO_DIRECTED_RANDOM) {
384
0
            for (igraph_int_t i=0; i < no_of_edges; ++i) {
385
0
                if (RNG_INTEGER(0,1)) {
386
0
                    igraph_int_t temp = VECTOR(edges)[2*i];
387
0
                    VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1];
388
0
                    VECTOR(edges)[2*i+1] = temp;
389
0
                }
390
0
            }
391
0
        } else if (mode == IGRAPH_TO_DIRECTED_ACYCLIC) {
392
            /* Currently, the endpoints of undirected edges are ordered in the
393
               internal graph datastructure, i.e. it is always true that from < to.
394
               However, it is not guaranteed that this will not be changed in
395
               the future, and this ordering should not be relied on outside of
396
               the implementation of the minimal API in type_indexededgelist.c.
397
398
               Therefore, we order the edge endpoints anyway in the following loop: */
399
0
            for (igraph_int_t i=0; i < no_of_edges; ++i) {
400
0
                if (VECTOR(edges)[2*i] > VECTOR(edges)[2*i+1]) {
401
0
                    igraph_int_t temp = VECTOR(edges)[2*i];
402
0
                    VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1];
403
0
                    VECTOR(edges)[2*i+1] = temp;
404
0
                }
405
0
            }
406
0
        }
407
408
0
        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
409
0
                                   no_of_nodes,
410
0
                                   IGRAPH_DIRECTED));
411
0
        IGRAPH_FINALLY(igraph_destroy, &newgraph);
412
0
        IGRAPH_CHECK(igraph_i_attribute_copy(&newgraph, graph, true, true, true));
413
0
        igraph_vector_int_destroy(&edges);
414
0
        IGRAPH_FINALLY_CLEAN(2);
415
416
0
        igraph_destroy(graph);
417
0
        *graph = newgraph;
418
419
0
        break;
420
0
      }
421
0
    case IGRAPH_TO_DIRECTED_MUTUAL:
422
0
      {
423
0
        igraph_t newgraph;
424
0
        igraph_vector_int_t edges;
425
0
        igraph_vector_int_t index;
426
0
        igraph_int_t size;
427
428
0
        IGRAPH_SAFE_MULT(no_of_edges, 4, &size);
429
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0);
430
0
        IGRAPH_CHECK(igraph_vector_int_reserve(&edges, size));
431
0
        IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
432
0
        IGRAPH_CHECK(igraph_vector_int_resize(&edges, size));
433
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_edges * 2);
434
0
        for (igraph_int_t i = 0; i < no_of_edges; i++) {
435
0
            VECTOR(edges)[no_of_edges * 2 + i * 2]  = VECTOR(edges)[i * 2 + 1];
436
0
            VECTOR(edges)[no_of_edges * 2 + i * 2 + 1] = VECTOR(edges)[i * 2];
437
0
            VECTOR(index)[i] = VECTOR(index)[no_of_edges + i] = i;
438
0
        }
439
440
0
        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
441
0
                                   no_of_nodes,
442
0
                                   IGRAPH_DIRECTED));
443
0
        IGRAPH_FINALLY(igraph_destroy, &newgraph);
444
0
        IGRAPH_CHECK(igraph_i_attribute_copy(&newgraph, graph, true, true, /* edges= */ false));
445
0
        IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, &newgraph, &index));
446
447
0
        igraph_vector_int_destroy(&index);
448
0
        igraph_vector_int_destroy(&edges);
449
0
        IGRAPH_FINALLY_CLEAN(3);
450
451
0
        igraph_destroy(graph);
452
0
        *graph = newgraph;
453
454
0
        break;
455
0
      }
456
0
    default:
457
0
        IGRAPH_ERROR("Cannot direct graph, invalid mode.", IGRAPH_EINVAL);
458
0
    }
459
460
0
    return IGRAPH_SUCCESS;
461
0
}
462
463
/**
464
 * \function igraph_to_undirected
465
 * \brief Convert a directed graph to an undirected one.
466
 *
467
 * </para><para>
468
 * If the supplied graph is undirected, this function does nothing.
469
 *
470
 * \param graph The graph object to convert.
471
 * \param mode Constant, specifies the details of how exactly the
472
 *        conversion is done. Possible values: \c
473
 *        IGRAPH_TO_UNDIRECTED_EACH: the number of edges remains
474
 *        constant, an undirected edge is created for each directed
475
 *        one, this version might create graphs with multiple edges;
476
 *        \c IGRAPH_TO_UNDIRECTED_COLLAPSE: one undirected edge will
477
 *        be created for each pair of vertices that are connected
478
 *        with at least one directed edge, no multiple edges will be
479
 *        created. \c IGRAPH_TO_UNDIRECTED_MUTUAL creates an undirected
480
 *        edge for each pair of mutual edges in the directed graph.
481
 *        Non-mutual edges are lost; loop edges are kept unconditionally.
482
 *        This mode might create multiple edges.
483
 * \param edge_comb What to do with the edge attributes. See the igraph
484
 *        manual section about attributes for details. \c NULL means that
485
 *        the edge attributes are lost during the conversion, \em except
486
 *        when \c mode is \c IGRAPH_TO_UNDIRECTED_EACH, in which case the
487
 *        edge attributes are kept intact.
488
 * \return Error code.
489
 *
490
 * Time complexity: O(|V|+|E|), the number of vertices plus the number
491
 * of edges.
492
 *
493
 * \example examples/simple/igraph_to_undirected.c
494
 */
495
496
igraph_error_t igraph_to_undirected(igraph_t *graph,
497
                         igraph_to_undirected_t mode,
498
3.23k
                         const igraph_attribute_combination_t *edge_comb) {
499
500
3.23k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
501
3.23k
    igraph_int_t no_of_edges = igraph_ecount(graph);
502
3.23k
    igraph_vector_int_t edges;
503
3.23k
    igraph_t newgraph;
504
3.23k
    igraph_bool_t attr = edge_comb && igraph_has_attribute_table();
505
506
3.23k
    if (mode != IGRAPH_TO_UNDIRECTED_EACH &&
507
3.23k
        mode != IGRAPH_TO_UNDIRECTED_COLLAPSE &&
508
0
        mode != IGRAPH_TO_UNDIRECTED_MUTUAL) {
509
0
        IGRAPH_ERROR("Cannot undirect graph, invalid mode", IGRAPH_EINVAL);
510
0
    }
511
512
3.23k
    if (!igraph_is_directed(graph)) {
513
0
        return IGRAPH_SUCCESS;
514
0
    }
515
516
3.23k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0);
517
518
3.23k
    if (mode == IGRAPH_TO_UNDIRECTED_EACH) {
519
0
        igraph_es_t es;
520
0
        igraph_eit_t eit;
521
522
0
        IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2));
523
0
        IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID));
524
0
        IGRAPH_FINALLY(igraph_es_destroy, &es);
525
0
        IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
526
0
        IGRAPH_FINALLY(igraph_eit_destroy, &eit);
527
528
0
        while (!IGRAPH_EIT_END(eit)) {
529
0
            igraph_int_t edge = IGRAPH_EIT_GET(eit);
530
0
            IGRAPH_CHECK(igraph_vector_int_push_back(&edges, IGRAPH_FROM(graph, edge)));
531
0
            IGRAPH_CHECK(igraph_vector_int_push_back(&edges, IGRAPH_TO(graph, edge)));
532
0
            IGRAPH_EIT_NEXT(eit);
533
0
        }
534
535
0
        igraph_eit_destroy(&eit);
536
0
        igraph_es_destroy(&es);
537
0
        IGRAPH_FINALLY_CLEAN(2);
538
539
0
        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
540
0
                                   no_of_nodes,
541
0
                                   IGRAPH_UNDIRECTED));
542
0
        IGRAPH_FINALLY(igraph_destroy, &newgraph);
543
0
        igraph_vector_int_destroy(&edges);
544
0
        IGRAPH_CHECK(igraph_i_attribute_copy(&newgraph, graph, true, true, true));
545
0
        IGRAPH_FINALLY_CLEAN(2);
546
0
        igraph_destroy(graph);
547
0
        *graph = newgraph;
548
549
3.23k
    } else if (mode == IGRAPH_TO_UNDIRECTED_COLLAPSE) {
550
3.23k
        igraph_vector_int_t inadj, outadj;
551
3.23k
        igraph_vector_int_t mergeinto;
552
3.23k
        igraph_int_t actedge = 0;
553
554
3.23k
        if (attr) {
555
0
            IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges);
556
0
        }
557
558
3.23k
        IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2));
559
3.23k
        IGRAPH_VECTOR_INT_INIT_FINALLY(&inadj, 0);
560
3.23k
        IGRAPH_VECTOR_INT_INIT_FINALLY(&outadj, 0);
561
562
157k
        for (igraph_int_t i = 0; i < no_of_nodes; i++) {
563
154k
            igraph_int_t n_out, n_in;
564
154k
            igraph_int_t p1 = -1, p2 = -1;
565
154k
            igraph_int_t e1 = 0, e2 = 0, n1 = 0, n2 = 0, last;
566
154k
            IGRAPH_CHECK(igraph_incident(graph, &outadj, i, IGRAPH_OUT, IGRAPH_LOOPS));
567
154k
            IGRAPH_CHECK(igraph_incident(graph, &inadj, i, IGRAPH_IN, IGRAPH_LOOPS));
568
154k
            n_out = igraph_vector_int_size(&outadj);
569
154k
            n_in = igraph_vector_int_size(&inadj);
570
571
186k
#define STEPOUT() if ( (++p1) < n_out) {    \
572
48.7k
        e1 = VECTOR(outadj)[p1]; \
573
48.7k
        n1 = IGRAPH_TO(graph, e1);      \
574
48.7k
    }
575
187k
#define STEPIN()  if ( (++p2) < n_in) {         \
576
49.1k
        e2 = VECTOR(inadj )[p2]; \
577
49.1k
        n2 = IGRAPH_FROM(graph, e2);        \
578
49.1k
    }
579
154k
#define ADD_NEW_EDGE() { \
580
62.3k
    IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); \
581
62.3k
    IGRAPH_CHECK(igraph_vector_int_push_back(&edges, last)); \
582
62.3k
}
583
154k
#define MERGE_INTO_CURRENT_EDGE(which) { \
584
65.2k
    if (attr) { \
585
0
        VECTOR(mergeinto)[which] = actedge; \
586
0
    } \
587
65.2k
}
588
589
154k
            STEPOUT();
590
154k
            STEPIN();
591
592
172k
            while (p1 < n_out && n1 <= i && p2 < n_in && n2 <= i) {
593
17.8k
                last = (n1 <= n2) ? n1 : n2;
594
17.8k
                ADD_NEW_EDGE();
595
28.0k
                while (p1 < n_out && last == n1) {
596
10.1k
                    MERGE_INTO_CURRENT_EDGE(e1);
597
10.1k
                    STEPOUT();
598
10.1k
                }
599
28.3k
                while (p2 < n_in && last == n2) {
600
10.5k
                    MERGE_INTO_CURRENT_EDGE(e2);
601
10.5k
                    STEPIN();
602
10.5k
                }
603
17.8k
                actedge++;
604
17.8k
            }
605
606
176k
            while (p1 < n_out && n1 <= i) {
607
21.9k
                last = n1;
608
21.9k
                ADD_NEW_EDGE();
609
43.9k
                while (p1 < n_out && last == n1) {
610
21.9k
                    MERGE_INTO_CURRENT_EDGE(e1);
611
21.9k
                    STEPOUT();
612
21.9k
                }
613
21.9k
                actedge++;
614
21.9k
            }
615
616
176k
            while (p2 < n_in && n2 <= i) {
617
22.5k
                last = n2;
618
22.5k
                ADD_NEW_EDGE();
619
45.0k
                while (p2 < n_in && last == n2) {
620
22.5k
                    MERGE_INTO_CURRENT_EDGE(e2);
621
22.5k
                    STEPIN();
622
22.5k
                }
623
22.5k
                actedge++;
624
22.5k
            }
625
154k
        }
626
627
3.23k
#undef MERGE_INTO_CURRENT_EDGE
628
3.23k
#undef ADD_NEW_EDGE
629
3.23k
#undef STEPOUT
630
3.23k
#undef STEPIN
631
632
3.23k
        igraph_vector_int_destroy(&outadj);
633
3.23k
        igraph_vector_int_destroy(&inadj);
634
3.23k
        IGRAPH_FINALLY_CLEAN(2);
635
636
3.23k
        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
637
3.23k
                                   no_of_nodes,
638
3.23k
                                   IGRAPH_UNDIRECTED));
639
3.23k
        IGRAPH_FINALLY(igraph_destroy, &newgraph);
640
3.23k
        igraph_vector_int_destroy(&edges);
641
3.23k
        IGRAPH_CHECK(igraph_i_attribute_copy(&newgraph, graph, true, true, /* edges= */ false));
642
643
3.23k
        if (attr) {
644
0
            igraph_fixed_vectorlist_t vl;
645
0
            IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto, actedge));
646
0
            IGRAPH_FINALLY(igraph_fixed_vectorlist_destroy, &vl);
647
648
0
            IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &newgraph, &vl.vecs, edge_comb));
649
650
0
            igraph_fixed_vectorlist_destroy(&vl);
651
0
            IGRAPH_FINALLY_CLEAN(1);
652
0
        }
653
654
3.23k
        IGRAPH_FINALLY_CLEAN(2);
655
3.23k
        igraph_destroy(graph);
656
3.23k
        *graph = newgraph;
657
658
3.23k
        if (attr) {
659
0
            igraph_vector_int_destroy(&mergeinto);
660
0
            IGRAPH_FINALLY_CLEAN(1);
661
0
        }
662
3.23k
    } else if (mode == IGRAPH_TO_UNDIRECTED_MUTUAL) {
663
0
        igraph_vector_int_t inadj, outadj;
664
0
        igraph_vector_int_t mergeinto;
665
0
        igraph_int_t actedge = 0;
666
667
0
        if (attr) {
668
0
            IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges);
669
0
            igraph_vector_int_fill(&mergeinto, -1);
670
0
        }
671
672
0
        IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2));
673
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&inadj, 0);
674
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&outadj, 0);
675
676
0
        for (igraph_int_t i = 0; i < no_of_nodes; i++) {
677
0
            igraph_int_t n_out, n_in;
678
0
            igraph_int_t p1 = -1, p2 = -1;
679
0
            igraph_int_t e1 = 0, e2 = 0, n1 = 0, n2 = 0;
680
0
            IGRAPH_CHECK(igraph_incident(graph, &outadj, i, IGRAPH_OUT, IGRAPH_LOOPS));
681
0
            IGRAPH_CHECK(igraph_incident(graph, &inadj,  i, IGRAPH_IN, IGRAPH_LOOPS));
682
0
            n_out = igraph_vector_int_size(&outadj);
683
0
            n_in = igraph_vector_int_size(&inadj);
684
685
0
#define STEPOUT() if ( (++p1) < n_out) {    \
686
0
        e1 = VECTOR(outadj)[p1]; \
687
0
        n1 = IGRAPH_TO(graph, e1);      \
688
0
    }
689
0
#define STEPIN()  if ( (++p2) < n_in) {         \
690
0
        e2 = VECTOR(inadj )[p2]; \
691
0
        n2 = IGRAPH_FROM(graph, e2);        \
692
0
    }
693
694
0
            STEPOUT();
695
0
            STEPIN();
696
697
0
            while (p1 < n_out && n1 <= i && p2 < n_in && n2 <= i) {
698
0
                if (n1 == n2) {
699
0
                    IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i));
700
0
                    IGRAPH_CHECK(igraph_vector_int_push_back(&edges, n1));
701
0
                    if (attr) {
702
0
                        VECTOR(mergeinto)[e1] = actedge;
703
0
                        VECTOR(mergeinto)[e2] = actedge;
704
0
                        actedge++;
705
0
                    }
706
0
                    STEPOUT();
707
0
                    STEPIN();
708
0
                } else if (n1 < n2) {
709
0
                    STEPOUT();
710
0
                } else { /* n2<n1 */
711
0
                    STEPIN();
712
0
                }
713
0
            }
714
0
        }
715
716
0
#undef STEPOUT
717
0
#undef STEPIN
718
719
0
        igraph_vector_int_destroy(&outadj);
720
0
        igraph_vector_int_destroy(&inadj);
721
0
        IGRAPH_FINALLY_CLEAN(2);
722
723
0
        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
724
0
                                   no_of_nodes,
725
0
                                   IGRAPH_UNDIRECTED));
726
0
        IGRAPH_FINALLY(igraph_destroy, &newgraph);
727
0
        igraph_vector_int_destroy(&edges);
728
0
        IGRAPH_CHECK(igraph_i_attribute_copy(&newgraph, graph, true, true, /* edges= */ false));
729
730
0
        if (attr) {
731
0
            igraph_fixed_vectorlist_t vl;
732
0
            IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto, actedge));
733
0
            IGRAPH_FINALLY(igraph_fixed_vectorlist_destroy, &vl);
734
735
0
            IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &newgraph, &vl.vecs, edge_comb));
736
737
0
            igraph_fixed_vectorlist_destroy(&vl);
738
0
            IGRAPH_FINALLY_CLEAN(1);
739
0
        }
740
741
0
        IGRAPH_FINALLY_CLEAN(2);
742
0
        igraph_destroy(graph);
743
0
        *graph = newgraph;
744
745
0
        if (attr) {
746
0
            igraph_vector_int_destroy(&mergeinto);
747
0
            IGRAPH_FINALLY_CLEAN(1);
748
0
        }
749
0
    }
750
751
3.23k
    return IGRAPH_SUCCESS;
752
3.23k
}
753
754
0
#define WEIGHT_OF(eid) (weights ? VECTOR(*weights)[eid] : 1)
755
756
/**
757
 * \function igraph_get_stochastic
758
 * \brief Stochastic adjacency matrix of a graph.
759
 *
760
 * Stochastic matrix of a graph. The stochastic matrix of a graph is
761
 * its adjacency matrix, normalized row-wise (or column-wise), such that
762
 * the sum of each row (or column) is one. The row-wise normalized matrix
763
 * is also called a \em right-stochastic and contains the transition
764
 * probabilities of a random walk that follows edge directions in a directed
765
 * graph. The column-wise normalized matrix is called \em left-stochastic and
766
 * is related to random walks moving against edge directions.
767
 *
768
 * </para><para>
769
 * When the out-degree (or in-degree) of a vertex is zero, the corresponding
770
 * row (or column) of the row-wise (or column-wise) normalized stochastic
771
 * matrix will be zero.
772
 *
773
 * \param graph The input graph.
774
 * \param res Pointer to an initialized matrix, the result is stored here.
775
 *   It will be resized as needed.
776
 * \param column_wise If \c false, row-wise normalization is used.
777
 *                    If \c true, column-wise normalization is used.
778
 * \param weights An optional vector containing the weight of each edge
779
 *        in the graph. Supply a null pointer here to make all edges have
780
 *        the same weight of 1.
781
 * \return Error code.
782
 *
783
 * Time complexity: O(|V|^2), |V| is the number of vertices in the graph.
784
 *
785
 * \sa \ref igraph_get_stochastic_sparse(), the sparse version of this
786
 * function.
787
 */
788
789
igraph_error_t igraph_get_stochastic(
790
    const igraph_t *graph, igraph_matrix_t *res, igraph_bool_t column_wise,
791
    const igraph_vector_t *weights
792
0
) {
793
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
794
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
795
0
    igraph_bool_t directed = igraph_is_directed(graph);
796
0
    igraph_int_t from, to;
797
0
    igraph_vector_t sums;
798
0
    igraph_real_t sum;
799
800
0
    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes));
801
0
    igraph_matrix_null(res);
802
803
0
    IGRAPH_VECTOR_INIT_FINALLY(&sums, no_of_nodes);
804
805
0
    if (directed) {
806
        /* Directed */
807
808
0
        IGRAPH_CHECK(igraph_strength(
809
0
            graph, &sums, igraph_vss_all(),
810
0
            column_wise ? IGRAPH_IN : IGRAPH_OUT,
811
0
            IGRAPH_LOOPS, weights
812
0
        ));
813
814
0
        for (igraph_int_t i = 0; i < no_of_edges; i++) {
815
0
            from = IGRAPH_FROM(graph, i);
816
0
            to = IGRAPH_TO(graph, i);
817
0
            sum = VECTOR(sums)[column_wise ? to : from];
818
0
            MATRIX(*res, from, to) += WEIGHT_OF(i) / sum;
819
0
        }
820
0
    } else {
821
        /* Undirected */
822
823
0
        IGRAPH_CHECK(igraph_strength(
824
0
            graph, &sums, igraph_vss_all(), IGRAPH_ALL,
825
0
            IGRAPH_LOOPS, weights
826
0
        ));
827
828
0
        for (igraph_int_t i = 0; i < no_of_edges; i++) {
829
0
            from = IGRAPH_FROM(graph, i);
830
0
            to = IGRAPH_TO(graph, i);
831
0
            MATRIX(*res, from, to) += WEIGHT_OF(i) / VECTOR(sums)[column_wise ? to : from];
832
0
            MATRIX(*res, to, from) += WEIGHT_OF(i) / VECTOR(sums)[column_wise ? from: to];
833
0
        }
834
0
    }
835
836
0
    igraph_vector_destroy(&sums);
837
0
    IGRAPH_FINALLY_CLEAN(1);
838
839
0
    return IGRAPH_SUCCESS;
840
0
}
841
842
#undef WEIGHT_OF
843
844
/**
845
 * \function igraph_get_stochastic_sparse
846
 * \brief The stochastic adjacency matrix of a graph.
847
 *
848
 * Stochastic matrix of a graph in sparse format. See \ref igraph_get_stochastic()
849
 * for the information on stochastic matrices.
850
 *
851
 * \param graph The input graph.
852
 * \param res Pointer to an \em initialized sparse matrix, the
853
 *    result is stored here. The matrix will be resized as needed.
854
 * \param column_wise If \c false, row-wise normalization is used.
855
 *                    If \c true, column-wise normalization is used.
856
 * \param weights An optional vector containing the weight of each edge
857
 *        in the graph. Supply a null pointer here to make all edges have
858
 *        the same weight of 1.
859
 * \return Error code.
860
 *
861
 * Time complexity: O(|V|+|E|), linear in the number of vertices and
862
 * edges.
863
 *
864
 * \sa \ref igraph_get_stochastic(), the dense version of this function.
865
 */
866
867
igraph_error_t igraph_get_stochastic_sparse(
868
    const igraph_t *graph, igraph_sparsemat_t *res, igraph_bool_t column_wise,
869
    const igraph_vector_t *weights
870
0
) {
871
0
    IGRAPH_CHECK(igraph_get_adjacency_sparse(graph, res, IGRAPH_GET_ADJACENCY_BOTH, weights, IGRAPH_LOOPS_TWICE));
872
873
0
    if (column_wise) {
874
0
        IGRAPH_CHECK(igraph_sparsemat_normalize_cols(res, /* allow_zeros = */ true));
875
0
    } else {
876
0
        IGRAPH_CHECK(igraph_sparsemat_normalize_rows(res, /* allow_zeros = */ true));
877
0
    }
878
879
0
    return IGRAPH_SUCCESS;
880
0
}
881
882
883
/**
884
 * \ingroup conversion
885
 * \function igraph_to_prufer
886
 * \brief Converts a tree to its Pr&uuml;fer sequence.
887
 *
888
 * A Pr&uuml;fer sequence is a unique sequence of integers associated
889
 * with a labelled tree. A tree on n >= 2 vertices can be represented by a
890
 * sequence of n-2 integers, each between 0 and n-1 (inclusive).
891
 *
892
 * \param graph Pointer to an initialized graph object which
893
          must be a tree on n >= 2 vertices.
894
 * \param prufer A pointer to the integer vector that should hold the Pr&uuml;fer sequence;
895
                 the vector must be initialized and will be resized to n - 2.
896
 * \return Error code:
897
 *          \clist
898
 *          \cli IGRAPH_ENOMEM
899
 *             there is not enough memory to perform the operation.
900
 *          \cli IGRAPH_EINVAL
901
 *             the graph is not a tree or it is has less than vertices
902
 *          \endclist
903
 *
904
 * \sa \ref igraph_from_prufer()
905
 *
906
 */
907
0
igraph_error_t igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* prufer) {
908
    /* For generating the Prüfer sequence, we enumerate the vertices u of the tree.
909
       We keep track of the degrees of all vertices, treating vertices
910
       of degree 0 as removed. We maintain the invariant that all leafs
911
       that are still contained in the tree are >= u.
912
       If u is a leaf, we remove it and add its unique neighbor to the Prüfer
913
       sequence. If the removal of u turns the neighbor into a leaf which is < u,
914
       we repeat the procedure for the new leaf and so on. */
915
0
    igraph_int_t u;
916
0
    igraph_vector_int_t degrees;
917
0
    igraph_vector_int_t neighbors;
918
0
    igraph_int_t prufer_index = 0;
919
0
    igraph_int_t n = igraph_vcount(graph);
920
0
    igraph_bool_t is_tree = false;
921
922
0
    IGRAPH_CHECK(igraph_is_tree(graph, &is_tree, NULL, IGRAPH_ALL));
923
924
0
    if (!is_tree) {
925
0
        IGRAPH_ERROR("The graph must be a tree", IGRAPH_EINVAL);
926
0
    }
927
928
0
    if (n < 2) {
929
0
        IGRAPH_ERROR("The tree must have at least 2 vertices", IGRAPH_EINVAL);
930
0
    }
931
932
0
    IGRAPH_CHECK(igraph_vector_int_resize(prufer, n - 2));
933
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&degrees, n);
934
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 1);
935
936
0
    IGRAPH_CHECK(igraph_degree(graph, &degrees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS));
937
938
0
    for (u = 0; u < n; ++u) {
939
0
        igraph_int_t degree = VECTOR(degrees)[u];
940
0
        igraph_int_t leaf = u;
941
942
0
        while (degree == 1 && leaf <= u) {
943
0
            igraph_int_t neighbor = 0;
944
0
            igraph_int_t neighbor_count = 0;
945
946
0
            VECTOR(degrees)[leaf] = 0; /* mark leaf v as deleted */
947
948
0
            IGRAPH_CHECK(igraph_neighbors(
949
0
                graph, &neighbors, leaf, IGRAPH_ALL, IGRAPH_LOOPS, IGRAPH_MULTIPLE
950
0
            ));
951
952
            /* Find the unique remaining neighbor of the leaf */
953
0
            neighbor_count = igraph_vector_int_size(&neighbors);
954
0
            for (igraph_int_t i = 0; i < neighbor_count; i++) {
955
0
                neighbor = VECTOR(neighbors)[i];
956
0
                if (VECTOR(degrees)[neighbor] > 0) {
957
0
                    break;
958
0
                }
959
0
            }
960
961
            /* remember that we have removed the leaf */
962
0
            VECTOR(degrees)[neighbor]--;
963
0
            degree = VECTOR(degrees)[neighbor];
964
965
            /* Add the neighbor to the prufer sequence unless it is the last vertex
966
            (i.e. degree == 0) */
967
0
            if (degree > 0) {
968
0
                VECTOR(*prufer)[prufer_index] = neighbor;
969
0
                prufer_index++;
970
0
            }
971
0
            leaf = neighbor;
972
0
        }
973
0
    }
974
975
0
    igraph_vector_int_destroy(&degrees);
976
0
    igraph_vector_int_destroy(&neighbors);
977
0
    IGRAPH_FINALLY_CLEAN(2);
978
979
0
    return IGRAPH_SUCCESS;
980
0
}