Skip to content

Commit b68fbcb

Browse files
authored
Simplify comparison (#315)
* Simplify comparison * Updates * Fix format * Fix * Add promote_variables to doc * Add Julia v1.10 to ci * Fix allocation on Julia v1.6
1 parent 3a244a9 commit b68fbcb

File tree

7 files changed

+117
-61
lines changed

7 files changed

+117
-61
lines changed

.github/workflows/ci.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ jobs:
2020
- version: '1.6'
2121
os: ubuntu-latest
2222
arch: x64
23+
- version: '1.10'
24+
os: ubuntu-latest
25+
arch: x64
2326
- version: '1'
2427
os: ubuntu-latest
2528
arch: x86
@@ -28,7 +31,7 @@ jobs:
2831
arch: x64
2932
steps:
3033
- uses: actions/checkout@v4
31-
- uses: julia-actions/setup-julia@v1
34+
- uses: julia-actions/setup-julia@v2
3235
with:
3336
version: ${{ matrix.version }}
3437
arch: ${{ matrix.arch }}

docs/src/types.md

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ powers
4040
constant_monomial
4141
map_exponents
4242
multiplication_preserves_monomial_order
43+
promote_variables
4344
```
4445

4546
### Ordering

src/comparison.jl

+92-7
Original file line numberDiff line numberDiff line change
@@ -249,12 +249,27 @@ struct Graded{O<:AbstractMonomialOrdering} <: AbstractMonomialOrdering
249249
same_degree_ordering::O
250250
end
251251

252-
_deg(exponents) = sum(exponents)
253-
_deg(mono::AbstractMonomial) = degree(mono)
254-
255-
function compare(a, b, ::Type{Graded{O}}) where {O}
256-
deg_a = _deg(a)
257-
deg_b = _deg(b)
252+
function compare(
253+
a::_TupleOrVector,
254+
b::_TupleOrVector,
255+
::Type{Graded{O}},
256+
) where {O}
257+
deg_a = sum(a)
258+
deg_b = sum(b)
259+
if deg_a == deg_b
260+
return compare(a, b, O)
261+
else
262+
return deg_a - deg_b
263+
end
264+
end
265+
# TODO Backward compat, remove
266+
function compare(
267+
a::AbstractMonomial,
268+
b::AbstractMonomial,
269+
::Type{Graded{O}},
270+
) where {O}
271+
deg_a = degree(a)
272+
deg_b = degree(b)
258273
if deg_a == deg_b
259274
return compare(a, b, O)
260275
else
@@ -284,7 +299,21 @@ struct Reverse{O<:AbstractMonomialOrdering} <: AbstractMonomialOrdering
284299
reverse_ordering::O
285300
end
286301

287-
compare(a, b, ::Type{Reverse{O}}) where {O} = compare(b, a, O)
302+
function compare(
303+
a::_TupleOrVector,
304+
b::_TupleOrVector,
305+
::Type{Reverse{O}},
306+
) where {O}
307+
return compare(b, a, O)
308+
end
309+
# TODO Backward compat, remove
310+
function compare(
311+
a::AbstractMonomial,
312+
b::AbstractMonomial,
313+
::Type{Reverse{O}},
314+
) where {O}
315+
return compare(b, a, O)
316+
end
288317

289318
"""
290319
ordering(p::AbstractPolynomialLike)
@@ -293,6 +322,62 @@ Returns the [`AbstractMonomialOrdering`](@ref) used for the monomials of `p`.
293322
"""
294323
function ordering end
295324

325+
ordering(::Type{<:AbstractMonomial}) = Graded{LexOrder}
326+
ordering(::Type{P}) where {P} = ordering(monomial_type(P))
327+
ordering(p::AbstractPolynomialLike) = ordering(typeof(p))
328+
329+
# We reverse the order of comparisons here so that the result
330+
# of x < y is equal to the result of Monomial(x) < Monomial(y)
331+
# Without `Base.@pure`, TypedPolynomials allocates on Julia v1.6
332+
# with `promote(x * y, x)`
333+
Base.@pure function compare(
334+
v1::AbstractVariable,
335+
v2::AbstractVariable,
336+
::Type{<:AbstractMonomialOrdering},
337+
)
338+
return -cmp(name(v1), name(v2))
339+
end
340+
341+
function compare(
342+
m1::AbstractMonomial,
343+
m2::AbstractMonomial,
344+
::Type{O},
345+
) where {O<:AbstractMonomialOrdering}
346+
s1, s2 = promote_variables(m1, m2)
347+
return compare(exponents(s1), exponents(s2), O)
348+
end
349+
350+
# Implement this to make coefficients be compared with terms.
351+
function _cmp_coefficient(a::Real, b::Real)
352+
return cmp(a, b)
353+
end
354+
function _cmp_coefficient(a::Number, b::Number)
355+
return cmp(abs(a), abs(b))
356+
end
357+
# By default, coefficients are not comparable so `a` is not strictly
358+
# less than `b`, they are considered sort of equal.
359+
_cmp_coefficient(a, b) = 0
360+
361+
function compare(
362+
t1::AbstractTermLike,
363+
t2::AbstractTermLike,
364+
::Type{O},
365+
) where {O<:AbstractMonomialOrdering}
366+
Δ = compare(monomial(t1), monomial(t2), O)
367+
if iszero(Δ)
368+
return _cmp_coefficient(coefficient(t1), coefficient(t2))
369+
end
370+
return Δ
371+
end
372+
373+
function Base.cmp(t1::AbstractTermLike, t2::AbstractTermLike)
374+
return compare(t1, t2, ordering(t1))
375+
end
376+
# TODO for backward compat, remove in next breaking release
377+
compare(t1::AbstractTermLike, t2::AbstractTermLike) = cmp(t1, t2)
378+
379+
Base.isless(t1::AbstractTermLike, t2::AbstractTermLike) = cmp(t1, t2) < 0
380+
296381
_last_lex_index(n, ::Type{LexOrder}) = n
297382
_prev_lex_index(i, ::Type{LexOrder}) = i - 1
298383
_not_first_indices(n, ::Type{LexOrder}) = n:-1:2

src/default_polynomial.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Base.one(p::Polynomial) = one(typeof(p))
9393
Base.zero(::Type{Polynomial{C,T,A}}) where {C,T,A} = Polynomial{C,T,A}(A())
9494
Base.zero(t::Polynomial) = zero(typeof(t))
9595

96-
compare_monomials(a, b) = compare(monomial(a), monomial(b))
96+
compare_monomials(a, b) = cmp(monomial(a), monomial(b))
9797

9898
function join_terms(
9999
terms1::AbstractArray{<:Term},

src/operators.jl

-30
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,3 @@
1-
# We reverse the order of comparisons here so that the result
2-
# of x < y is equal to the result of Monomial(x) < Monomial(y)
3-
Base.@pure function Base.isless(v1::AbstractVariable, v2::AbstractVariable)
4-
return name(v1) > name(v2)
5-
end
6-
function Base.isless(m1::AbstractTermLike, m2::AbstractTermLike)
7-
return isless(promote(m1, m2)...)
8-
end
9-
10-
# Implement this to make coefficients be compared with terms.
11-
function isless_coefficient(a::Real, b::Real)
12-
return a < b
13-
end
14-
function isless_coefficient(a::Number, b::Number)
15-
return abs(a) < abs(b)
16-
end
17-
# By default, coefficients are not comparable so `a` is not strictly
18-
# less than `b`, they are considered sort of equal.
19-
isless_coefficient(a, b) = false
20-
21-
function Base.isless(t1::AbstractTerm, t2::AbstractTerm)
22-
if monomial(t1) < monomial(t2)
23-
return true
24-
elseif monomial(t1) == monomial(t2)
25-
return isless_coefficient(coefficient(t1), coefficient(t2))
26-
else
27-
return false
28-
end
29-
end
30-
311
# promoting multiplication is not a good idea
322
# For example a polynomial of Float64 * a polynomial of JuMP affine expression
333
# is a polynomial of JuMP affine expression but if we promote it would be a

src/promote.jl

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
"""
2+
promote_variables(p::AbstractPolynomialLike, q::AbstractPolynomialLike)
3+
4+
Return two polynomials over the same variables.
5+
"""
6+
function promote_variables end
7+
18
# MonomialLike
29
Base.promote_rule(::Type{M}, ::Type{M}) where {M<:AbstractMonomialLike} = M
310
function Base.promote_rule(

test/commutative/comparison.jl

+12-22
Original file line numberDiff line numberDiff line change
@@ -146,31 +146,21 @@
146146
grlex = Graded{lex}
147147
rinvlex = Reverse{InverseLexOrder}
148148
grevlex = Graded{rinvlex}
149-
@test MP.compare([1, 0, 1], [1, 1, 0], grlex) == -1
150-
@test MP.compare([1, 1, 0], [1, 0, 1], grlex) == 1
151149
Mod.@polyvar x y z
152150
# [CLO13, p. 58]
153-
@test MP.compare(1:3, [3, 2, 0], lex) < 0
154-
@test MP.compare(1:3, [3, 2, 0], grlex) > 0
155-
@test MP.compare(1:3, [3, 2, 0], rinvlex) < 0
156-
@test MP.compare(1:3, [3, 2, 0], grevlex) > 0
157-
@test MP.compare([1, 2, 4], [1, 1, 5], lex) > 0
158-
@test MP.compare([1, 2, 4], [1, 1, 5], grlex) > 0
159-
@test MP.compare([1, 2, 4], [1, 1, 5], rinvlex) > 0
160-
@test MP.compare([1, 2, 4], [1, 1, 5], grevlex) > 0
161-
@test MP.compare(x * y^2 * z^3, x^3 * y^2, lex) < 0
162-
@test MP.compare(x * y^2 * z^3, x^3 * y^2, grlex) > 0
163-
@test MP.compare(x * y^2 * z^3, x^3 * y^2, rinvlex) < 0
164-
@test MP.compare(x * y^2 * z^3, x^3 * y^2, grevlex) > 0
165-
@test MP.compare(x * y^2 * z^4, x * y * z^5, lex) > 0
166-
@test MP.compare(x * y^2 * z^4, x * y * z^5, grlex) > 0
167-
@test MP.compare(x * y^2 * z^4, x * y * z^5, rinvlex) > 0
168-
@test MP.compare(x * y^2 * z^4, x * y * z^5, grevlex) > 0
151+
@test compare(x * y^2 * z^3, x^3 * y^2, lex) < 0
152+
@test compare(x * y^2 * z^3, x^3 * y^2, grlex) > 0
153+
@test compare(x * y^2 * z^3, x^3 * y^2, rinvlex) < 0
154+
@test compare(x * y^2 * z^3, x^3 * y^2, grevlex) > 0
155+
@test compare(x * y^2 * z^4, x * y * z^5, lex) > 0
156+
@test compare(x * y^2 * z^4, x * y * z^5, grlex) > 0
157+
@test compare(x * y^2 * z^4, x * y * z^5, rinvlex) > 0
158+
@test compare(x * y^2 * z^4, x * y * z^5, grevlex) > 0
169159
# [CLO13, p. 59]
170-
@test MP.compare(x^5 * y * z, x^4 * y * z^2, lex) > 0
171-
@test MP.compare(x^5 * y * z, x^4 * y * z^2, grlex) > 0
172-
@test MP.compare(x^5 * y * z, x^4 * y * z^2, rinvlex) > 0
173-
@test MP.compare(x^5 * y * z, x^4 * y * z^2, grevlex) > 0
160+
@test compare(x^5 * y * z, x^4 * y * z^2, lex) > 0
161+
@test compare(x^5 * y * z, x^4 * y * z^2, grlex) > 0
162+
@test compare(x^5 * y * z, x^4 * y * z^2, rinvlex) > 0
163+
@test compare(x^5 * y * z, x^4 * y * z^2, grevlex) > 0
174164
# [CLO13] Cox, D., Little, J., & OShea, D.
175165
# *Ideals, varieties, and algorithms: an introduction to computational algebraic geometry and commutative algebra*.
176166
# Springer Science & Business Media, **2013**.

0 commit comments

Comments
 (0)