Division

Given two polynomials, $p$ and $d$, there are unique $r$ and $q$ such that $p = q d + r$ and the leading term of $d$ does not divide the leading term of $r$. You can obtain $q$ using the div function and $r$ using the rem function. The divrem function returns $(q, r)$.

Given a polynomial $p$ and divisors $d_1, \ldots, d_n$, one can find $r$ and $q_1, \ldots, q_n$ such that $p = q_1 d_1 + \cdots + q_n d_n + r$ and none of the leading terms of $q_1, \ldots, q_n$ divide the leading term of $r$. You can obtain the vector $[q_1, \ldots, q_n]$ using div(p, d) where $d = [d_1, \ldots, d_n]$ and $r$ using the rem function with the same arguments. The divrem function returns $(q, r)$.

MultivariatePolynomials.dividesFunction
divides(t1::AbstractTermLike, t2::AbstractTermLike)

Returns whether the monomial of t1 divides the monomial of t2.

Examples

Calling divides(2x^2y, 3xy) should return false because x^2y does not divide xy since x has a degree 2 in x^2y which is greater than the degree of x on xy. However, calling divides(3xy, 2x^2y) should return true.

source
MultivariatePolynomials.div_multipleFunction
div_multiple(a, b, ma::MA.MutableTrait)

Return the division of a by b assuming that a is a multiple of b. If a is not a multiple of b then this function may return anything.

source

Note that the coefficients of the polynomials need to be a field for div, rem and divrem to work. If the coefficient type is not a field, it is promoted to a field using promote_to_field.

MultivariatePolynomials.promote_to_fieldFunction
promote_to_field(::Type{T})

Promote the type T to a field. For instance, promote_to_field(T) returns Rational{T} if T is an integer and promote_to_field(T) returns RationalPoly{T} if T is a polynomial.

source

Alternatively, pseudo_rem or pseudo_divrem can be used instead as they do not require the coefficient type to be a field.

MultivariatePolynomials.pseudo_remFunction
pseudo_rem(f::_APL, g::_APL, algo)

Return the pseudo remainder of f modulo g as defined in [Knu14, Algorithm R, p. 425].

See pseudo_divrem for more details.

[Knu14] Knuth, D.E., 2014. Art of computer programming, volume 2: Seminumerical algorithms. Addison-Wesley Professional. Third edition.

source
MultivariatePolynomials.pseudo_divremFunction
pseudo_divrem(f::_APL{S}, g::_APL{T}, algo) where {S,T}

Return the pseudo divisor and remainder of f modulo g as defined in [Knu14, Algorithm R, p. 425].

When the coefficient type is not a field, it is not always possible to carry a division. For instance, the division of f = 3x + 1 by g = 2x + 1 cannot be done over integers. On the other hand, one can write 2f = 3g - 1. In general, the pseudo division of f by g is:

\[l f(x) = q(x) g(x) + r(x)\]

where l is a power of the leading coefficient of g some constant.

See also pseudo_rem.

[Knu14] Knuth, D.E., 2014. Art of computer programming, volume 2: Seminumerical algorithms. Addison-Wesley Professional. Third edition.

source

Greatest Common Divisor (GCD)

The Greatest Common Divisor (GCD) and Least Common Multiple (LCM) can be obtained for integers respectively with the gcd and lcm functions. The same functions can be used with monomials and polynomials:

Base.gcdFunction
gcd(m1::AbstractMonomialLike, m2::AbstractMonomialLike)

Return the largest monomial m such that both divides(m, m1) and divides(m, m2) are true.

julia> @polyvar x y z;

julia> gcd(x^2*y^7*z^3, x^4*y^5*z^2)
x²y⁵z²
source
function gcd(p1::AbstractPolynomialLike{T}, p2::AbstractPolynomialLike{S}) where {T, S}

Returns a greatest common divisor of p1 and p2. Note that it does not make sense, in general, to speak of "the" greatest common divisor of u and v; there is a set of greatest common divisors, each one being a unit multiple of the others [Knu14, p. 424].

Implementation notes

The classical algorithm for computing the gcd, commonly referred to as the Euclidean Algorithm is to use a recursion with the base case gcd(p, 0) = p and the relation gcd(p1, p2) = gcd(p2, rem(p1, p2)). The relation comes from the Euclidean division: p1 = q * p2 + r, if g divides p1 and p2 then it divides r and if g divides r and p2 then it divides p1.

For multivariate polynomials, you may have rem(p1, p2) = p1 hence this will not terminate. To ensure we make progress, we can pick a given variable xi and try to find q1 and q2 such that q2 * p1 = q1 * p2 + r and the degree of r in xi is strictly smaller than the degree of p1 in xi. Note that if g divides p1 and p2 then it divides r but if g divides r and p2 then it might divide q2 and not p1. So what do we do ? Let dj be the degree of pj in xi. Suppose we pick qj to be the coefficient of pj in xi^dj. If g divides q2 then it means that the degree of g in xi is zero. Therefore, if it divides p2 then it also divides the coefficients of p2 in xi^k for k = 0, 1, ..., d2. This means that if we ensure that these are relatively prime then we won't have any issue. So we start by computing a gcd gj of the coefficients in each degree of xi of pj, this is called the content of pj. And then we compute _gcd(p1 / g1, p2 / g2) * gcd(g1, g2) where we can use the recursion _gcd(p1, p2) = _gcd(p2, q2 * p1 - q1 * p2) where q1, q2 are as defined above. This is the GeneralizedEuclideanAlgorithm.

