Merge pull request #29 from kalmarek/fix/#28_normalform_hashing

Fix for #28: normalform & hashing
This commit is contained in:
Marek Kaluba 2023-06-12 17:16:55 +02:00 committed by GitHub
commit d385992e92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 50 additions and 17 deletions

View File

@ -1,7 +1,7 @@
name = "Groups" name = "Groups"
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557" uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
authors = ["Marek Kaluba <kalmar@amu.edu.pl>"] authors = ["Marek Kaluba <kalmar@amu.edu.pl>"]
version = "0.7.6" version = "0.7.7"
[deps] [deps]
GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120"

View File

@ -14,12 +14,7 @@ end
λ(i, j) = Transvection(, i, j) λ(i, j) = Transvection(, i, j)
function Base.show(io::IO, t::Transvection) function Base.show(io::IO, t::Transvection)
id = if t.id === :ϱ print(io, t.id, subscriptify(t.i), '.', subscriptify(t.j))
'ϱ'
else # if t.id === :λ
'λ'
end
print(io, id, subscriptify(t.i), '.', subscriptify(t.j))
return t.inv && print(io, "^-1") return t.inv && print(io, "^-1")
end end

View File

@ -37,6 +37,7 @@ function _update_savedhash!(g::AbstractFPGroupElement, data)
end end
function Base.hash(g::AbstractFPGroupElement, h::UInt) function Base.hash(g::AbstractFPGroupElement, h::UInt)
g = normalform!(g)
_isvalidhash(g) || _update_savedhash!(g, equality_data(g)) _isvalidhash(g) || _update_savedhash!(g, equality_data(g))
return hash(g.savedhash >> count_ones(__BITFLAGS_MASK), h) return hash(g.savedhash >> count_ones(__BITFLAGS_MASK), h)
end end

View File

@ -23,7 +23,7 @@ end
function Base.show(io::IO, s::ElementarySymplectic) function Base.show(io::IO, s::ElementarySymplectic)
i, j = Groups.subscriptify(s.i), Groups.subscriptify(s.j) i, j = Groups.subscriptify(s.i), Groups.subscriptify(s.j)
print(io, s.symbol, i, j) print(io, s.symbol, i, '.', j)
return !isone(s.val) && print(io, "^$(s.val)") return !isone(s.val) && print(io, "^$(s.val)")
end end

View File

@ -88,6 +88,9 @@ end
abstract type AbstractFPGroupElement{Gr} <: GroupElement end abstract type AbstractFPGroupElement{Gr} <: GroupElement end
Base.copy(g::AbstractFPGroupElement) = one(g) * g
word(f::AbstractFPGroupElement) = f.word
mutable struct FPGroupElement{Gr<:AbstractFPGroup,W<:AbstractWord} <: mutable struct FPGroupElement{Gr<:AbstractFPGroup,W<:AbstractWord} <:
AbstractFPGroupElement{Gr} AbstractFPGroupElement{Gr}
word::W word::W
@ -111,7 +114,9 @@ function Base.show(io::IO, ::Type{<:FPGroupElement{Gr}}) where {Gr}
return print(io, FPGroupElement, "{$Gr, …}") return print(io, FPGroupElement, "{$Gr, …}")
end end
word(f::AbstractFPGroupElement) = f.word function Base.copy(f::FPGroupElement)
return FPGroupElement(copy(word(f)), parent(f), f.savedhash)
end
#convenience #convenience
KnuthBendix.alphabet(g::AbstractFPGroupElement) = alphabet(parent(g)) KnuthBendix.alphabet(g::AbstractFPGroupElement) = alphabet(parent(g))
@ -134,7 +139,13 @@ function Base.:(==)(g::AbstractFPGroupElement, h::AbstractFPGroupElement)
end end
function Base.deepcopy_internal(g::FPGroupElement, stackdict::IdDict) function Base.deepcopy_internal(g::FPGroupElement, stackdict::IdDict)
return FPGroupElement(copy(word(g)), parent(g), g.savedhash) haskey(stackdict, objectid(g)) && return stackdict[objectid(g)]
cw = if haskey(stackdict, objectid(word(g)))
stackdict[objectid(word(g))]
else
copy(word(g))
end
return FPGroupElement(cw, parent(g), g.savedhash)
end end
function Base.inv(g::GEl) where {GEl<:AbstractFPGroupElement} function Base.inv(g::GEl) where {GEl<:AbstractFPGroupElement}

