Walks, cycles and components
#include <flint/bool_mat.h>void bool_mat_transitive_closure(bool_mat_t B, const bool_mat_t A)sourceSets B to the transitive closure A + A^2 + A^3 + \cdots of A: B_{ij} = 1 if and only if there is a walk of positive length from i to j. Uses Warshall's algorithm [War1962], in O(n^3) operations.
slong bool_mat_get_strongly_connected_components(slong * p, const bool_mat_t A)sourceComputes the strongly connected components of the graph of A and returns their number k. Sets p_i, for each vertex i, to the index in \{0, \ldots, k - 1\} of the component containing i. The components form a directed acyclic graph, and they are numbered in postorder with respect to it: if there is an edge from a vertex of component a to a vertex of a different component b, then a > b. Uses Tarjan's algorithm [Tar1972], in O(n^2) operations.
slong bool_mat_nilpotency_degree(const bool_mat_t A)sourceReturns the nilpotency degree of A, the smallest k \ge 1 with A^k = 0, or -1 if A is not nilpotent. For an empty matrix it returns 0. For a nilpotent matrix the degree is one more than the length of the longest path in the graph, which is computed by a topological sort.
slong bool_mat_all_pairs_longest_walk(fmpz_mat_t B, const bool_mat_t A)sourceSets B_{ij} to the length of the longest walk from vertex i to vertex j in the graph of A, where the empty walk from a vertex to itself has length 0. B_{ij} = -1 if there is no walk from i to j, and B_{ij} = -2 if there are arbitrarily long walks (a walk from i to j through a cycle). The matrix B must be n \times n.
Returns -2 if some entry of B is -2, and otherwise the largest entry of B, which is one less than bool_mat_nilpotency_degree; for an empty matrix it returns -1.
The function works on the condensation of the graph, the acyclic graph of its strongly connected components. An application is to matrix power series: if A has the sparsity pattern of a real or complex matrix M and B_{ij} \ne -2, then (M^k)_{ij} = 0 for all k > B_{ij}, so the tail \sum_{k > N} a_k (M^k)_{ij} of a power series vanishes for N \ge B_{ij}.
A square Boolean matrix A of order n is the adjacency matrix of a directed graph on the vertices 0, \ldots, n - 1, and the Boolean product of matrices composes walks: (A^k)_{ij} = 1 exactly when there is a walk of length k from i to j. The four functions described here compute properties of all powers of A at once from the structure of the graph, without forming the powers.
bool_mat_transitive_closuregives \sum_{k \ge 1} A^k, the reachability relation, by Warshall's algorithm [War1962] in O(n^3) operations.bool_mat_get_strongly_connected_componentspartitions the vertices into strongly connected components (maximal sets of vertices that can all reach each other) with Tarjan's depth-first search [Tar1972], in O(n^2) operations since the graph is given by a dense matrix. The components are numbered so that edges between different components go from higher to lower numbers.bool_mat_nilpotency_degreefinds the least k with A^k = 0. Such a k exists exactly when the graph has no cycle, and it is then one more than the number of edges of a longest path. The function finds a topological order by depth-first search and computes longest paths along it.bool_mat_all_pairs_longest_walkrefines this to each pair of vertices: the largest k with (A^k)_{ij} = 1, or -1 if there is none, or -2 if there is none largest. A pair (i, j) has arbitrarily long walks when some walk from i to j passes through a component containing a cycle. The function works on the condensation of the graph (the acyclic graph of components).
The last function serves to bound the error of truncated matrix power series: if M is a real or complex matrix with the sparsity pattern A, then (M^k)_{ij} = 0 whenever (A^k)_{ij} = 0, so the entry (i, j) of \sum_k a_k M^k is a finite sum when B_{ij} \ge 0. arb_mat_exp and acb_mat_exp use this to compute exponentials of nilpotent and partly nilpotent matrices with exact zero error bounds where possible.
Example#
The graph with edges 0 \to 1, 1 \to 2, 2 \to 1 and 1 \to 3 has the components \{3\}, \{1, 2\} and \{0\}, numbered 0, 1 and 2. Every walk into the cycle 1 \to 2 \to 1 can be made arbitrarily long. Removing the edge 2 \to 1 leaves an acyclic graph whose longest path, 0 \to 1 \to 3 (or 0 \to 1 \to 2), has two edges, so A^3 = 0.
#include <flint/flint.h>
#include <flint/bool_mat.h>
#include <flint/fmpz_mat.h>
int main(void)
{
bool_mat_t A, C;
fmpz_mat_t W;
slong p[4], k, w;
bool_mat_init(A, 4, 4);
bool_mat_init(C, 4, 4);
fmpz_mat_init(W, 4, 4);
bool_mat_set_entry(A, 0, 1, 1);
bool_mat_set_entry(A, 1, 2, 1);
bool_mat_set_entry(A, 2, 1, 1);
bool_mat_set_entry(A, 1, 3, 1);
flint_printf("A =\n");
bool_mat_print(A);
bool_mat_transitive_closure(C, A);
flint_printf("transitive closure =\n");
bool_mat_print(C);
k = bool_mat_get_strongly_connected_components(p, A);
flint_printf("%wd components: %{slong*}\n", k, p, (slong) 4);
flint_printf("nilpotent: %d\n", bool_mat_is_nilpotent(A));
w = bool_mat_all_pairs_longest_walk(W, A);
flint_printf("longest walks (return value %wd):\n", w);
fmpz_mat_print_pretty(W);
flint_printf("\n");
/* remove the cycle 1 -> 2 -> 1 */
bool_mat_set_entry(A, 2, 1, 0);
flint_printf("without 2 -> 1: nilpotency degree %wd\n",
bool_mat_nilpotency_degree(A));
w = bool_mat_all_pairs_longest_walk(W, A);
flint_printf("longest walks (return value %wd):\n", w);
fmpz_mat_print_pretty(W);
flint_printf("\n");
bool_mat_clear(A);
bool_mat_clear(C);
fmpz_mat_clear(W);
return 0;
}A = [0, 1, 0, 0] [0, 0, 1, 1] [0, 1, 0, 0] [0, 0, 0, 0] transitive closure = [0, 1, 1, 1] [0, 1, 1, 1] [0, 1, 1, 1] [0, 0, 0, 0] 3 components: [2, 1, 1, 0] nilpotent: 0 longest walks (return value -2): [[0 -2 -2 -2] [-1 -2 -2 -2] [-1 -2 -2 -2] [-1 -1 -1 0] ] without 2 -> 1: nilpotency degree 3 longest walks (return value 2): [[0 1 2 2] [-1 0 1 1] [-1 -1 0 -1] [-1 -1 -1 0] ]
The diagonal entries of the walk matrix are 0 for vertices off any cycle (the empty walk) and -2 for vertices on a cycle.
References#
- [War1962] S. Warshall. A theorem on Boolean matrices. Journal of the ACM 9(1), 11–12, 1962.
- [Tar1972] R. E. Tarjan. Depth-first search and linear graph algorithms. SIAM Journal on Computing 1(2), 146–160, 1972.
The FLINT team, “Walks, cycles and components (bool_mat.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/bool_mat/graphs.html (generated 2026-10-20).
@misc{flint-doc-bool-mat-graphs,
author = {{The FLINT team}},
title = {{Walks, cycles and components (bool\_mat.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/bool_mat/graphs.html},
note = {Generated 2026-10-20}
}