Skip to content

Commit dd6c877

Browse files
blegatkalmarek
andauthored
Allow basis to change isless (#82)
* Allow basis to change isless * Fixes * Use field * Move to example * Add canonical back * Simplify * Move comment * Fix * Remove type piracy * Add license * Fix * Update test/runtests.jl Co-authored-by: Marek Kaluba <kalmar@mailbox.org> --------- Co-authored-by: Marek Kaluba <kalmar@mailbox.org>
1 parent 3fc7287 commit dd6c877

File tree

6 files changed

+100
-29
lines changed

6 files changed

+100
-29
lines changed

examples/bivariate.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# This file is a part of StarAlgebras.jl. License is MIT: https://github.com/JuliaAlgebra/StarAlgebras.jl/blob/main/LICENSE
2+
# Copyright (c) 2021-2025: Marek Kaluba, Benoît Legat
3+
4+
# Example implementation of Bivariate polynomials
5+
# See MultivariatePolynomials.jl for general implementation
6+
struct ExponentsIterator end
7+
Base.eltype(::Type{ExponentsIterator}) = NTuple{2,Int}
8+
Base.IteratorSize(::Type{ExponentsIterator}) = Base.IsInfinite()
9+
10+
function Base.iterate(::ExponentsIterator)
11+
z = (0, 0)
12+
return z, (z, 0)
13+
end
14+
15+
function Base.iterate(::ExponentsIterator, state)
16+
z, deg = state
17+
if iszero(z[2])
18+
deg += 1
19+
z = (0, deg)
20+
else
21+
z = (z[1] + 1, z[2] - 1)
22+
end
23+
return z, (z, deg)
24+
end
25+
26+
function grlex(a::NTuple{2,Int}, b::NTuple{2,Int})
27+
return isless((sum(a), a), (sum(b), b))
28+
end
29+
30+
struct Monomial
31+
exponents::NTuple{2,Int}
32+
end
33+
34+
Base.one(::Monomial) = Monomial((0, 0))
35+
Base.:*(a::Monomial, b::Monomial) = Monomial(a.exponents .+ b.exponents)
36+
37+
monomial(exp) = Monomial(exp)
38+
exponents(mono::Monomial) = mono.exponents
39+
40+
SA.comparable(::ExponentsIterator) = grlex
41+
42+
function bivariate_algebra()
43+
exps = ExponentsIterator()
44+
basis = SA.MappedBasis(exps, monomial, exponents)
45+
mstr = SA.DiracMStructure(basis, *)
46+
object = Monomial((0, 0))
47+
return SA.StarAlgebra(object, mstr)
48+
end

src/bases.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,15 @@ key_type(b::AbstractBasis) = key_type(typeof(b))
3838
Implicit bases are bases that contains the product of all its elements.
3939
This makes these bases particularly useful to work with [`AlgebraElement`](@ref)s with supports that can not be reasonably bounded.
4040
Note that these bases may not explictly store its elements in memory as they may be potentially infinite.
41+
The elements of the basis are iterated in increasing order according to `comparable(object(b))`.
4142
"""
4243
abstract type ImplicitBasis{T,I} <: AbstractBasis{T,I} end
4344

44-
function zero_coeffs(::Type{S}, ::ImplicitBasis{T,I}) where {S,T,I}
45-
return SparseCoefficients(I[], S[])
45+
comparable(::Type) = isless
46+
comparable(object) = comparable(eltype(object))
47+
48+
function zero_coeffs(::Type{S}, basis::ImplicitBasis{T,I}) where {S,T,I}
49+
return SparseCoefficients(I[], S[], comparable(object(basis)))
4650
end
4751

4852
"""

src/sparse_coeffs.jl

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,33 @@
11
# This file is a part of StarAlgebras.jl. License is MIT: https://github.com/JuliaAlgebra/StarAlgebras.jl/blob/main/LICENSE
22
# Copyright (c) 2021-2025: Marek Kaluba, Benoît Legat
33

4-
struct SparseCoefficients{K,V,Vk,Vv} <: AbstractCoefficients{K,V}
4+
struct SparseCoefficients{K,V,Vk,Vv,L} <: AbstractCoefficients{K,V}
55
basis_elements::Vk
66
values::Vv
7+
isless::L
78
end
89

9-
function SparseCoefficients(elts::Ks, vals::Vs) where {Ks,Vs}
10-
return SparseCoefficients{eltype(elts),eltype(vals),Ks,Vs}(elts, vals)
10+
function SparseCoefficients(elts::Ks, vals::Vs, isless = isless) where {Ks,Vs}
11+
return SparseCoefficients{eltype(elts),eltype(vals),Ks,Vs,typeof(isless)}(elts, vals, isless)
1112
end
1213

1314
Base.keys(sc::SparseCoefficients) = sc.basis_elements
1415
Base.values(sc::SparseCoefficients) = sc.values
1516
function Base.copy(sc::SparseCoefficients)
16-
return SparseCoefficients(copy(keys(sc)), copy(values(sc)))
17+
return SparseCoefficients(copy(keys(sc)), copy(values(sc)), sc.isless)
1718
end
1819

19-
function _search(keys::Tuple, key)
20+
function _search(keys::Tuple, key; lt)
2021
# `searchsortedfirst` is not defined for `Tuple`
2122
return findfirst(isequal(key), keys)
2223
end
2324

24-
function _search(keys, key::K) where {K}
25-
return searchsortedfirst(keys, key; lt = comparable(K))
25+
function _search(keys, key::K; lt) where {K}
26+
return searchsortedfirst(keys, key; lt)
2627
end
2728

2829
function Base.getindex(sc::SparseCoefficients{K}, key::K) where {K}
29-
k = _search(sc.basis_elements, key)
30+
k = _search(sc.basis_elements, key; lt = sc.isless)
3031
if k in eachindex(sc.basis_elements)
3132
v = sc.values[k]
3233
if sc.basis_elements[k] == key
@@ -40,7 +41,7 @@ function Base.getindex(sc::SparseCoefficients{K}, key::K) where {K}
4041
end
4142

4243
function Base.setindex!(sc::SparseCoefficients{K}, val, key::K) where {K}
43-
k = searchsortedfirst(sc.basis_elements, key; lt = comparable(K))
44+
k = searchsortedfirst(sc.basis_elements, key; lt = sc.isless)
4445
if k in eachindex(sc.basis_elements) && sc.basis_elements[k] == key
4546
sc.values[k] = val
4647
else
@@ -100,7 +101,7 @@ _first_sparse_coeffs(c::SparseCoefficients, args...) = c
100101
_first_sparse_coeffs(_, args...) = _first_sparse_coeffs(args...)
101102

102103
function Base.zero(sc::SparseCoefficients)
103-
return SparseCoefficients(empty(keys(sc)), empty(values(sc)))
104+
return SparseCoefficients(empty(keys(sc)), empty(values(sc)), sc.isless)
104105
end
105106

106107
_similar(x::Tuple) = _similar(x, typeof(x[1]))
@@ -111,16 +112,16 @@ _similar(x, ::Type{T}) where {T} = similar(x, T)
111112
_similar_type(::Type{<:Tuple}, ::Type{T}) where {T} = Vector{T}
112113
_similar_type(::Type{V}, ::Type{T}) where {V,T} = similar_type(V, T)
113114

114-
function similar_type(::Type{SparseCoefficients{K,V,Vk,Vv}}, ::Type{T}) where {K,V,Vk,Vv,T}
115-
return SparseCoefficients{K,T,_similar_type(Vk, K),_similar_type(Vv, T)}
115+
function similar_type(::Type{SparseCoefficients{K,V,Vk,Vv,L}}, ::Type{T}) where {K,V,Vk,Vv,T,L}
116+
return SparseCoefficients{K,T,_similar_type(Vk, K),_similar_type(Vv, T),L}
116117
end
117118

118119
function Base.similar(s::SparseCoefficients, ::Type{T} = value_type(s)) where {T}
119-
return SparseCoefficients(collect(s.basis_elements), _similar(s.values, T))
120+
return SparseCoefficients(collect(s.basis_elements), _similar(s.values, T), s.isless)
120121
end
121122

122123
function map_keys(f::Function, s::SparseCoefficients)
123-
return SparseCoefficients(map(f, s.basis_elements), s.values)
124+
return SparseCoefficients(map(f, s.basis_elements), s.values, s.isless)
124125
end
125126

126127
function MA.mutability(
@@ -144,29 +145,24 @@ function __prealloc(X::SparseCoefficients, Y::SparseCoefficients, op)
144145
return similar(X, T)
145146
end
146147

147-
comparable(::Type) = isless
148-
function MA.operate!(::typeof(canonical), res::SparseCoefficients)
149-
return MA.operate!(canonical, res, comparable(key_type(res)))
150-
end
151-
152148
function unsafe_push!(res::SparseCoefficients, key, value)
153149
push!(res.basis_elements, key)
154150
push!(res.values, value)
155151
return res
156152
end
157153

158-
# `::C` is needed to force Julia specialize on the function type
154+
# `{...,L}` is needed to force Julia specialize on the function type
159155
# Otherwise, we get one allocation when we call `issorted`
160156
# See https://docs.julialang.org/en/v1/manual/performance-tips/#Be-aware-of-when-Julia-avoids-specializing
161-
function MA.operate!(::typeof(canonical), res::SparseCoefficients, cmp::C) where {C}
162-
sorted = issorted(res.basis_elements; lt = cmp)
157+
function MA.operate!(::typeof(canonical), res::SparseCoefficients{K,V,Vk,Vv,L}) where {K,V,Vk,Vv,L}
158+
sorted = issorted(res.basis_elements; lt = res.isless)
163159
distinct = allunique(res.basis_elements)
164160
if sorted && distinct && !any(iszero, res.values)
165161
return res
166162
end
167163

168164
if !sorted
169-
p = sortperm(res.basis_elements; lt = cmp)
165+
p = sortperm(res.basis_elements; lt = res.isless)
170166
permute!(res.basis_elements, p)
171167
permute!(res.values, p)
172168
end

src/star.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ star(::AbstractBasis, x) = star(x)
1616
function star(b::AbstractBasis, d::SparseCoefficients)
1717
k = star.(Ref(b), keys(d))
1818
v = star.(values(d))
19-
return SparseCoefficients(k, v)
19+
return SparseCoefficients(k, v, d.isless)
2020
end
2121

2222
function star(b::FixedBasis, i::Integer)

test/graded_lex.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# This file is a part of StarAlgebras.jl. License is MIT: https://github.com/JuliaAlgebra/StarAlgebras.jl/blob/main/LICENSE
2+
# Copyright (c) 2021-2025: Marek Kaluba, Benoît Legat
3+
4+
# Example with Graded Lex Ordering
5+
using Test
6+
import StarAlgebras as SA
7+
8+
@testset "Graded Lex" begin
9+
alg = bivariate_algebra()
10+
o = one(alg)
11+
@test isone(o)
12+
@test SA.coeffs(o).isless == grlex
13+
a = SA.AlgebraElement(
14+
SA.SparseCoefficients(
15+
collect(Iterators.take(SA.object(SA.basis(alg)), 3)),
16+
[2, -1, 3],
17+
grlex,
18+
),
19+
alg,
20+
)
21+
c = a * a
22+
@test c.coeffs.values == [4, -4, 12, 1, -6, 9]
23+
@test c.coeffs.basis_elements == [(0, 0), (0, 1), (1, 0), (0, 2), (1, 1), (2, 0)]
24+
end

test/runtests.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ end
4646

4747
# some applications:
4848
using Groups
49-
function Base.isless(g::Groups.FPGroupElement, h::Groups.FPGroupElement)
50-
return isless(Groups.word(g), Groups.word(h))
51-
end
49+
lexord(a, b) = isless(Groups.word(a), Groups.word(b))
50+
SA.comparable(::Type{<:Groups.FPGroupElement}) = lexord
5251
include("sum_of_squares.jl")
5352
end

0 commit comments

Comments
 (0)