Coverage Report

Created: 2025-11-11 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}