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=ABC = AB, choosing between the methods below from the dimensions, the lengths of the entries and the modulus. CC may be AA or BB. 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=ABC = 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. CC may be AA or BB.

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=ABC = AB by evaluating AA and BB at L=ℓA+ℓB−1L = \ell_A + \ell_B - 1 points (where ℓA\ell_A, ℓB\ell_B are the largest lengths of the entries), multiplying the LL pairs of matrices over Z/nZ\mathbb{Z}/n\mathbb{Z} with nmod_mat_mul and interpolating. Requires nn to be a prime with n≥Ln \ge L; throws if n<Ln < 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 CC to the coefficients of degrees zlz_l to zh−1z_h - 1 of the corresponding entry of ABAB (the product shifted right by zlz_l and truncated to length zh−zlz_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 CC may be partially written. CC may be AA or BB.

Each entry of AA and BB is transformed once in the transformed polynomial ring of gr (gr_ctx_init_gr_poly_transformed_repr), the kk 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 zl=0z_l = 0 and zh=ℓA+ℓB−1z_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 Z/nZ\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 Z[x]\mathbb{Z}[x] and (Z/nZ)[x](\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 dd be the smallest of the three dimensions, ℓA\ell_A and ℓB\ell_B the largest lengths of the entries of AA and BB, and ℓ=min⁡(ℓA,ℓB)\ell = \min(\ell_A, \ell_B).

ConditionMethod
d≥2d \ge 2, ℓ≥2d\ell \ge 2d, ℓ≥c\ell \ge cnmod_poly_mat_mulmid_fft_small, if it succeeds
d<10d < 10nmod_poly_mat_mul_classical
nn prime, log⁡2n>β/4\log_2 n > \beta/4, n≥ℓA+ℓB−1n \ge \ell_A + \ell_B - 1, d>60+ℓd > 60 + \sqrt{\ell}nmod_poly_mat_mul_interpolate
max⁡(ℓA,ℓB)>128\max(\ell_A, \ell_B) > 128nmod_poly_mat_mul_classical
otherwisenmod_poly_mat_mul_KS

Here c=64c = 64, or 32 if nn has more than 40 bits, and cc is doubled for d=2d = 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 zl,…,zh−1z_l, \ldots, z_h - 1 of each entry of ABAB, as the entries of CC (shifted down by zlz_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+knmk + kn input entries and the mnmn outputs once, plus mknmkn 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×44 \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-11-02).