View File

@ -26,8 +26,7 @@ function wlmetric_ball(
new = collect( new = collect(
op(o, s) for o in @view(ball[sizes[end-1]:end]) for s in S op(o, s) for o in @view(ball[sizes[end-1]:end]) for s in S
) )
append!(ball, new) ball = union!(ball, new)
unique!(ball)
push!(sizes, length(ball)) push!(sizes, length(ball))
end end
end end

View File

@ -14,7 +14,8 @@
G = FPGroup(F, [a * b => b * a, a * c => c * a, b * c => c * b]) G = FPGroup(F, [a * b => b * a, a * c => c * a, b * c => c * b])
@test G isa FPGroup @test G isa FPGroup
@test sprint(show, G) == "⟨ a b c | \n\t a*b => b*a a*c => c*a b*c => c*b ⟩" @test sprint(show, G) ==
"⟨ a b c | \n\t a*b => b*a a*c => c*a b*c => c*b ⟩"
@test rand(G) isa FPGroupElement @test rand(G) isa FPGroupElement
f = a * c * b f = a * c * b
@ -40,7 +41,7 @@
end end
# quotient of G # quotient of G
H = FPGroup(G, [aG^2 => cG, bG * cG => aG], max_rules=200) H = FPGroup(G, [aG^2 => cG, bG * cG => aG]; max_rules = 200)
h = H(word(g)) h = H(word(g))
@ -48,15 +49,21 @@
@test_throws AssertionError h == g @test_throws AssertionError h == g
@test_throws MethodError h * g @test_throws MethodError h * g
H = FPGroup(G, [aG^2 => cG, bG * cG => aG], max_rules=200) H = FPGroup(G, [aG^2 => cG, bG * cG => aG]; max_rules = 200)
@test_throws AssertionError one(H) == one(H) @test_throws AssertionError one(H) == one(H)
Groups.normalform!(h) Groups.normalform!(h)
@test h == H([5]) @test h == H([5])
@test_logs (:warn, "using generic isfiniteorder(::AbstractFPGroupElement): the returned `false` might be wrong") isfiniteorder(h) @test_logs (
:warn,
"using generic isfiniteorder(::AbstractFPGroupElement): the returned `false` might be wrong",
) isfiniteorder(h)
@test_logs (:warn, "using generic isfinite(::AbstractFPGroup): the returned `false` might be wrong") isfinite(H) @test_logs (
:warn,
"using generic isfinite(::AbstractFPGroup): the returned `false` might be wrong",
) isfinite(H)
Logging.with_logger(Logging.NullLogger()) do Logging.with_logger(Logging.NullLogger()) do
@testset "GroupsCore conformance: H" begin @testset "GroupsCore conformance: H" begin
@ -64,4 +71,24 @@
test_GroupElement_interface(rand(H, 2)...) test_GroupElement_interface(rand(H, 2)...)
end end
end end
@testset "hash/normalform #28" begin
function cyclic_group(n::Integer)
A = Alphabet([:a, :A], [2, 1])
F = FreeGroup(A)
a, = Groups.gens(F)
e = one(F)
Cₙ = FPGroup(F, [a^n => e])
return Cₙ
end
n = 15
G = cyclic_group(n)
ball, sizes = Groups.wlmetric_ball(gens(G); radius = n)
@test first(sizes) == 2
@test last(sizes) == n
@test Set(ball) == Set([first(gens(G))^i for i in 0:n-1])
end
end end