FLINT3.7.0-dev
Overview

Multiplying matrices over the integers

Multiplying an m×km \times k by a k×nk \times n integer matrix with entries of aa and bb bits produces entries of up to c=a+b+log⁡2kc = a + b + \log_2 k bits. Two parameters decide the best method: the dimension d=min⁡(m,k,n)d = \min(m, k, n) and the entry size. For small entries the cost is dominated by memory traffic and by the overhead of the fmpz representation; for large matrices of moderate entries, by the mknmkn multiply-adds; for large entries, by the cost of each integer multiplication. fmpz_mat_mul implements the choice; the exact tests are in How the method is chosen.

The generic gr_mat_mul over the integers (gr_ctx_init_fmpz) calls fmpz_mat_mul. Conversely, several other modules reduce their matrix products to it: fmpq_mat_mul after clearing denominators of rows and columns, fmpz_mod_mat_mul before reducing, fmpz_poly_mat_mul_KS and nmod_poly_mat_mul_KS after Kronecker substitution, arb_mat_mul_block and nfloat's matrix multiplication on blocks of exponent-aligned mantissas, and padic_mat_mul.

The methods#

MethodFunctionCostBest for
Dot products in machine words_fmpz_mat_mul_small_internal, _fmpz_mat_mul_double_word_internalmknmkn word multiply-adds, 1 to 5 wordsentries up to two words, dd up to a few hundred
Classical dot productsfmpz_mat_mul_classicalmknmkn multiplications of entriestiny kk, irregular entries
Waksmanfmpz_mat_mul_waksman≈mkn/2\approx mkn/2 multiplications, O(mkn)O(mkn) additionsd<20d < 20, balanced entries of thousands of bits
Strassen–Winogradfmpz_mat_mul_strassenO(dlog⁡27)O(d^{\log_2 7}) multiplications of entriesd≥8d \ge 8, entries of hundreds of bits
Floating-point gemmfmpz_mat_mul_blas≈(c/20)\approx (c / 20) dgemm calls, or one if c≤53c \le 53large dd, cc up to a few hundred bits
Multimodularfmpz_mat_mul_multi_mod≈c/59\approx c/59 products over Z/pZ\mathbb{Z}/p\mathbb{Z}, plus reductionslarge dd, entries of up to thousands of bits
Transformed entriesfmpz_mat_mul_fft_smallmk+kn+mnmk + kn + mn transforms, mknmkn pointwise productsentries of many thousands of bits
Schönhage–Strassen entriesfmpz_mat_mul_fftas above, with the fft modulenot used by fmpz_mat_mul

Machine words#

When all entries fit in a signed word minus two bits (small fmpz), the product can be computed without touching the multiprecision machinery: the kernels read the words, multiply them with one hardware multiplication, and accumulate in one, two or three words depending on the bound cc. _fmpz_mat_mul_small_internal transposes BB into an array of words and processes blocks of 16 rows of AA against 128 entries of the inner dimension, which keeps the working set in cache, and splits the rows over threads. Entries of up to two words are handled likewise by _fmpz_mat_mul_double_word_internal, which multiplies two-word integers with four word products and accumulates in four or five words. For dimensions above about 1000 these lose to Strassen–Winograd (when cc fits in a word) or to multimodular multiplication.

Fewer multiplications#

For entries of thousands of bits, each multiplication of entries costs M(b)M(b), much more than an addition, so algorithms that trade multiplications for additions win for small dimensions. Waksman's algorithm [Waksman1970] computes, for each pair of inner indices, the products (ai,2l+b2l+1,j)(ai,2l+1+b2l,j)(a_{i,2l} + b_{2l+1,j})(a_{i,2l+1} + b_{2l,j}), whose sum over ll is the dot product plus terms depending only on row ii or column jj; it needs mkn/2+O(mk+kn)mkn/2 + O(mk + kn) multiplications. Strassen–Winograd [Str1969; Win1971], in the operation order of Bodrato [Bodrato2010], needs 7 instead of 8 half-size products per level at the cost of 15 additions; FLINT applies one level and recurses through fmpz_mat_mul. See Strassen–Winograd multiplication.

Products modulo primes#

The entries of CC are less than 2c2^c in absolute value, so CC is determined by CC modulo moduli p1,…,prp_1, \ldots, p_r with product at least 2c+12^{c + 1} (or 2c2^c if all entries are nonnegative). The multimodular method reduces AA and BB modulo each pip_i, multiplies with nmod_mat_mul, and reconstructs CC by the Chinese remainder theorem (Chinese remaindering). FLINT uses the modulus 2632^{63}, for which reduction is free, and primes above 2592^{59}, so r≈1+(c−63)/59r \approx 1 + (c - 63)/59; if c≤63c \le 63, the single modulus 2c2^c.

Proposition 1. For d×dd \times d matrices with entries of bb bits, multimodular multiplication costs

O(r⋅dω+d2⋅T(b)),r=O(b/β+log⁡d/β+1),O\big(r \cdot d^\omega + d^2 \cdot T(b)\big), \qquad r = O(b/\beta + \log d / \beta + 1),

word operations, where dωd^\omega is the cost of nmod_mat_mul and T(b)=O(M(b)log⁡(b/β))T(b) = O(M(b) \log(b/\beta)) that of reducing a bb-bit integer modulo rr primes, or of reconstructing it, with a subproduct tree.

