FLINT3.7.0-dev

Computing echelon forms and nullspaces

Synopsis#include <flint/nmod_mat.h>
slong nmod_mat_rref(nmod_mat_t A)source

Puts AA in reduced row echelon form and returns its rank.

slong nmod_mat_nullspace(nmod_mat_t X, const nmod_mat_t A)source

Sets the first dd columns of XX to a basis of the right nullspace {x:Ax=0}\{x : Ax = 0\} of the m×nm \times n matrix AA, sets the other entries of XX to zero, and returns the nullity d=n−rank⁡(A)d = n - \operatorname{rank}(A). XX must have nn rows and at least dd columns; n×nn \times n always suffices. XX may be aliased with AA. The basis is the one with an identity matrix in the rows of the columns of AA without pivot, computed with nmod_mat_ple and nmod_mat_ple_nullspace.

slong nmod_mat_left_nullspace(nmod_mat_t X, const nmod_mat_t A)source

Sets the first dd rows of XX to a basis of the left nullspace {v:vA=0}\{v : vA = 0\} of the m×nm \times n matrix AA, sets the other entries of XX to zero, and returns the nullity d=m−rank⁡(A)d = m - \operatorname{rank}(A). XX must have at least dd rows and mm columns; m×mm \times m always suffices. The basis is the one with an identity matrix in the columns of the rows of AA that are not in its row rank profile, computed with nmod_mat_ple and nmod_mat_ple_left_nullspace.

The reduced row echelon form of a matrix over Z/pZ\mathbb{Z}/p\mathbb{Z} is the unique matrix row equivalent to it in which each nonzero row starts with a 1 (its pivot), the pivots move strictly to the right from row to row, the zero rows come last, and each pivot is the only nonzero entry in its column. nmod_mat_rref computes it in place, and nmod_mat_nullspace and nmod_mat_left_nullspace compute the bases of the nullspaces that it defines, for the matrix and for its transpose. The modulus must be prime.

AlgorithmGaussian elimination and LU decompositionLU decomposition with row pivoting over Z/pZ\mathbb{Z}/p\mathbb{Z} in its classical, delayed-reduction and recursive forms, and how FLINT derives triangular solving, linear system solving, inverses, determinants, ranks, echelon forms and nullspaces from it; plus division-free determinants and the Howell form over Z/nZ\mathbb{Z}/n\mathbb{Z}.

Algorithm#

nmod_mat_rref does not use Gauss–Jordan elimination. For an m×nm \times n matrix AA it

  1. computes an LU decomposition with nmod_mat_lu_with_pivots, which gives the rank rr, the row echelon form UU (the first rr rows) and the pivot columns c0<⋯<cr−1c_0 < \cdots < c_{r-1};
  2. clears the entries of LL;
  3. if r<nr < n, writes U=(U1∣U2)U = (U_1 \mid U_2) up to a column permutation, with U1U_1 the r×rr \times r upper triangular matrix of pivot columns and U2U_2 the other n−rn - r columns, and replaces U2U_2 by U1−1U2U_1^{-1} U_2 with nmod_mat_solve_triu;
  4. sets the pivot columns to the columns of the identity matrix.

The zero rows of the result come last. The cost is that of the LU decomposition plus one triangular solve, O(mnrω−2)O(m n r^{\omega - 2}) field operations with the exponent ω\omega of matrix multiplication [JPS2013]. A matrix with one row is just divided by its first nonzero entry.

Nullspaces#

Let RR be the reduced row echelon form of AA, with pivot columns c0,…,cr−1c_0, \ldots, c_{r-1} and free columns f0<⋯<fd−1f_0 < \cdots < f_{d-1}, where d=n−rd = n - r. For each free column fif_i, the vector xx with xfi=1x_{f_i} = 1, xfj=0x_{f_j} = 0 for j≠ij \ne i and xck=−Rk,fix_{c_k} = -R_{k, f_i} satisfies Rx=0Rx = 0, hence Ax=0Ax = 0, and these dd vectors form a basis of the right nullspace. nmod_mat_nullspace(X, A) writes them to the first dd columns of XX in this order, sets the remaining entries of XX to zero and returns dd. XX must have nn rows and at least dd columns. Since dd is not known in advance, an n×nn \times n matrix is the usual choice. This basis is the unique one whose vectors have the entries xfj=δijx_{f_j} = \delta_{ij} in the free positions, so it depends only on AA. nmod_mat_nullspace computes it without forming RR, from the PLE decomposition computed by nmod_mat_ple, with nmod_mat_ple_nullspace: one triangular solve, as for RR (see Kernels).

