FLINT3.7.0-dev
Algorithm

Monomial orderings and exponent packing

Cost
Comparison, multiplication and divisibility test of packed monomials cost O(N) word operations for N words per monomial; with k fields of width b \le 64 on a 64-bit machine, N = \lceil k / \lfloor 64/b \rfloor \rceil.

Every sparse multivariate module of FLINT (fmpz_mpoly, fmpq_mpoly, nmod_mpoly, fmpz_mod_mpoly, fq_nmod_mpoly, fq_zech_mpoly and the generic gr_mpoly) uses the same distributed representation: a polynomial is a list of terms c_i x^{e_i} with nonzero coefficients, sorted so that x^{e_0} > x^{e_1} > \cdots for a fixed monomial ordering, with the coefficients in one array and the exponent vectors in another. The exponent vectors are packed: several exponents share a machine word, in a layout chosen so that the monomial operations needed by the algorithms become a few word operations. This design follows Monagan and Pearce [MonPea2007], who showed that packing is a main factor in the speed of sparse polynomial arithmetic, and Bachmann and Schönemann [BacSch1998] for the treatment of orderings. The functions are in the module mpoly.h, and the notes of Schultz [Sch-mpoly] give further details.

Monomial orderings#

A monomial ordering is a total order < on the monomials x^e = x_0^{e_0} \cdots x_{n-1}^{e_{n-1}}, e \in \mathbb{N}^n, such that 1 \le x^e and x^a < x^b \Rightarrow x^{a+c} < x^{b+c}. Such an order is a well-order (Dickson's lemma), which is what makes the division algorithm and the heap algorithms terminate [CLO2015, §2.2]. FLINT supports three, given by ordering_t, with |e| = e_0 + \cdots + e_{n-1}:

  • ORD_LEX: x^a > x^b if the first nonzero entry of a - b is positive;
  • ORD_DEGLEX: x^a > x^b if |a| > |b|, or |a| = |b| and x^a > x^b in lex order;
  • ORD_DEGREVLEX: x^a > x^b if |a| > |b|, or |a| = |b| and the last nonzero entry of a - b is negative.

The variable x_0 is the most significant one. The leading term \operatorname{lt}(A), monomial \operatorname{lm}(A) and coefficient \operatorname{lc}(A) refer to the first term in this order. The GCD and factoring code converts its inputs to ORD_LEX, in which the leading coefficient with respect to x_0 is a polynomial in the other variables, as the recursive view R[x_1, \ldots][x_0] requires.

Packed monomials#

Fix a field width b. The exponent vector e is stored as a sequence of fields f_0, f_1, \ldots, f_{k-1}, each a b-bit unsigned integer, where k = n for ORD_LEX and k = n + 1 for the degree orderings:

  • for ORD_LEX and ORD_DEGLEX, field n - 1 - i holds e_i, so that e_0 is in the most significant position among the variables;
  • for ORD_DEGREVLEX, field i holds e_i;
  • for the degree orderings, field n holds the total degree |e|.

The fields are concatenated, field 0 in the least significant bits, into the integer

\Phi(e) = \sum_{j < k} f_j \, 2^{b j},

which is stored in N words, least significant word first. Two layouts are used:

  • single precision, 8 \le b \le FLINT_BITS: a word holds \lfloor \mathrm{FLINT\_BITS}/b \rfloor fields, fields do not straddle words, and unused high bits of a word are zero; N = \lceil k / \lfloor \mathrm{FLINT\_BITS}/b \rfloor \rceil;
  • multiprecision, b a multiple of FLINT_BITS: each field takes b/\mathrm{FLINT\_BITS} words and N = k b / \mathrm{FLINT\_BITS}.

In both cases the fields must satisfy f_j < 2^{b-1}: the top bit of each field is a guard bit, which is zero in every stored monomial. When b does not divide FLINT_BITS the words are not literally the digits of \Phi(e), because of the unused bits, but all statements below hold word by word, and we keep writing \Phi for the packed form.

The width is chosen by mpoly_fix_bits, which rounds a required number of bits up to the largest b with the same number of words: three variables in lex order with 64-bit words give b = 21, two give b = 32. A polynomial starts with b = 8 (MPOLY_MIN_BITS), and an operation whose result needs larger fields repacks with a larger width. All monomials of a polynomial use the same width, recorded in its bits field.

Comparison#

Let cmpmask be zero for ORD_LEX and ORD_DEGLEX, and for ORD_DEGREVLEX the pattern with all bits of fields 0, \ldots, n-1 set and those of field n clear (mpoly_get_cmpmask).

Lemma 1 (Comparison of packed monomials). For each of the three orderings, x^a > x^b if and only if \Phi(a) \oplus \mathit{cmpmask} > \Phi(b) \oplus \mathit{cmpmask} as unsigned integers, where \oplus is the bitwise exclusive or.

Proof. Comparing the integers is comparing the fields lexicographically from the most significant one. For ORD_LEX the fields from the top are e_0, e_1, \ldots, which is the definition. For ORD_DEGLEX the total degree comes first, then e_0, e_1, \ldots. For ORD_DEGREVLEX the top field is the total degree, and the remaining fields from the top are e_{n-1}, \ldots, e_0, each complemented by the mask. Complementing a b-bit field reverses the order of its values, so for equal degrees \Phi(a) \oplus \mathit{cmpmask} > \Phi(b) \oplus \mathit{cmpmask} if and only if, at the last index i with a_i \ne b_i, a_i < b_i. That is the definition of reverse lexicographic order.

So comparing monomials costs at most N word comparisons (mpoly_monomial_cmp), and a single one in the frequent case N = 1.

The layout relies only on four properties: multiplying monomials adds fields, comparing them compares the packed words after an exclusive or with a mask, every exponent is one of the fields, and all fields are nonnegative.

Multiplication and overflow#

Since every field, including the degree field, is additive, \Phi(a + c) = \Phi(a) + \Phi(c) as long as no field overflows, so monomials are multiplied by adding words (mpoly_monomial_add). The guard bits make overflow detectable.

Lemma 2 (Overflow). Let a, c have fields < 2^{b-1} and let M be the pattern with the guard bit of each field set (mpoly_overflow_mask_sp). In single precision the word sum \Phi(a) + \Phi(c) involves no carry between fields, and it is a valid packed monomial (all fields of a + c are < 2^{b-1}) if and only if (\Phi(a) + \Phi(c)) \mathbin{\&} M = 0.

Proof. Each field of the sum is < 2^{b-1} + 2^{b-1} = 2^b, so it fits in its b bits and nothing is carried into the next field. The field of a + c is then the field of the sum, and it is < 2^{b-1} exactly when its top bit is zero.

In multiprecision the guard bit is the top bit of the most significant word of each field (mpoly_monomial_overflows_mp). Algorithms usually avoid testing: before multiplying B by C they compute the maximum of each field over B and over C (mpoly_max_fields_fmpz), add them and choose b large enough for the sum, so that no product of monomials can overflow. Overflow tests are needed where results are not known in advance, as in exact division and in powering.

Divisibility, halving, minima and maxima#

Lemma 3 (Divisibility test). With a, c and M as above, x^c divides x^a if and only if the word difference \Phi(a) - \Phi(c) satisfies (\Phi(a) - \Phi(c)) \mathbin{\&} M = 0, and then \Phi(a) - \Phi(c) = \Phi(a - c).

Proof. If a_j \ge c_j for all fields, the subtraction proceeds field by field without borrows and gives \Phi(a - c), whose guard bits are zero. Otherwise let j be the lowest field with a_j < c_j. All lower fields are subtracted without borrow, and field j becomes a_j - c_j + 2^b > 2^b - 2^{b-1} = 2^{b-1}, borrowing from the field above, so its guard bit is set.

mpoly_monomial_divides performs this test (word by word, so a borrow never crosses a word boundary unnoticed), and the exact division code uses it to detect a leading monomial of the dividend that is not divisible by that of the divisor. The same trick gives, for square roots (mpoly_monomial_halves): x^a is a square if and only if the lowest bit of each word is zero and (\Phi(a) \gg 1) \mathbin{\&} M = 0, since shifting a word right by one moves the lowest bit of each field into the guard bit of the field below. Field-wise minima and maxima are computed without unpacking: in s = M + \Phi(a) - \Phi(c) each field equals 2^{b-1} + a_j - c_j \in [1, 2^b), so there are no borrows and the guard bit of field j is set exactly when a_j \ge c_j; spreading it over the field gives a mask selecting a_j or c_j (mpoly_monomial_min1, mpoly_monomial_max).

Other representations#

The distributed representation suits arithmetic, but some algorithms want a recursive or dense view, to which FLINT converts temporarily:

  • Recursive sparse: nmod_mpolyu_t and its relatives are sparse polynomials in one or two main variables whose coefficients are sparse polynomials in the others, and nmod_mpolyn_t is a sparse polynomial whose coefficients are dense univariate polynomials in the last variable. Brown's GCD algorithm works with the latter, Zippel's with the former (Greatest common divisors).
  • Univariate with multivariate coefficients: fmpz_mpoly_univar_t and its relatives, public types used for resultants, discriminants and contents.
  • Dense: fmpz_mpolyd_t stores all coefficients in a box of given degree bounds and is used by fmpz_mpoly_mul_dense; the array methods of multiplication and division use temporary dense arrays of coefficients indexed by the exponents (Sparse and dense multiplication).
  • Dense bivariate: fmpz_bpoly_t, n_bpoly_t and their relatives, arrays of univariate polynomials, used in bivariate factorisation and resultants.

Deflation, compression and repacking#

Many algorithms first simplify the exponents. Write s_i for the minimal exponent of x_i in A and t_i for the GCD of the differences e_i - s_i over the terms. Then A = x^s \, \iota_t(A') where \iota_t is the substitution x_i \mapsto x_i^{t_i} and A' is the deflated polynomial, with exponents (e_i - s_i)/t_i (mpoly_gcd_info_stride, fmpz_mpoly_deflation, fmpz_mpoly_deflate, fmpz_mpoly_inflate).

Lemma 4 (GCDs commute with substitution of powers). Let D be \mathbb{Z} or a field, let t_i \ge 1, and let \iota be the ring homomorphism of S = D[x_0, \ldots, x_{n-1}] to itself given by x_i \mapsto x_i^{t_i}. Then \gcd(\iota(A), \iota(B)) = \iota(\gcd(A, B)) up to units.

Proof. Let R be the image of \iota, the ring D[y_0, \ldots, y_{n-1}] with y_i = x_i^{t_i}; \iota is an isomorphism onto it. The ring S is a free R-module with basis the monomials x^j, 0 \le j_i < t_i, hence flat over R, and for a flat extension (aS) \cap (bS) = (aR \cap bR) S. In the unique factorisation domains R and S the intersection of two principal ideals is generated by the least common multiple, so \operatorname{lcm}_S(a, b) and \operatorname{lcm}_R(a, b) agree up to units of S, and so do the greatest common divisors ab / \operatorname{lcm}.

Thus the GCD of A = x^{s} \iota(A') and B = x^{s'} \iota(B') with common strides t is x^{\min(s, s')} \iota(\gcd(A', B')), since \iota(A') and \iota(B') are not divisible by any variable. The GCD code uses this to reduce degrees. The factoring code goes further and applies an invertible integer affine map to the exponents (compression, mpoly_compression_set), described in Compression.

When exponents need more than one word per field, most algorithms first try to repack them into one word (fmpz_mpoly_repack_bits), which succeeds whenever all fields are below 2^{\mathrm{FLINT\_BITS}-1}; the GCD code also deflates. Algorithms that only exist for single-word fields (array multiplication, dense methods, GCD and factoring internals) decline or fail otherwise.

Use in FLINT#

int mpoly_monomial_cmp(const ulong * exp2, const ulong * exp3, slong N, const ulong * cmpmask)
int mpoly_monomial_divides(ulong * exp_ptr, const ulong * exp2, const ulong * exp3, slong N, ulong mask)
void mpoly_get_cmpmask(ulong * cmpmask, slong N, flint_bitcnt_t bits, const mpoly_ctx_t mctx)
void fmpz_mpoly_deflate(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz * shift, const fmpz * stride, const fmpz_mpoly_ctx_t ctx)

Referenced from#

References#

  • [MonPea2007] M. Monagan and R. Pearce. Polynomial division using dynamic arrays, heaps, and packed exponent vectors. Computer Algebra in Scientific Computing (CASC 2007), Lecture Notes in Computer Science 4770, Springer, 2007, 295–315. https://doi.org/10.1007/978-3-540-75187-8_23
  • [BacSch1998] O. Bachmann and H. Schönemann. Monomial representations for Gröbner bases computations. Proceedings of ISSAC 1998, ACM, 309–316. https://doi.org/10.1145/281508.281657
  • [Sch-mpoly] D. Schultz. Algorithms for multivariate polynomials. Notes in the FLINT source tree, src/mpoly/doc/MPolyAlgorithms.tex.
  • [CLO2015] D. Cox, J. Little and D. O'Shea. Ideals, Varieties, and Algorithms, fourth edition. Undergraduate Texts in Mathematics, Springer, 2015. https://doi.org/10.1007/978-3-319-16721-3
Cite this page

The FLINT team, “Monomial orderings and exponent packing,” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/algorithms/multivariate/representation.html (generated 2026-10-20).