/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 | } |