From b439b5b864defd6a9cfa97c2262eed9d660eee55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 2 May 2025 16:22:53 +0200 Subject: [PATCH 1/7] Simplify comparison --- src/comparison.jl | 54 +++++++++++++++++++++++++++++++++++---- src/default_polynomial.jl | 2 +- src/operators.jl | 30 ---------------------- src/promote.jl | 7 +++++ 4 files changed, 57 insertions(+), 36 deletions(-) diff --git a/src/comparison.jl b/src/comparison.jl index 06c5526c..6c5981ff 100644 --- a/src/comparison.jl +++ b/src/comparison.jl @@ -249,12 +249,19 @@ struct Graded{O<:AbstractMonomialOrdering} <: AbstractMonomialOrdering same_degree_ordering::O end -_deg(exponents) = sum(exponents) -_deg(mono::AbstractMonomial) = degree(mono) +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 -function compare(a, b, ::Type{Graded{O}}) where {O} - deg_a = _deg(a) - deg_b = _deg(b) +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 @@ -293,6 +300,43 @@ Returns the [`AbstractMonomialOrdering`](@ref) used for the monomials of `p`. """ function ordering end +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) +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::AbstractTerm, t2::AbstractTerm, ::Type{O}) where {O<:AbstractMonomialOrdering} + Δ = compare(monomial(t1), monomial(t2), O) + if iszero(Δ) + return _cmp_coefficient(coefficient(t1), coefficient(t2)) + end + return Δ +end + +Base.cmp(t1::AbstractTermLike, t2::AbstractTermLike) = compare(t1, t2, ordering(t1)) + +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( From 8740e2800bb9f953a8b08008fa40a4f5c8afcffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 2 May 2025 18:35:01 +0200 Subject: [PATCH 2/7] Updates --- .github/workflows/ci.yml | 2 +- src/comparison.jl | 10 +++++++--- test/commutative/comparison.jl | 34 ++++++++++++---------------------- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6d81433..8d130758 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,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/src/comparison.jl b/src/comparison.jl index 6c5981ff..de9eebb6 100644 --- a/src/comparison.jl +++ b/src/comparison.jl @@ -258,7 +258,7 @@ function compare(a::_TupleOrVector, b::_TupleOrVector, ::Type{Graded{O}}) where 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) @@ -291,7 +291,9 @@ struct Reverse{O<:AbstractMonomialOrdering} <: AbstractMonomialOrdering reverse_ordering::O end -compare(a, b, ::Type{Reverse{O}}) where {O} = compare(b, a, O) +compare(a::_TupleOrVector, b::_TupleOrVector, ::Type{Reverse{O}}) where {O} = compare(b, a, O) +# TODO Backward compat, remove +compare(a::AbstractMonomial, b::AbstractMonomial, ::Type{Reverse{O}}) where {O} = compare(b, a, O) """ ordering(p::AbstractPolynomialLike) @@ -325,7 +327,7 @@ end # less than `b`, they are considered sort of equal. _cmp_coefficient(a, b) = 0 -function compare(t1::AbstractTerm, t2::AbstractTerm, ::Type{O}) where {O<:AbstractMonomialOrdering} +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)) @@ -334,6 +336,8 @@ function compare(t1::AbstractTerm, t2::AbstractTerm, ::Type{O}) where {O<:Abstra end Base.cmp(t1::AbstractTermLike, t2::AbstractTermLike) = compare(t1, t2, ordering(t1)) +# 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 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**. From f4dca5207174fcbed04c6fd3c5f1d535daef5ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 2 May 2025 18:35:14 +0200 Subject: [PATCH 3/7] Fix format --- src/comparison.jl | 50 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/src/comparison.jl b/src/comparison.jl index de9eebb6..7493771d 100644 --- a/src/comparison.jl +++ b/src/comparison.jl @@ -249,7 +249,11 @@ struct Graded{O<:AbstractMonomialOrdering} <: AbstractMonomialOrdering same_degree_ordering::O end -function compare(a::_TupleOrVector, b::_TupleOrVector, ::Type{Graded{O}}) where {O} +function compare( + a::_TupleOrVector, + b::_TupleOrVector, + ::Type{Graded{O}}, +) where {O} deg_a = sum(a) deg_b = sum(b) if deg_a == deg_b @@ -259,7 +263,11 @@ function compare(a::_TupleOrVector, b::_TupleOrVector, ::Type{Graded{O}}) where end end # TODO Backward compat, remove -function compare(a::AbstractMonomial, b::AbstractMonomial, ::Type{Graded{O}}) where {O} +function compare( + a::AbstractMonomial, + b::AbstractMonomial, + ::Type{Graded{O}}, +) where {O} deg_a = degree(a) deg_b = degree(b) if deg_a == deg_b @@ -291,9 +299,21 @@ struct Reverse{O<:AbstractMonomialOrdering} <: AbstractMonomialOrdering reverse_ordering::O end -compare(a::_TupleOrVector, b::_TupleOrVector, ::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 -compare(a::AbstractMonomial, b::AbstractMonomial, ::Type{Reverse{O}}) where {O} = compare(b, a, O) +function compare( + a::AbstractMonomial, + b::AbstractMonomial, + ::Type{Reverse{O}}, +) where {O} + return compare(b, a, O) +end """ ordering(p::AbstractPolynomialLike) @@ -307,11 +327,19 @@ 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) -function compare(v1::AbstractVariable, v2::AbstractVariable, ::Type{<:AbstractMonomialOrdering}) +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} +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 @@ -327,7 +355,11 @@ end # 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} +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)) @@ -335,7 +367,9 @@ function compare(t1::AbstractTermLike, t2::AbstractTermLike, ::Type{O}) where {O return Δ end -Base.cmp(t1::AbstractTermLike, t2::AbstractTermLike) = compare(t1, t2, ordering(t1)) +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) From 891d47223c830ed99ec64e42a9c576b3390a5918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 3 May 2025 08:29:49 +0200 Subject: [PATCH 4/7] Fix --- src/comparison.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/comparison.jl b/src/comparison.jl index 7493771d..51cd3ad8 100644 --- a/src/comparison.jl +++ b/src/comparison.jl @@ -322,6 +322,7 @@ 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)) From 67e0e85d416ccb87aec3288d8d9f8b0737925154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 3 May 2025 08:47:45 +0200 Subject: [PATCH 5/7] Add promote_variables to doc --- docs/src/types.md | 1 + 1 file changed, 1 insertion(+) 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 From b51307b12d5e0bcf4f91924bde12b560f4c1d2d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 3 May 2025 09:30:21 +0200 Subject: [PATCH 6/7] Add Julia v1.10 to ci --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d130758..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 From f6064fe53daa19f5d6acc0137cc32bed0d2e4fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 3 May 2025 12:07:35 +0200 Subject: [PATCH 7/7] Fix allocation on Julia v1.6 --- src/comparison.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/comparison.jl b/src/comparison.jl index 51cd3ad8..d0c713f5 100644 --- a/src/comparison.jl +++ b/src/comparison.jl @@ -328,7 +328,9 @@ 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) -function compare( +# Without `Base.@pure`, TypedPolynomials allocates on Julia v1.6 +# with `promote(x * y, x)` +Base.@pure function compare( v1::AbstractVariable, v2::AbstractVariable, ::Type{<:AbstractMonomialOrdering},