diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6d81433..1ab4095a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,9 @@ jobs: - version: '1.6' os: ubuntu-latest arch: x64 + - version: '1.10' + os: ubuntu-latest + arch: x64 - version: '1' os: ubuntu-latest arch: x86 @@ -28,7 +31,7 @@ jobs: arch: x64 steps: - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 + - uses: julia-actions/setup-julia@v2 with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} diff --git a/docs/src/types.md b/docs/src/types.md index 3ff9f61e..a8f40f4d 100644 --- a/docs/src/types.md +++ b/docs/src/types.md @@ -40,6 +40,7 @@ powers constant_monomial map_exponents multiplication_preserves_monomial_order +promote_variables ``` ### Ordering diff --git a/src/comparison.jl b/src/comparison.jl index 06c5526c..d0c713f5 100644 --- a/src/comparison.jl +++ b/src/comparison.jl @@ -249,12 +249,27 @@ struct Graded{O<:AbstractMonomialOrdering} <: AbstractMonomialOrdering same_degree_ordering::O end -_deg(exponents) = sum(exponents) -_deg(mono::AbstractMonomial) = degree(mono) - -function compare(a, b, ::Type{Graded{O}}) where {O} - deg_a = _deg(a) - deg_b = _deg(b) +function compare( + a::_TupleOrVector, + b::_TupleOrVector, + ::Type{Graded{O}}, +) where {O} + deg_a = sum(a) + deg_b = sum(b) + if deg_a == deg_b + return compare(a, b, O) + else + return deg_a - deg_b + end +end +# TODO Backward compat, remove +function compare( + a::AbstractMonomial, + b::AbstractMonomial, + ::Type{Graded{O}}, +) where {O} + deg_a = degree(a) + deg_b = degree(b) if deg_a == deg_b return compare(a, b, O) else @@ -284,7 +299,21 @@ struct Reverse{O<:AbstractMonomialOrdering} <: AbstractMonomialOrdering reverse_ordering::O end -compare(a, b, ::Type{Reverse{O}}) where {O} = compare(b, a, O) +function compare( + a::_TupleOrVector, + b::_TupleOrVector, + ::Type{Reverse{O}}, +) where {O} + return compare(b, a, O) +end +# TODO Backward compat, remove +function compare( + a::AbstractMonomial, + b::AbstractMonomial, + ::Type{Reverse{O}}, +) where {O} + return compare(b, a, O) +end """ ordering(p::AbstractPolynomialLike) @@ -293,6 +322,62 @@ Returns the [`AbstractMonomialOrdering`](@ref) used for the monomials of `p`. """ function ordering end +ordering(::Type{<:AbstractMonomial}) = Graded{LexOrder} +ordering(::Type{P}) where {P} = ordering(monomial_type(P)) +ordering(p::AbstractPolynomialLike) = ordering(typeof(p)) + +# We reverse the order of comparisons here so that the result +# of x < y is equal to the result of Monomial(x) < Monomial(y) +# Without `Base.@pure`, TypedPolynomials allocates on Julia v1.6 +# with `promote(x * y, x)` +Base.@pure function compare( + v1::AbstractVariable, + v2::AbstractVariable, + ::Type{<:AbstractMonomialOrdering}, +) + return -cmp(name(v1), name(v2)) +end + +function compare( + m1::AbstractMonomial, + m2::AbstractMonomial, + ::Type{O}, +) where {O<:AbstractMonomialOrdering} + s1, s2 = promote_variables(m1, m2) + return compare(exponents(s1), exponents(s2), O) +end + +# Implement this to make coefficients be compared with terms. +function _cmp_coefficient(a::Real, b::Real) + return cmp(a, b) +end +function _cmp_coefficient(a::Number, b::Number) + return cmp(abs(a), abs(b)) +end +# By default, coefficients are not comparable so `a` is not strictly +# less than `b`, they are considered sort of equal. +_cmp_coefficient(a, b) = 0 + +function compare( + t1::AbstractTermLike, + t2::AbstractTermLike, + ::Type{O}, +) where {O<:AbstractMonomialOrdering} + Δ = compare(monomial(t1), monomial(t2), O) + if iszero(Δ) + return _cmp_coefficient(coefficient(t1), coefficient(t2)) + end + return Δ +end + +function Base.cmp(t1::AbstractTermLike, t2::AbstractTermLike) + return compare(t1, t2, ordering(t1)) +end +# TODO for backward compat, remove in next breaking release +compare(t1::AbstractTermLike, t2::AbstractTermLike) = cmp(t1, t2) + +Base.isless(t1::AbstractTermLike, t2::AbstractTermLike) = cmp(t1, t2) < 0 + _last_lex_index(n, ::Type{LexOrder}) = n _prev_lex_index(i, ::Type{LexOrder}) = i - 1 _not_first_indices(n, ::Type{LexOrder}) = n:-1:2 diff --git a/src/default_polynomial.jl b/src/default_polynomial.jl index a7b45bf6..9c7d268d 100644 --- a/src/default_polynomial.jl +++ b/src/default_polynomial.jl @@ -93,7 +93,7 @@ Base.one(p::Polynomial) = one(typeof(p)) Base.zero(::Type{Polynomial{C,T,A}}) where {C,T,A} = Polynomial{C,T,A}(A()) Base.zero(t::Polynomial) = zero(typeof(t)) -compare_monomials(a, b) = compare(monomial(a), monomial(b)) +compare_monomials(a, b) = cmp(monomial(a), monomial(b)) function join_terms( terms1::AbstractArray{<:Term}, diff --git a/src/operators.jl b/src/operators.jl index 61b7a082..67af4b71 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -1,33 +1,3 @@ -# We reverse the order of comparisons here so that the result -# of x < y is equal to the result of Monomial(x) < Monomial(y) -Base.@pure function Base.isless(v1::AbstractVariable, v2::AbstractVariable) - return name(v1) > name(v2) -end -function Base.isless(m1::AbstractTermLike, m2::AbstractTermLike) - return isless(promote(m1, m2)...) -end - -# Implement this to make coefficients be compared with terms. -function isless_coefficient(a::Real, b::Real) - return a < b -end -function isless_coefficient(a::Number, b::Number) - return abs(a) < abs(b) -end -# By default, coefficients are not comparable so `a` is not strictly -# less than `b`, they are considered sort of equal. -isless_coefficient(a, b) = false - -function Base.isless(t1::AbstractTerm, t2::AbstractTerm) - if monomial(t1) < monomial(t2) - return true - elseif monomial(t1) == monomial(t2) - return isless_coefficient(coefficient(t1), coefficient(t2)) - else - return false - end -end - # promoting multiplication is not a good idea # For example a polynomial of Float64 * a polynomial of JuMP affine expression # is a polynomial of JuMP affine expression but if we promote it would be a diff --git a/src/promote.jl b/src/promote.jl index 1302fffc..3c666be8 100644 --- a/src/promote.jl +++ b/src/promote.jl @@ -1,3 +1,10 @@ +""" + promote_variables(p::AbstractPolynomialLike, q::AbstractPolynomialLike) + +Return two polynomials over the same variables. +""" +function promote_variables end + # MonomialLike Base.promote_rule(::Type{M}, ::Type{M}) where {M<:AbstractMonomialLike} = M function Base.promote_rule( diff --git a/test/commutative/comparison.jl b/test/commutative/comparison.jl index 4fab76e9..94a5baf9 100644 --- a/test/commutative/comparison.jl +++ b/test/commutative/comparison.jl @@ -146,31 +146,21 @@ grlex = Graded{lex} rinvlex = Reverse{InverseLexOrder} grevlex = Graded{rinvlex} - @test MP.compare([1, 0, 1], [1, 1, 0], grlex) == -1 - @test MP.compare([1, 1, 0], [1, 0, 1], grlex) == 1 Mod.@polyvar x y z # [CLO13, p. 58] - @test MP.compare(1:3, [3, 2, 0], lex) < 0 - @test MP.compare(1:3, [3, 2, 0], grlex) > 0 - @test MP.compare(1:3, [3, 2, 0], rinvlex) < 0 - @test MP.compare(1:3, [3, 2, 0], grevlex) > 0 - @test MP.compare([1, 2, 4], [1, 1, 5], lex) > 0 - @test MP.compare([1, 2, 4], [1, 1, 5], grlex) > 0 - @test MP.compare([1, 2, 4], [1, 1, 5], rinvlex) > 0 - @test MP.compare([1, 2, 4], [1, 1, 5], grevlex) > 0 - @test MP.compare(x * y^2 * z^3, x^3 * y^2, lex) < 0 - @test MP.compare(x * y^2 * z^3, x^3 * y^2, grlex) > 0 - @test MP.compare(x * y^2 * z^3, x^3 * y^2, rinvlex) < 0 - @test MP.compare(x * y^2 * z^3, x^3 * y^2, grevlex) > 0 - @test MP.compare(x * y^2 * z^4, x * y * z^5, lex) > 0 - @test MP.compare(x * y^2 * z^4, x * y * z^5, grlex) > 0 - @test MP.compare(x * y^2 * z^4, x * y * z^5, rinvlex) > 0 - @test MP.compare(x * y^2 * z^4, x * y * z^5, grevlex) > 0 + @test compare(x * y^2 * z^3, x^3 * y^2, lex) < 0 + @test compare(x * y^2 * z^3, x^3 * y^2, grlex) > 0 + @test compare(x * y^2 * z^3, x^3 * y^2, rinvlex) < 0 + @test compare(x * y^2 * z^3, x^3 * y^2, grevlex) > 0 + @test compare(x * y^2 * z^4, x * y * z^5, lex) > 0 + @test compare(x * y^2 * z^4, x * y * z^5, grlex) > 0 + @test compare(x * y^2 * z^4, x * y * z^5, rinvlex) > 0 + @test compare(x * y^2 * z^4, x * y * z^5, grevlex) > 0 # [CLO13, p. 59] - @test MP.compare(x^5 * y * z, x^4 * y * z^2, lex) > 0 - @test MP.compare(x^5 * y * z, x^4 * y * z^2, grlex) > 0 - @test MP.compare(x^5 * y * z, x^4 * y * z^2, rinvlex) > 0 - @test MP.compare(x^5 * y * z, x^4 * y * z^2, grevlex) > 0 + @test compare(x^5 * y * z, x^4 * y * z^2, lex) > 0 + @test compare(x^5 * y * z, x^4 * y * z^2, grlex) > 0 + @test compare(x^5 * y * z, x^4 * y * z^2, rinvlex) > 0 + @test compare(x^5 * y * z, x^4 * y * z^2, grevlex) > 0 # [CLO13] Cox, D., Little, J., & OShea, D. # *Ideals, varieties, and algorithms: an introduction to computational algebraic geometry and commutative algebra*. # Springer Science & Business Media, **2013**.