FLINT3.7.0-dev
Algorithm

Monomial orderings and exponent packing

Cost
Comparison, multiplication and divisibility test of packed monomials cost O(N)O(N) word operations for NN words per monomial; with kk fields of width b≤64b \le 64 on a 64-bit machine, N=⌈k/⌊64/b⌋⌉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 cixeic_i x^{e_i} with nonzero coefficients, sorted so that xe0>xe1>⋯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 xe=x0e0⋯xn−1en−1x^e = x_0^{e_0} \cdots x_{n-1}^{e_{n-1}}, e∈Nne \in \mathbb{N}^n, such that 1≤xe1 \le x^e and xa<xb⇒xa+c<xb+cx^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∣=e0+⋯+en−1|e| = e_0 + \cdots + e_{n-1}:

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

The variable x0x_0 is the most significant one. The leading term lt⁡(A)\operatorname{lt}(A), monomial lm⁡(A)\operatorname{lm}(A) and coefficient lc⁡(A)\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 x0x_0 is a polynomial in the other variables, as the recursive view R[x1,…][x0]R[x_1, \ldots][x_0] requires.

Packed monomials#

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

  • for ORD_LEX and ORD_DEGLEX, field n−1−in - 1 - i holds eie_i, so that e0e_0 is in the most significant position among the variables;
  • for ORD_DEGREVLEX, field ii holds eie_i;
  • for the degree orderings, field nn holds the total degree ∣e∣|e|.

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

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

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

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

In both cases the fields must satisfy fj<2b−1f_j < 2^{b-1}: the top bit of each field is a guard bit, which is zero in every stored monomial. When bb does not divide FLINT_BITS the words are not literally the digits of Φ(e)\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 bb with the same number of words: three variables in lex order with 64-bit words give b=21b = 21, two give b=32b = 32. A polynomial starts with b=8b = 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,…,n−10, \ldots, n-1 set and those of field nn clear (mpoly_get_cmpmask).

Lemma 1 (Comparison of packed monomials). For each of the three orderings, xa>xbx^a > x^b if and only if Φ(a)⊕cmpmask>Φ(b)⊕cmpmask\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 e0,e1,…e_0, e_1, \ldots, which is the definition. For ORD_DEGLEX the total degree comes first, then e0,e1,…e_0, e_1, \ldots. For ORD_DEGREVLEX the top field is the total degree, and the remaining fields from the top are en−1,…,e0e_{n-1}, \ldots, e_0, each complemented by the mask. Complementing a bb-bit field reverses the order of its values, so for equal degrees Φ(a)⊕cmpmask>Φ(b)⊕cmpmask\Phi(a) \oplus \mathit{cmpmask} > \Phi(b) \oplus \mathit{cmpmask} if and only if, at the last index ii with ai≠bia_i \ne b_i, ai<bia_i < b_i. That is the definition of reverse lexicographic order.

So comparing monomials costs at most NN word comparisons (mpoly_monomial_cmp), and a single one in the frequent case N=1N = 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, Φ(a+c)=Φ(a)+Φ(c)\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,ca, c have fields <2b−1< 2^{b-1} and let MM be the pattern with the guard bit of each field set (mpoly_overflow_mask_sp). In single precision the word sum Φ(a)+Φ(c)\Phi(a) + \Phi(c) involves no carry between fields, and it is a valid packed monomial (all fields of a+ca + c are <2b−1< 2^{b-1}) if and only if (Φ(a)+Φ(c))&M=0(\Phi(a) + \Phi(c)) \mathbin{\&} M = 0.

Proof. Each field of the sum is <2b−1+2b−1=2b< 2^{b-1} + 2^{b-1} = 2^b, so it fits in its bb bits and nothing is carried into the next field. The field of a+ca + c is then the field of the sum, and it is <2b−1< 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 BB by CC they compute the maximum of each field over BB and over CC (mpoly_max_fields_fmpz), add them and choose bb 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,ca, c and MM as above, xcx^c divides xax^a if and only if the word difference Φ(a)−Φ(c)\Phi(a) - \Phi(c) satisfies (Φ(a)−Φ(c))&M=0(\Phi(a) - \Phi(c)) \mathbin{\&} M = 0, and then Φ(a)−Φ(c)=Φ(a−c)\Phi(a) - \Phi(c) = \Phi(a - c).

Proof. If aj≥cja_j \ge c_j for all fields, the subtraction proceeds field by field without borrows and gives Φ(a−c)\Phi(a - c), whose guard bits are zero. Otherwise let jj be the lowest field with aj<cja_j < c_j. All lower fields are subtracted without borrow, and field jj becomes aj−cj+2b>2b−2b−1=2b−1a_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): xax^a is a square if and only if the lowest bit of each word is zero and (Φ(a)≫1)&M=0(\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+Φ(a)−Φ(c)s = M + \Phi(a) - \Phi(c) each field equals 2b−1+aj−cj∈[1,2b)2^{b-1} + a_j - c_j \in [1, 2^b), so there are no borrows and the guard bit of field jj is set exactly when aj≥cja_j \ge c_j; spreading it over the field gives a mask selecting aja_j or cjc_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 sis_i for the minimal exponent of xix_i in AA and tit_i for the GCD of the differences ei−sie_i - s_i over the terms. Then A=xs ιt(A′)A = x^s \, \iota_t(A') where ιt\iota_t is the substitution xi↦xitix_i \mapsto x_i^{t_i} and A′A' is the deflated polynomial, with exponents (ei−si)/ti(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 DD be Z\mathbb{Z} or a field, let ti≥1t_i \ge 1, and let ι\iota be the ring homomorphism of S=D[x0,…,xn−1]S = D[x_0, \ldots, x_{n-1}] to itself given by xi↦xitix_i \mapsto x_i^{t_i}. Then gcd⁡(ι(A),ι(B))=ι(gcd⁡(A,B))\gcd(\iota(A), \iota(B)) = \iota(\gcd(A, B)) up to units.

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

Thus the GCD of A=xsι(A′)A = x^{s} \iota(A') and B=xs′ι(B′)B = x^{s'} \iota(B') with common strides tt is xmin⁡(s,s′)ι(gcd⁡(A′,B′))x^{\min(s, s')} \iota(\gcd(A', B')), since ι(A′)\iota(A') and ι(B′)\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 2FLINT_BITS−12^{\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-11-02).