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

View File

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

View File

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

View File

@ -23,7 +23,7 @@ end
function Base.show(io::IO, s::ElementarySymplectic)
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)")
end

View File

@ -88,6 +88,9 @@ 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} <:
AbstractFPGroupElement{Gr}
word::W
@ -111,7 +114,9 @@ function Base.show(io::IO, ::Type{<:FPGroupElement{Gr}}) where {Gr}
return print(io, FPGroupElement, "{$Gr, …}")
end
word(f::AbstractFPGroupElement) = f.word
function Base.copy(f::FPGroupElement)
return FPGroupElement(copy(word(f)), parent(f), f.savedhash)
end
#convenience
KnuthBendix.alphabet(g::AbstractFPGroupElement) = alphabet(parent(g))
@ -134,7 +139,13 @@ function Base.:(==)(g::AbstractFPGroupElement, h::AbstractFPGroupElement)
end
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
function Base.inv(g::GEl) where {GEl<:AbstractFPGroupElement}

View File

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

View File

@ -14,7 +14,8 @@
G = FPGroup(F, [a * b => b * a, a * c => c * a, b * c => c * b])
@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
f = a * c * b
@ -40,7 +41,7 @@
end
# 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))
@ -48,15 +49,21 @@
@test_throws AssertionError 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)
Groups.normalform!(h)
@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
@testset "GroupsCore conformance: H" begin
@ -64,4 +71,24 @@
test_GroupElement_interface(rand(H, 2)...)
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