Coverage Report

Created: 2025-08-29 06:51

/src/igraph/src/operators/contract.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   IGraph library.
3
   Copyright (C) 2006-2021 The igraph development team
4
5
   This program is free software; you can redistribute it and/or modify
6
   it under the terms of the GNU General Public License as published by
7
   the Free Software Foundation; either version 2 of the License, or
8
   (at your option) any later version.
9
10
   This program is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
   GNU General Public License for more details.
14
15
   You should have received a copy of the GNU General Public License
16
   along with this program; if not, write to the Free Software
17
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
   02110-1301 USA
19
20
*/
21
22
#include "igraph_operators.h"
23
24
#include "igraph_constructors.h"
25
#include "igraph_interface.h"
26
27
#include "graph/attributes.h"
28
29
/**
30
 * \function igraph_contract_vertices
31
 * \brief Replace multiple vertices with a single one.
32
 *
33
 * This function modifies the graph by merging several vertices
34
 * into one. The vertices in the modified graph correspond
35
 * to groups of vertices in the input graph. No edges are removed,
36
 * thus the modified graph will typically have self-loops
37
 * (corresponding to in-group edges) and multi-edges
38
 * (corresponding to multiple connections between two groups).
39
 * Use \ref igraph_simplify() to eliminate self-loops and
40
 * merge multi-edges.
41
 *
42
 * \param graph The input graph. It will be modified in-place.
43
 * \param mapping A vector giving the mapping. For each
44
 *        vertex in the original graph, it should contain
45
 *        its desired ID in the result graph. In order not to create
46
 *        "orphan vertices" that have no corresponding vertices
47
 *        in the original graph, ensure that the IDs are consecutive
48
 *        integers starting from zero.
49
 * \param vertex_comb What to do with the vertex attributes.
50
 *        \c NULL means that vertex attributes are not kept
51
 *        after the contraction (not even for unaffected
52
 *        vertices). See the igraph manual section about attributes
53
 *        for details.
54
 * \return Error code.
55
 *
56
 * Time complexity: O(|V|+|E|), linear in the number
57
 * or vertices plus edges.
58
 *
59
 * \example examples/simple/igraph_contract_vertices.c
60
 */
61
62
igraph_error_t igraph_contract_vertices(igraph_t *graph,
63
                             const igraph_vector_int_t *mapping,
64
24.6k
                             const igraph_attribute_combination_t *vertex_comb) {
65
24.6k
    igraph_vector_int_t edges;
66
24.6k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
67
24.6k
    igraph_integer_t no_of_edges = igraph_ecount(graph);
68
24.6k
    igraph_bool_t vattr = vertex_comb && igraph_has_attribute_table();
69
24.6k
    igraph_t res;
70
24.6k
    igraph_integer_t last;
71
24.6k
    igraph_integer_t no_new_vertices;
72
73
24.6k
    if (igraph_vector_int_size(mapping) != no_of_nodes) {
74
0
        IGRAPH_ERRORF("Mapping vector length (%" IGRAPH_PRId ") "
75
0
                "not equal to number of nodes (%" IGRAPH_PRId ").",
76
0
                IGRAPH_EINVAL, igraph_vector_int_size(mapping), no_of_nodes);
77
0
    }
78
79
24.6k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0);
80
24.6k
    IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2));
81
82
24.6k
    if (no_of_nodes > 0) {
83
24.6k
        last = igraph_vector_int_max(mapping);
84
24.6k
    } else {
85
        /* Ensure that no_new_vertices will be zero
86
         * when the input graph has no vertices. */
87
2
        last = -1;
88
2
    }
89
90
1.38M
    for (igraph_integer_t edge = 0; edge < no_of_edges; edge++) {
91
1.35M
        igraph_integer_t from = IGRAPH_FROM(graph, edge);
92
1.35M
        igraph_integer_t to = IGRAPH_TO(graph, edge);
93
94
1.35M
        igraph_integer_t nfrom = VECTOR(*mapping)[from];
95
1.35M
        igraph_integer_t nto = VECTOR(*mapping)[to];
96
97
1.35M
        igraph_vector_int_push_back(&edges, nfrom);
98
1.35M
        igraph_vector_int_push_back(&edges, nto);
99
100
1.35M
        if (nfrom > last) {
101
0
            last = nfrom;
102
0
        }
103
1.35M
        if (nto   > last) {
104
0
            last = nto;
105
0
        }
106
1.35M
    }
107
108
24.6k
    no_new_vertices = last + 1;
109
110
24.6k
    IGRAPH_CHECK(igraph_create(&res, &edges, no_new_vertices,
111
24.6k
                               igraph_is_directed(graph)));
112
113
24.6k
    igraph_vector_int_destroy(&edges);
114
24.6k
    IGRAPH_FINALLY_CLEAN(1);
115
116
24.6k
    IGRAPH_FINALLY(igraph_destroy, &res);
117
118
24.6k
    IGRAPH_CHECK(igraph_i_attribute_copy(&res, graph, true, /* vertex= */ false, true));
119
120
24.6k
    if (vattr) {
121
3.21k
        igraph_vector_int_list_t merges;
122
3.21k
        igraph_vector_int_t sizes;
123
124
3.21k
        IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&merges, no_new_vertices);
125
3.21k
        IGRAPH_VECTOR_INT_INIT_FINALLY(&sizes, no_new_vertices);
126
127
147k
        for (igraph_integer_t i = 0; i < no_of_nodes; i++) {
128
143k
            igraph_integer_t to = VECTOR(*mapping)[i];
129
143k
            igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&merges, to);
130
143k
            VECTOR(sizes)[to] += 1;
131
143k
            IGRAPH_CHECK(igraph_vector_int_push_back(v, i));
132
143k
        }
133
134
3.21k
        IGRAPH_CHECK(igraph_i_attribute_combine_vertices(graph, &res,
135
3.21k
                     &merges,
136
3.21k
                     vertex_comb));
137
138
3.21k
        igraph_vector_int_destroy(&sizes);
139
3.21k
        igraph_vector_int_list_destroy(&merges);
140
3.21k
        IGRAPH_FINALLY_CLEAN(2);
141
3.21k
    }
142
143
24.6k
    IGRAPH_FINALLY_CLEAN(1);
144
24.6k
    igraph_destroy(graph);
145
24.6k
    *graph = res;
146
147
24.6k
    return IGRAPH_SUCCESS;
148
24.6k
}