FLINT3.7.0-dev

Multiplying polynomial matrices modulo n

Synopsis#include <flint/nmod_poly_mat.h>
void nmod_poly_mat_mul(nmod_poly_mat_t C, const nmod_poly_mat_t A, const nmod_poly_mat_t B)source

Sets C = AB, choosing between the methods below from the dimensions, the lengths of the entries and the modulus. C may be A or B. See Choice.

void nmod_poly_mat_mul_classical(nmod_poly_mat_t C, const nmod_poly_mat_t A, const nmod_poly_mat_t B)source
void nmod_poly_mat_mul_KS(nmod_poly_mat_t C, const nmod_poly_mat_t A, const nmod_poly_mat_t B)source

Sets C = AB by sums of polynomial products, respectively by Kronecker substitution: the entries are packed into integers with nmod_poly_bit_pack, multiplied with fmpz_mat_mul and unpacked. C may be A or B.

void nmod_poly_mat_mul_interpolate(nmod_poly_mat_t C, const nmod_poly_mat_t A, const nmod_poly_mat_t B)source

Sets C = AB by evaluating A and B at L = \ell_A + \ell_B - 1 points (where \ell_A, \ell_B are the largest lengths of the entries), multiplying the L pairs of matrices over \mathbb{Z}/n\mathbb{Z} with nmod_mat_mul and interpolating. Requires n to be a prime with n \ge L; throws if n < L.

int nmod_poly_mat_mulmid_fft_small(nmod_poly_mat_t C, const nmod_poly_mat_t A, const nmod_poly_mat_t B, slong zl, slong zh)source

Sets each entry of C to the coefficients of degrees z_l to z_h - 1 of the corresponding entry of AB (the product shifted right by z_l and truncated to length z_h - z_l), and returns 1; returns 0 if the small-prime FFT is not available or no transform plan admits the size of the accumulated products, in which case C may be partially written. C may be A or B.

Each entry of A and B is transformed once in the transformed polynomial ring of gr (gr_ctx_init_gr_poly_transformed_repr), the k pointwise products of each output entry are accumulated in the transformed domain, and only the requested window is reconstructed by Chinese remaindering. The work is spread over threads. If the transforms of all inputs do not fit in the storage budget of fft_small.h, the product is computed in blocks of rows and columns. With z_l = 0 and z_h = \ell_A + \ell_B - 1 this is the full product, which is how nmod_poly_mat_mul uses it for long entries.

nmod_poly_mat_mul multiplies matrices whose entries are polynomials over \mathbb{Z}/n\mathbb{Z}. Depending on the dimensions, the lengths of the entries and the modulus, it computes the product entry by entry with polynomial multiplications, as one large integer matrix product by Kronecker substitution, as many small matrix products by evaluation and interpolation, or with the small-prime FFT, transforming each entry only once. All methods give the same result.

AlgorithmMatrices of polynomialsHow FLINT multiplies matrices over \mathbb{Z}[x] and (\mathbb{Z}/n\mathbb{Z})[x] — classical products of polynomials, Kronecker substitution into one integer matrix product, evaluation and interpolation, and transforming each entry once with the small-prime FFT — and how it computes determinants, ranks, inverses and solutions by fraction-free elimination or by evaluation and interpolation.

Choice of method#

Let d be the smallest of the three dimensions, \ell_A and \ell_B the largest lengths of the entries of A and B, and \ell = \min(\ell_A, \ell_B).

ConditionMethod
d \ge 2, \ell \ge 2d, \ell \ge cnmod_poly_mat_mulmid_fft_small, if it succeeds
d < 10nmod_poly_mat_mul_classical
n prime, \log_2 n > \beta/4, n \ge \ell_A + \ell_B - 1, d > 60 + \sqrt{\ell}nmod_poly_mat_mul_interpolate
\max(\ell_A, \ell_B) > 128nmod_poly_mat_mul_classical
otherwisenmod_poly_mat_mul_KS

Here c = 64, or 32 if n has more than 40 bits, and c is doubled for d = 2. The classical method pays off for small dimensions, where there is little to share between the products of entries; Kronecker substitution for short entries, where it produces integer matrices with entries of a few hundred bits that fmpz_mat_mul multiplies efficiently (Multiplying matrices over the integers); evaluation and interpolation for large matrices over a large prime field, where the fast nmod_mat_mul kernels do the work; and the transformed method for long entries.

Window of coefficients#

nmod_poly_mat_mulmid_fft_small(C, A, B, zl, zh) computes only the coefficients of degrees z_l, \ldots, z_h - 1 of each entry of AB, as the entries of C (shifted down by z_l). This is the middle product used, for example, in Newton iterations for matrix power series, where the low part of a product is known in advance. Its cost is that of transforming the mk + kn input entries and the mn outputs once, plus mkn pointwise products, with transforms of the length needed for the window; see Windows and wrap-around. It returns 0 when fft_small is not available or no plan fits the accumulation bound; the caller must then use another method.

Example#

Products of 4 \times 4 matrices with entries of length 150 modulo a 62-bit prime, by all methods, and a window of coefficients:

Examplenmod_poly_mat_mul_methods.cchecked by make checkdoc
#include <flint/flint.h>
#include <flint/ulong_extras.h>
#include <flint/nmod_poly.h>
#include <flint/nmod_poly_mat.h>

int main(void)
{
    nmod_poly_mat_t A, B, C, D;
    flint_rand_t state;
    ulong p = n_nextprime(UWORD(1) << 61, 1);
    slong i, j, d = 4, len = 150;
    int ok, all = 1;

    flint_rand_init(state);
    nmod_poly_mat_init(A, d, d, p);
    nmod_poly_mat_init(B, d, d, p);
    nmod_poly_mat_init(C, d, d, p);
    nmod_poly_mat_init(D, d, d, p);
    nmod_poly_mat_rand(A, state, len);
    nmod_poly_mat_rand(B, state, len);

    nmod_poly_mat_mul(C, A, B);
    flint_printf("degree of product: %wd\n", nmod_poly_mat_degree(C));

    nmod_poly_mat_mul_classical(D, A, B);
    flint_printf("classical:   %d\n", nmod_poly_mat_equal(C, D));
    nmod_poly_mat_mul_KS(D, A, B);
    flint_printf("KS:          %d\n", nmod_poly_mat_equal(C, D));
    nmod_poly_mat_mul_interpolate(D, A, B);
    flint_printf("interpolate: %d\n", nmod_poly_mat_equal(C, D));

    /* coefficients 100 to 199 of each entry */
    ok = nmod_poly_mat_mulmid_fft_small(D, A, B, 100, 200);
    if (ok)
    {
        for (i = 0; i < d; i++)
            for (j = 0; j < d; j++)
            {
                nmod_poly_t t;
                nmod_poly_init(t, p);
                nmod_poly_shift_right(t, nmod_poly_mat_entry(C, i, j), 100);
                nmod_poly_truncate(t, 100);
                all = all && nmod_poly_equal(t, nmod_poly_mat_entry(D, i, j));
                nmod_poly_clear(t);
            }
    }
    flint_printf("mulmid:      %d\n", all);

    nmod_poly_mat_clear(A);
    nmod_poly_mat_clear(B);
    nmod_poly_mat_clear(C);
    nmod_poly_mat_clear(D);
    flint_rand_clear(state);
    return 0;
}
Output
degree of product: 298
classical:   1
KS:          1
interpolate: 1
mulmid:      1

Guides#

Cite this page

The FLINT team, “Multiplying polynomial matrices modulo n (nmod_poly_mat.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/nmod_poly_mat/mul.html (generated 2026-10-20).