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.divides — Functiondivides(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.
MultivariatePolynomials.div_multiple — Functiondiv_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.
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_field — Functionpromote_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.
Alternatively, pseudo_rem or pseudo_divrem can be used instead as they do not require the coefficient type to be a field.
MultivariatePolynomials.pseudo_rem — Functionpseudo_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.
MultivariatePolynomials.pseudo_divrem — Functionpseudo_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.
MultivariatePolynomials.rem_or_pseudo_rem — Functionrem_or_pseudo_rem(f::_APL, g::_APL, algo)If the coefficient type is a field, return rem, otherwise, return pseudo_rem.
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.gcd — Functiongcd(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²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.
Base.lcm — Functionlcm(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^3MultivariatePolynomials.AbstractUnivariateGCDAlgorithm — Typeabstract type AbstractUnivariateGCDAlgorithm endAlgorithm computing the greatest common divisor of univariate polynomials. See GeneralizedEuclideanAlgorithm and SubresultantAlgorithm.
MultivariatePolynomials.GeneralizedEuclideanAlgorithm — Typestruct GeneralizedEuclideanAlgorithm <: AbstractUnivariateGCDAlgorithm
primitive_rem::Bool
skip_last::Bool
endAlgorithm 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.
MultivariatePolynomials.SubresultantAlgorithm — Typemutable struct SubresultantAlgorithm <: AbstractUnivariateGCDAlgorithm
skipped_divisions::Int
endAlgorithm 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.
Internal functions of the gcd algorithm:
MultivariatePolynomials.isolate_variable — Functionisolate_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.
MultivariatePolynomials.primitive_univariate_gcd! — Functionprimitive_univariate_gcd!(p::_APL, q::_APL, algo::AbstractUnivariateGCDAlgorithm)Returns the gcd of primitive polynomials p and q using algorithm algo which is a subtype of AbstractUnivariateGCDAlgorithm. The function might modify p or q.
MultivariatePolynomials.univariate_gcd — Functionunivariate_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
- separates
p1andp2in theircontentandprimitive_partusingprimitive_part_content; see [Knu14, Algorithm E: E1, p. 426] or [Knu14, Algorithm C: C1, p. 428]. - Computes the
gcdof the contents and primitive parts, usingprimitive_univariate_gcd!for primitive parts. - 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.
MultivariatePolynomials.content — Functioncontent(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.
MultivariatePolynomials.primitive_part — Functionprimitive_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.
MultivariatePolynomials.primitive_part_content — Functionprimitive_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.