/src/igraph/src/operators/permute.c
Line | Count | Source |
1 | | /* |
2 | | igraph library. |
3 | | Copyright (C) 2006-2025 The igraph development team <igraph@igraph.org> |
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, see <https://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | #include "igraph_operators.h" |
20 | | #include "igraph_isomorphism.h" |
21 | | |
22 | | #include "igraph_constructors.h" |
23 | | #include "igraph_interface.h" |
24 | | |
25 | | #include "graph/attributes.h" |
26 | | |
27 | | /** |
28 | | * \function igraph_invert_permutation |
29 | | * \brief Inverts a permutation. |
30 | | * |
31 | | * Produces the inverse of \p permutation into \p inverse and at the same time it checks |
32 | | * that the permutation vector is valid, i.e. all indices are within range and there are |
33 | | * no duplicate entries. |
34 | | * |
35 | | * \param permutation A permutation vector containing 0-based integer indices. |
36 | | * \param inverse An initialized vector. The inverse of \p permutation will be stored here. |
37 | | * \return Error code. |
38 | | */ |
39 | 18.0k | igraph_error_t igraph_invert_permutation(const igraph_vector_int_t *permutation, igraph_vector_int_t *inverse) { |
40 | 18.0k | const igraph_int_t n = igraph_vector_int_size(permutation); |
41 | | |
42 | 18.0k | IGRAPH_CHECK(igraph_vector_int_resize(inverse, n)); |
43 | 18.0k | igraph_vector_int_fill(inverse, -1); |
44 | | |
45 | 82.9k | for (igraph_int_t i = 0; i < n; i++) { |
46 | 64.9k | igraph_int_t j = VECTOR(*permutation)[i]; |
47 | 64.9k | if (j < 0 || j >= n) { |
48 | 0 | IGRAPH_ERROR("Invalid index in permutation vector.", IGRAPH_EINVAL); |
49 | 0 | } |
50 | 64.9k | if (VECTOR(*inverse)[j] != -1) { |
51 | | /* This element of 'inverse' has already been set, 'j' is a duplicate value. */ |
52 | 0 | IGRAPH_ERROR("Duplicate entry in permutation vector.", IGRAPH_EINVAL); |
53 | 0 | } |
54 | 64.9k | VECTOR(*inverse)[j] = i; |
55 | 64.9k | } |
56 | | |
57 | 18.0k | return IGRAPH_SUCCESS; |
58 | 18.0k | } |
59 | | |
60 | | /** |
61 | | * \function igraph_permute_vertices |
62 | | * \brief Permute the vertices. |
63 | | * |
64 | | * This function creates a new graph from the input graph by permuting |
65 | | * its vertices according to the specified mapping. Call this function |
66 | | * with the output of \ref igraph_canonical_permutation() to create |
67 | | * the canonical form of a graph. |
68 | | * |
69 | | * \param graph The input graph. |
70 | | * \param res Pointer to an uninitialized graph object. The new graph |
71 | | * is created here. |
72 | | * \param permutation The permutation to apply. The i-th element of the |
73 | | * vector specifies the index of the vertex in the original graph that |
74 | | * will become vertex i in the new graph. |
75 | | * \return Error code. |
76 | | * |
77 | | * Time complexity: O(|V|+|E|), linear in terms of the number of |
78 | | * vertices and edges. |
79 | | */ |
80 | | igraph_error_t igraph_permute_vertices(const igraph_t *graph, igraph_t *res, |
81 | 18.0k | const igraph_vector_int_t *permutation) { |
82 | | |
83 | 18.0k | igraph_int_t no_of_nodes = igraph_vcount(graph); |
84 | 18.0k | igraph_int_t no_of_edges = igraph_ecount(graph); |
85 | 18.0k | igraph_vector_int_t edges; |
86 | 18.0k | igraph_vector_int_t index; |
87 | 18.0k | igraph_int_t p; |
88 | | |
89 | 18.0k | if (igraph_vector_int_size(permutation) != no_of_nodes) { |
90 | 0 | IGRAPH_ERROR("Permute vertices: invalid permutation vector size.", IGRAPH_EINVAL); |
91 | 0 | } |
92 | | |
93 | 18.0k | IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_nodes); |
94 | | |
95 | | /* Also checks that 'permutation' is valid: */ |
96 | 18.0k | IGRAPH_CHECK(igraph_invert_permutation(permutation, &index)); |
97 | | |
98 | 18.0k | IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); |
99 | | |
100 | 18.0k | p = 0; |
101 | 102k | for (igraph_int_t i = 0; i < no_of_edges; i++) { |
102 | 84.4k | VECTOR(edges)[p++] = VECTOR(index)[ IGRAPH_FROM(graph, i) ]; |
103 | 84.4k | VECTOR(edges)[p++] = VECTOR(index)[ IGRAPH_TO(graph, i) ]; |
104 | 84.4k | } |
105 | | |
106 | 18.0k | IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, igraph_is_directed(graph))); |
107 | 18.0k | IGRAPH_FINALLY(igraph_destroy, res); |
108 | | |
109 | | /* Attributes */ |
110 | 18.0k | if (graph->attr) { |
111 | 0 | igraph_vector_int_t vtypes; |
112 | 0 | IGRAPH_CHECK(igraph_i_attribute_copy(res, graph, true, /* vertex= */ false, true)); |
113 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&vtypes, 0); |
114 | 0 | IGRAPH_CHECK(igraph_i_attribute_get_info(graph, 0, 0, 0, &vtypes, 0, 0)); |
115 | 0 | if (igraph_vector_int_size(&vtypes) != 0) { |
116 | 0 | IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res, permutation)); |
117 | 0 | } |
118 | 0 | igraph_vector_int_destroy(&vtypes); |
119 | 0 | IGRAPH_FINALLY_CLEAN(1); |
120 | 0 | } |
121 | | |
122 | 18.0k | igraph_vector_int_destroy(&index); |
123 | 18.0k | igraph_vector_int_destroy(&edges); |
124 | 18.0k | IGRAPH_FINALLY_CLEAN(3); /* +1 for res */ |
125 | | |
126 | 18.0k | return IGRAPH_SUCCESS; |
127 | 18.0k | } |