nmod_mat_left_nullspace(X, A) writes to the first d=m−rd = m - r rows of XX, which must have at least dd rows and mm columns, the basis of {v:vA=0}\{v : vA = 0\} obtained in the same way from the transpose of AA: the unique one whose vectors have the entries δij\delta_{ij} at the rows of AA that are not in its row rank profile. Since nmod_mat_ple reveals the row rank profile, this basis is computed from the PLE decomposition of AA itself, with nmod_mat_ple_left_nullspace, without transposing AA.

Example#

A 3×53 \times 5 matrix of rank 2 modulo 13:

Examplenmod_mat_rref_nullspace.cchecked by make checkdoc
#include <flint/flint.h>
#include <flint/nmod_mat.h>

int main(void)
{
    /* a 3 x 5 matrix of rank 2 modulo 13: row 2 = 2 row 0 + row 1 */
    ulong a[3][5] = { { 2, 4, 1, 3, 0 },
                      { 1, 2, 5, 0, 7 },
                      { 5, 10, 7, 6, 7 } };
    nmod_mat_t A, R, X, Y, T;
    slong i, j, rank, nullity;

    nmod_mat_init(A, 3, 5, 13);
    for (i = 0; i < 3; i++)
        for (j = 0; j < 5; j++)
            nmod_mat_entry(A, i, j) = a[i][j];

    nmod_mat_init_set(R, A);
    rank = nmod_mat_rref(R);
    flint_printf("rank %wd, reduced row echelon form:\n", rank);
    nmod_mat_print_pretty(R);

    /* right nullspace: the first 'nullity' columns of X */
    nmod_mat_init(X, 5, 5, 13);
    nullity = nmod_mat_nullspace(X, A);
    flint_printf("nullity %wd, basis in the columns of:\n", nullity);
    nmod_mat_print_pretty(X);

    nmod_mat_init(T, 3, 5, 13);
    nmod_mat_mul(T, A, X);
    flint_printf("A X is zero: %d\n", nmod_mat_is_zero(T));

    /* left nullspace: the first rows of Y */
    nmod_mat_init(Y, 3, 3, 13);
    nullity = nmod_mat_left_nullspace(Y, A);
    flint_printf("left nullity %wd, first row (%wu %wu %wu)\n", nullity,
        nmod_mat_entry(Y, 0, 0), nmod_mat_entry(Y, 0, 1), nmod_mat_entry(Y, 0, 2));
    nmod_mat_mul(T, Y, A);
    flint_printf("Y A is zero: %d\n", nmod_mat_is_zero(T));

    nmod_mat_clear(A);
    nmod_mat_clear(R);
    nmod_mat_clear(X);
    nmod_mat_clear(Y);
    nmod_mat_clear(T);
    return 0;
}
Output
rank 2, reduced row echelon form:
<3 x 5 integer matrix mod 13>
[ 1  2  0  6  5]
[ 0  0  1  4  3]
[ 0  0  0  0  0]
nullity 3, basis in the columns of:
<5 x 5 integer matrix mod 13>
[11  7  8  0  0]
[ 1  0  0  0  0]
[ 0  9 10  0  0]
[ 0  1  0  0  0]
[ 0  0  1  0  0]
A X is zero: 1
left nullity 1, first row (11 12 1)
Y A is zero: 1

The pivot columns are 0 and 2, the free columns 1, 3 and 4. The first basis vector of the nullspace is (−2,1,0,0,0)(-2, 1, 0, 0, 0), read off column 1 of RR. The left nullspace is spanned by (−2,−1,1)(-2, -1, 1), since row 2 of AA is twice row 0 plus row 1.

Guides#

References#

  • [JPS2013] C.-P. Jeannerod, C. Pernet and A. Storjohann, "Rank-profile revealing Gaussian elimination and the CUP matrix decomposition", Journal of Symbolic Computation 56 (2013), 46-68, https://doi.org/10.1016/j.jsc.2013.04.004
Cite this page

The FLINT team, “Computing echelon forms and nullspaces (nmod_mat.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/nmod_mat/rref.html (generated 2026-11-02).