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^3
MultivariatePolynomials.AbstractUnivariateGCDAlgorithm
— Typeabstract type AbstractUnivariateGCDAlgorithm end
Algorithm computing the greatest common divisor of univariate polynomials. See GeneralizedEuclideanAlgorithm
and SubresultantAlgorithm
.
MultivariatePolynomials.GeneralizedEuclideanAlgorithm
— Typestruct 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.
MultivariatePolynomials.SubresultantAlgorithm
— Typemutable 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.
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
p1
andp2
in theircontent
andprimitive_part
usingprimitive_part_content
; see [Knu14, Algorithm E: E1, p. 426] or [Knu14, Algorithm C: C1, p. 428]. - Computes the
gcd
of 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.