Proof. There are 2d22d^2 entries to reduce and d2d^2 to reconstruct, each by a tree of products of the rr moduli [GG2003, §10.3], and rr matrix products modulo word-size moduli.

Compared with the d3M(b)d^3 M(b) of the classical algorithm, the matrix products cost dωd^\omega word operations per prime, that is, about dωb/βd^\omega b/\beta in total, which for bb up to a few thousand bits is much less than d3M(b)d^3 M(b), and the conversions are only quadratic in dd. Since nmod_mat_mul runs at nearly the floating-point peak of the processor (Multiplying matrices over Z/nZ), this is the method of choice for large matrices.

Floating point#

fmpz_mat_mul_blas is a multimodular method with primes small enough for a double matrix product to be exact: residues in the symmetric range below p/2p/2 with k(p/2)2<253k (p/2)^2 < 2^{53}, so that every dot product is computed exactly (Exactness). That gives primes of about 26−12log⁡2k26 - \frac12 \log_2 k bits (at most about 21 bits when c>200c > 200, to keep the reconstruction cheap), and about c/(26−12log⁡2k)c/(26 - \frac12 \log_2 k) calls to flint_dgemm. If a+b+log⁡2k≤53a + b + \log_2 k \le 53, a single dgemm on the entries suffices. With a tuned BLAS library or FLINT's own kernels (Floating-point matrix multiplication (gemm)) this is the fastest method for large matrices with entries up to a few hundred bits: fmpz_mat_mul uses it for d>50d > 50 when c≤53c \le 53, and from about d=250d = 250 to 13001300 depending on cc.

Transformed entries#

For entries of b≫1000b \gg 1000 bits, each product of entries costs a multiplication of large integers, which with an FFT means two forward transforms, a pointwise product and an inverse transform. In a matrix product each entry of AA takes part in nn products and each entry of BB in mm, so transforming every entry once and adding the kk pointwise products of each entry of CC in the transformed domain saves almost all transforms.

Proposition 2. With transforms of length L=O(b/log⁡b)L = O(b/\log b) points, multiplying through transformed entries costs

O((mk+kn+mn) Llog⁡L+mkn L)O\big((mk + kn + mn) \, L \log L + mkn \, L\big)

operations, against O(mkn Llog⁡L)O(mkn \, L \log L) for multiplying the entries separately.

Proof. Each of the mk+knmk + kn input entries and mnmn output entries is transformed once, and each of the mknmkn products is a pointwise product of length LL. The transform length must accommodate the accumulated sum of kk products, which adds log⁡2k\log_2 k bits to the coefficients and changes LL by a constant factor.

fmpz_mat_mul_fft_small implements this with the transformed integer ring of gr, built on the small-prime FFT of fft_small.h (Sums of products and fixed operands). The transforms of the inputs must be stored, (mk+kn)L(mk + kn) L numbers, which the function limits with flint_fft_small_max_transformed_ring_size, falling back to row and column blocks and, if necessary, to blocks of the inner dimension. fmpz_mat_mul uses it when the smaller entry size exceeds about 7000+0.15d27000 + 0.15 d^2 bits (10000 for d≤5d \le 5). The older fmpz_mat_mul_fft does the same with the Schönhage–Strassen transform of the fft.h module, for entries of at least 2000 bits.

Squaring and powers#

For A2A^2, the transformed method keeps one pool of transforms, and Bodrato's variant of Strassen–Winograd [Bodrato2010] uses squarings of half-size blocks where possible. fmpz_mat_sqr applies Bodrato's formulas for dimensions up to 3 (and 4 with large entries) and otherwise fmpz_mat_mul. fmpz_mat_pow uses binary powering.

  • fmpq_mat_mul multiplies diag⁡(r)−1A′\operatorname{diag}(r)^{-1} A' and B′diag⁡(s)−1B' \operatorname{diag}(s)^{-1}, where A′A' and B′B' are AA and BB with rows, respectively columns, multiplied by their least common denominators rir_i and sjs_j, so that one integer product suffices.
  • Polynomial matrices over Z\mathbb{Z} and Z/nZ\mathbb{Z}/n\mathbb{Z} are multiplied by Kronecker substitution into integer matrices, or by evaluation and interpolation; see Matrices of polynomials.

Referenced from#

References#

  • [Waksman1970] Waksman, Abraham, "On Winograd's algorithm for inner products", IEEE Transactions on Computers 19.04 (1970): 360-361.
  • [Str1969] V. Strassen, "Gaussian elimination is not optimal", Numerische Mathematik 13 (1969), 354-356, https://doi.org/10.1007/BF02165411
  • [Win1971] S. Winograd, "On multiplication of 2 × 2 matrices", Linear Algebra and its Applications 4 (1971), 381-388, https://doi.org/10.1016/0024-3795(71)90009-7
  • [Bodrato2010] Bodrato, Marco : A Strassen-like Matrix Multiplication Suited for Squaring and Higher Power Computation. Proceedings of the ISSAC 2010 München, Germany, 25-28 July, 2010
  • [GG2003] J. von zur Gathen and J. Gerhard, Modern Computer Algebra, second edition, Cambridge University Press (2003)
Cite this page

The FLINT team, “Multiplying matrices over the integers,” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/algorithms/linear-algebra/integer-matrix-multiplication.html (generated 2026-11-02).