Computing echelon forms and nullspaces
#include <flint/nmod_mat.h>slong nmod_mat_rref(nmod_mat_t A)sourcePuts in reduced row echelon form and returns its rank.
slong nmod_mat_nullspace(nmod_mat_t X, const nmod_mat_t A)sourceSets the first columns of to a basis of the right nullspace of the matrix , sets the other entries of to zero, and returns the nullity . must have rows and at least columns; always suffices. may be aliased with . The basis is the one with an identity matrix in the rows of the columns of 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)sourceSets the first rows of to a basis of the left nullspace of the matrix , sets the other entries of to zero, and returns the nullity . must have at least rows and columns; always suffices. The basis is the one with an identity matrix in the columns of the rows of 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 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.
Algorithm#
nmod_mat_rref does not use Gauss–Jordan elimination. For an matrix it
- computes an LU decomposition with
nmod_mat_lu_with_pivots, which gives the rank , the row echelon form (the first rows) and the pivot columns ; - clears the entries of ;
- if , writes up to a column permutation, with the upper triangular matrix of pivot columns and the other columns, and replaces by with
nmod_mat_solve_triu; - 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, field operations with the exponent of matrix multiplication [JPS2013]. A matrix with one row is just divided by its first nonzero entry.
Nullspaces#
Let be the reduced row echelon form of , with pivot columns and free columns , where . For each free column , the vector with , for and satisfies , hence , and these vectors form a basis of the right nullspace. nmod_mat_nullspace(X, A) writes them to the first columns of in this order, sets the remaining entries of to zero and returns . must have rows and at least columns. Since is not known in advance, an matrix is the usual choice. This basis is the unique one whose vectors have the entries in the free positions, so it depends only on . nmod_mat_nullspace computes it without forming , from the PLE decomposition computed by nmod_mat_ple, with nmod_mat_ple_nullspace: one triangular solve, as for (see Kernels).
nmod_mat_left_nullspace(X, A) writes to the first rows of , which must have at least rows and columns, the basis of obtained in the same way from the transpose of : the unique one whose vectors have the entries at the rows of 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 itself, with nmod_mat_ple_left_nullspace, without transposing .
Example#
A matrix of rank 2 modulo 13:
#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;
}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 , read off column 1 of . The left nullspace is spanned by , since row 2 of 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
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).
@misc{flint-doc-nmod-mat-rref,
author = {{The FLINT team}},
title = {{Computing echelon forms and nullspaces (nmod\_mat.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/nmod_mat/rref.html},
note = {Generated 2026-11-02}
}