[Knu14] Knuth, D.E., 2014. Art of computer programming, volume 2: Seminumerical algorithms. Addison-Wesley Professional. Third edition.

source
Base.lcmFunction
lcm(m1::AbstractMonomialLike, m2::AbstractMonomialLike)

Return the smallest monomial m such that both divides(m1, m) and divides(m2, m) are true.

julia> @polyvar x y z;

julia> lcm(x^2*y^7*z^3, x^4*y^5*z^2)
x^4*y^7*z^3
source
MultivariatePolynomials.GeneralizedEuclideanAlgorithmType
struct GeneralizedEuclideanAlgorithm <: AbstractUnivariateGCDAlgorithm
    primitive_rem::Bool
    skip_last::Bool
end

Algorithm computing the greatest common divisor of univariate polynomials using the Euclidean algorithm generalized for polynomials with coefficients over a a unique factorization domain, see [Knu14, Algorithm E, p. 426-427].

If primitive_rem is true, the intermediate remainders produced in the polynomial division are made primitive. If primitive_part is set to false, only the resuting remainder is made primitive (the intermediate remainders of the generalized Euclidean algorithm still need to be made primitive).

[Knu14] Knuth, D.E., 2014. Art of computer programming, volume 2: Seminumerical algorithms. Addison-Wesley Professional. Third edition.

source
MultivariatePolynomials.SubresultantAlgorithmType
mutable struct SubresultantAlgorithm <: AbstractUnivariateGCDAlgorithm
    skipped_divisions::Int
end

Algorithm computing the greatest common divisor of univariate polynomials using the Subresultant algorithm, see [Knu14, Algorithm C, p. 428-429].

The division by g*h^δ in the algorithm only works if the iteration of [Knu14, Algorithm R, p. 426] is carried out even when the divided polynomial has a zero term. For computational savings, we don't do that so we store in skipped_division the number of skipped divisions so that the division by g*h^δ can be adapted accordingly.

In [Knu14, Algorithm C, p. 426], it is stated that there should be ``

[Knu14] Knuth, D.E., 2014. Art of computer programming, volume 2: Seminumerical algorithms. Addison-Wesley Professional. Third edition.

source

Internal functions of the gcd algorithm:

MultivariatePolynomials.isolate_variableFunction
isolate_variable(poly::_APL, var::AbstractVariable, mutability::MA.MutableTrait)

Returns a polynomial with variable var. The other variables of poly are moved as coefficients.

The output can be mutated without affecting poly if mutability is MA.IsNotMutable.

source
MultivariatePolynomials.univariate_gcdFunction
univariate_gcd(p1::AbstractPolynomialLike, p2::AbstractPolynomialLike, algo::AbstractUnivariateGCDAlgorithm)

Return the greatest common divisor of the polynomials p1 and p2 that have at most one variable in common and for which the coefficients are either AbstractFloat or part of a unique factorization domain, e.g., rational numbers, integers or multivariate polynomials. So p1 and p2 should have at most one variable in common but their coefficients can be multivariate polynomials that share arbitrarily many variables.

If the coefficients are not AbstractFloat, this

  1. separates p1 and p2 in their content and primitive_part using primitive_part_content; see [Knu14, Algorithm E: E1, p. 426] or [Knu14, Algorithm C: C1, p. 428].
  2. Computes the gcd of the contents and primitive parts, using primitive_univariate_gcd! for primitive parts.
  3. Return the product of these two gcd; see [Knu14, Algorithm E: E4, p. 427] or [Knu14, Algorithm C: C4, p. 429].

[Knu14] Knuth, D.E., 2014. Art of computer programming, volume 2: Seminumerical algorithms. Addison-Wesley Professional. Third edition.

source
MultivariatePolynomials.contentFunction
content(poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm, mutability::MA.MutableTrait) where {T}

Return the content of the polynomial poly over a unique factorization domain S as defined in [Knu14, (3) p. 423]. That is, return the gcd of the coefficients of poly. See also primitive_part_content.

The output can be mutated without affecting poly if mutability is MA.IsNotMutable.

[Knu14] Knuth, D.E., 2014. Art of computer programming, volume 2: Seminumerical algorithms. Addison-Wesley Professional. Third edition.

source
MultivariatePolynomials.primitive_partFunction
primitive_part(poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm) where {T}

Return the primitive part of the polynomial poly over a unique factorization domain S as defined in [Knu14, (3) p. 423]. That is, return the exact division of poly by its content. If the content is also needed, call primitive_part_content instead.

[Knu14] Knuth, D.E., 2014. Art of computer programming, volume 2: Seminumerical algorithms. Addison-Wesley Professional. Third edition.

source
MultivariatePolynomials.primitive_part_contentFunction
primitive_part_content(poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm) where {T}

Return the primitive part and content of the polynomial poly over a unique factorization domain S as defined in [Knu14, (3) p. 423]. This is more efficient to call this function rather than calling primitive_part and content separately since computing the primitive part requires computing the content first and this function avoid computing the content twice.

[Knu14] Knuth, D.E., 2014. Art of computer programming, volume 2: Seminumerical algorithms. Addison-Wesley Professional. Third edition.

source