FLINT3.7.0-dev

Walks, cycles and components

Synopsis#include <flint/bool_mat.h>
void bool_mat_transitive_closure(bool_mat_t B, const bool_mat_t A)source

Sets BB to the transitive closure A+A2+A3+⋯A + A^2 + A^3 + \cdots of AA: Bij=1B_{ij} = 1 if and only if there is a walk of positive length from ii to jj. Uses Warshall's algorithm [War1962], in O(n3)O(n^3) operations.

slong bool_mat_get_strongly_connected_components(slong * p, const bool_mat_t A)source

Computes the strongly connected components of the graph of AA and returns their number kk. Sets pip_i, for each vertex ii, to the index in {0,…,k−1}\{0, \ldots, k - 1\} of the component containing ii. 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 aa to a vertex of a different component bb, then a>ba > b. Uses Tarjan's algorithm [Tar1972], in O(n2)O(n^2) operations.

slong bool_mat_nilpotency_degree(const bool_mat_t A)source

Returns the nilpotency degree of AA, the smallest k≥1k \ge 1 with Ak=0A^k = 0, or −1-1 if AA 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)source

Sets BijB_{ij} to the length of the longest walk from vertex ii to vertex jj in the graph of AA, where the empty walk from a vertex to itself has length 0. Bij=−1B_{ij} = -1 if there is no walk from ii to jj, and Bij=−2B_{ij} = -2 if there are arbitrarily long walks (a walk from ii to jj through a cycle). The matrix BB must be n×nn \times n.

Returns −2-2 if some entry of BB is −2-2, and otherwise the largest entry of BB, which is one less than bool_mat_nilpotency_degree; for an empty matrix it returns −1-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 AA has the sparsity pattern of a real or complex matrix MM and Bij≠−2B_{ij} \ne -2, then (Mk)ij=0(M^k)_{ij} = 0 for all k>Bijk > B_{ij}, so the tail ∑k>Nak(Mk)ij\sum_{k > N} a_k (M^k)_{ij} of a power series vanishes for N≥BijN \ge B_{ij}.

A square Boolean matrix AA of order nn is the adjacency matrix of a directed graph on the vertices 0,…,n−10, \ldots, n - 1, and the Boolean product of matrices composes walks: (Ak)ij=1(A^k)_{ij} = 1 exactly when there is a walk of length kk from ii to jj. The four functions described here compute properties of all powers of AA at once from the structure of the graph, without forming the powers.

  • bool_mat_transitive_closure gives ∑k≥1Ak\sum_{k \ge 1} A^k, the reachability relation, by Warshall's algorithm [War1962] in O(n3)O(n^3) operations.
  • bool_mat_get_strongly_connected_components partitions 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(n2)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_degree finds the least kk with Ak=0A^k = 0. Such a kk 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_walk refines this to each pair of vertices: the largest kk with (Ak)ij=1(A^k)_{ij} = 1, or −1-1 if there is none, or −2-2 if there is none largest. A pair (i,j)(i, j) has arbitrarily long walks when some walk from ii to jj 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 MM is a real or complex matrix with the sparsity pattern AA, then (Mk)ij=0(M^k)_{ij} = 0 whenever (Ak)ij=0(A^k)_{ij} = 0, so the entry (i,j)(i, j) of ∑kakMk\sum_k a_k M^k is a finite sum when Bij≥0B_{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→10 \to 1, 1→21 \to 2, 2→12 \to 1 and 1→31 \to 3 has the components {3}\{3\}, {1,2}\{1, 2\} and {0}\{0\}, numbered 0, 1 and 2. Every walk into the cycle 1→2→11 \to 2 \to 1 can be made arbitrarily long. Removing the edge 2→12 \to 1 leaves an acyclic graph whose longest path, 0→1→30 \to 1 \to 3 (or 0→1→20 \to 1 \to 2), has two edges, so A3=0A^3 = 0.

Examplebool_mat_graphs.cchecked by make checkdoc
#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;
}
Output
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-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.
Cite this page

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-11-02).