Files
agent_compositor_test/references/igraph-1.0.1/src/connectivity/reachability.c
T
Abdelrahman Said a11edf0c53 Add graph references
2026-06-28 13:49:01 +01:00

258 lines
10 KiB
C

/*
igraph library.
Copyright (C) 2024 The igraph development team <igraph@igraph.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "igraph_reachability.h"
#include "igraph_adjlist.h"
#include "igraph_bitset_list.h"
#include "igraph_components.h"
#include "igraph_constructors.h"
#include "igraph_interface.h"
/**
* \ingroup structural
* \function igraph_reachability
* \brief Calculates which vertices are reachable from each vertex in the graph.
*
* The resulting list will contain one bitset for each strongly connected component.
* The bitset for component i will have its j-th bit set, if vertex j is reachable
* from some vertex in component i in 0 or more steps.
* In particular, a vertex is always reachable from itself.
*
* \param graph The graph object to analyze.
* \param membership Pointer to an integer vector. For every vertex,
* the ID of its component is given. The vector will be resized as needed.
* This parameter must not be \c NULL.
* \param csize Pointer to an integer vector or \c NULL. For every component, it
* gives its size (vertex count), the order being defined by the component
* IDs. The vector will be resized as needed.
* \param no_of_components Pointer to an integer or \c NULL. The number of
* components will be stored here.
* \param reach A list of bitsets representing the result. It will be resized
* as needed. <code>reach[membership[u]][v]</code> is set to \c true if
* vertex \c v is reachable from vertex \c u.
* \param mode In directed graphs, controls the treatment of edge directions.
* Ignored in undirected graphs. With \c IGRAPH_OUT, reachability is computed
* by traversing edges along their direction. With \c IGRAPH_IN, edges are
* traversed opposite to their direction. With \c IGRAPH_ALL, edge directions
* are ignored and the graph is treated as undirected.
* \return Error code:
* \c IGRAPH_ENOMEM if there is not enough memory
* to perform the operation.
*
* \sa \ref igraph_connected_components() to find the connnected components
* of a graph; \ref igraph_count_reachable() to count how many vertices
* are reachable from each vertex; \ref igraph_subcomponent() to find
* which vertices are rechable from a single vertex.
*
* Time complexity: O(|C||V|/w + |V| + |E|), where
* |C| is the number of strongly connected components (at most |V|),
* |V| is the number of vertices, and
* |E| is the number of edges respectively,
* and w is the bit width of \type igraph_int_t, typically the
* word size of the machine (32 or 64).
*/
igraph_error_t igraph_reachability(
const igraph_t *graph,
igraph_vector_int_t *membership,
igraph_vector_int_t *csize,
igraph_int_t *no_of_components,
igraph_bitset_list_t *reach,
igraph_neimode_t mode) {
const igraph_int_t no_of_nodes = igraph_vcount(graph);
igraph_int_t no_of_comps;
igraph_adjlist_t adjlist, dag;
if (mode != IGRAPH_ALL && mode != IGRAPH_OUT && mode != IGRAPH_IN) {
IGRAPH_ERROR("Invalid mode for reachability.", IGRAPH_EINVMODE);
}
if (! igraph_is_directed(graph)) {
mode = IGRAPH_ALL;
}
IGRAPH_CHECK(igraph_connected_components(graph,
membership, csize, &no_of_comps,
mode == IGRAPH_ALL ? IGRAPH_WEAK : IGRAPH_STRONG));
if (no_of_components) {
*no_of_components = no_of_comps;
}
IGRAPH_CHECK(igraph_bitset_list_resize(reach, no_of_comps));
for (igraph_int_t comp = 0; comp < no_of_comps; comp++) {
IGRAPH_CHECK(igraph_bitset_resize(igraph_bitset_list_get_ptr(reach, comp), no_of_nodes));
}
for (igraph_int_t v = 0; v < no_of_nodes; v++) {
IGRAPH_BIT_SET(*igraph_bitset_list_get_ptr(reach, VECTOR(*membership)[v]), v);
}
if (mode == IGRAPH_ALL) {
return IGRAPH_SUCCESS;
}
IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
IGRAPH_CHECK(igraph_adjlist_init_empty(&dag, no_of_comps));
IGRAPH_FINALLY(igraph_adjlist_destroy, &dag);
for (igraph_int_t v = 0; v < no_of_nodes; v++) {
const igraph_vector_int_t *neighbours = igraph_adjlist_get(&adjlist, v);
igraph_vector_int_t *dag_neighbours = igraph_adjlist_get(&dag, VECTOR(*membership)[v]);
const igraph_int_t n = igraph_vector_int_size(neighbours);
for (igraph_int_t i = 0; i < n; i++) {
igraph_int_t w = VECTOR(*neighbours)[i];
if (VECTOR(*membership)[v] != VECTOR(*membership)[w]) {
IGRAPH_CHECK(igraph_vector_int_push_back(dag_neighbours, VECTOR(*membership)[w]));
}
}
}
/* Iterate through strongly connected components in reverser topological order,
* exploiting the fact that they are indexed in topological order. */
for (igraph_int_t i = 0; i < no_of_comps; i++) {
const igraph_int_t comp = mode == IGRAPH_IN ? i : no_of_comps - i - 1;
const igraph_vector_int_t *dag_neighbours = igraph_adjlist_get(&dag, comp);
igraph_bitset_t *from_bitset = igraph_bitset_list_get_ptr(reach, comp);
const igraph_int_t n = igraph_vector_int_size(dag_neighbours);
for (igraph_int_t j = 0; j < n; j++) {
const igraph_bitset_t *to_bitset = igraph_bitset_list_get_ptr(reach, VECTOR(*dag_neighbours)[j]);
igraph_bitset_or(from_bitset, from_bitset, to_bitset);
}
}
igraph_adjlist_destroy(&adjlist);
igraph_adjlist_destroy(&dag);
IGRAPH_FINALLY_CLEAN(2);
return IGRAPH_SUCCESS;
}
/**
* \ingroup structural
* \function igraph_count_reachable
* \brief The number of vertices reachable from each vertex in the graph.
*
* \param graph The graph object to analyze.
* \param counts Integer vector. <code>counts[v]</code> will store the number
* of vertices reachable from vertex \c v, including \c v itself.
* \param mode In directed graphs, controls the treatment of edge directions.
* Ignored in undirected graphs. With \c IGRAPH_OUT, reachability is computed
* by traversing edges along their direction. With \c IGRAPH_IN, edges are
* traversed opposite to their direction. With \c IGRAPH_ALL, edge directions
* are ignored and the graph is treated as undirected.
* \return Error code:
* \c IGRAPH_ENOMEM if there is not enough memory
* to perform the operation.
*
* \sa \ref igraph_connected_components(), \ref igraph_transitive_closure()
*
* Time complexity: O(|C||V|/w + |V| + |E|), where
* |C| is the number of strongly connected components (at most |V|),
* |V| is the number of vertices, and
* |E| is the number of edges respectively,
* and w is the bit width of \type igraph_int_t, typically the
* word size of the machine (32 or 64).
*/
igraph_error_t igraph_count_reachable(const igraph_t *graph,
igraph_vector_int_t *counts,
igraph_neimode_t mode) {
igraph_vector_int_t membership;
igraph_int_t no_of_nodes = igraph_vcount(graph);
igraph_bitset_list_t reach;
IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0);
IGRAPH_BITSET_LIST_INIT_FINALLY(&reach, 0);
IGRAPH_CHECK(igraph_reachability(graph, &membership, NULL, NULL, &reach, mode));
IGRAPH_CHECK(igraph_vector_int_resize(counts, igraph_vcount(graph)));
for (igraph_int_t i = 0; i < no_of_nodes; i++) {
VECTOR(*counts)[i] = igraph_bitset_popcount(igraph_bitset_list_get_ptr(&reach, VECTOR(membership)[i]));
}
igraph_bitset_list_destroy(&reach);
igraph_vector_int_destroy(&membership);
IGRAPH_FINALLY_CLEAN(2);
return IGRAPH_SUCCESS;
}
/**
* \ingroup structural
* \function igraph_transitive_closure
* \brief Computes the transitive closure of a graph.
*
* The resulting graph will have an edge from vertex \c i to vertex \c j
* if \c j is reachable from \c i.
*
* \param graph The graph object to analyze.
* \param closure The resulting graph representing the transitive closure.
* \return Error code:
* \c IGRAPH_ENOMEM if there is not enough memory
* to perform the operation.
*
* \sa \ref igraph_connected_components(), \ref igraph_count_reachable()
*
* Time complexity: O(|V|^2 + |E|), where
* |V| is the number of vertices, and
* |E| is the number of edges, respectively.
*/
igraph_error_t igraph_transitive_closure(const igraph_t *graph, igraph_t *closure) {
const igraph_int_t no_of_nodes = igraph_vcount(graph);
const igraph_bool_t directed = igraph_is_directed(graph);
igraph_vector_int_t membership, edges;
igraph_bitset_list_t reach;
IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0);
IGRAPH_BITSET_LIST_INIT_FINALLY(&reach, 0);
IGRAPH_CHECK(igraph_reachability(graph, &membership, NULL, NULL, &reach, IGRAPH_OUT));
IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0);
for (igraph_int_t u = 0; u < no_of_nodes; u++) {
const igraph_bitset_t *row = igraph_bitset_list_get_ptr(&reach, VECTOR(membership)[u]);
for (igraph_int_t v = directed ? 0 : u + 1; v < no_of_nodes; v++) {
if (u != v && IGRAPH_BIT_TEST(*row, v)) {
IGRAPH_CHECK(igraph_vector_int_push_back(&edges, u));
IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v));
}
}
}
igraph_bitset_list_destroy(&reach);
igraph_vector_int_destroy(&membership);
IGRAPH_FINALLY_CLEAN(2);
IGRAPH_CHECK(igraph_create(closure, &edges, no_of_nodes, directed));
igraph_vector_int_destroy(&edges);
IGRAPH_FINALLY_CLEAN(1);
return IGRAPH_SUCCESS;
}