From e7740b9716a771ac0719cb8d69a97dea1761baa4 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Fri, 28 May 2021 18:54:21 +0200 Subject: [PATCH 01/37] WIP: mcgs --- Project.toml | 4 +- src/Groups.jl | 1 + src/groups/mcg.jl | 67 ++++++++++++++++ src/groups/symplectic_twists.jl | 131 ++++++++++++++++++++++++++++++++ 4 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 src/groups/mcg.jl create mode 100644 src/groups/symplectic_twists.jl diff --git a/Project.toml b/Project.toml index b4012e5..30d990a 100644 --- a/Project.toml +++ b/Project.toml @@ -14,15 +14,15 @@ ThreadsX = "ac1d9e8a-700a-412c-b207-f0111f4b6c0d" [compat] AbstractAlgebra = "^0.13.0, ^0.14.0, ^0.15.0" -KnuthBendix = "^0.2.0" GroupsCore = "^0.3" +KnuthBendix = "^0.2.0" OrderedCollections = "1" ThreadsX = "^0.1.0" julia = "1.3, 1.4, 1.5" [extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test", "BenchmarkTools"] diff --git a/src/Groups.jl b/src/Groups.jl index 596e127..f5ab027 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -31,6 +31,7 @@ include("normalform.jl") include("new_autgroups.jl") include("groups/sautFn.jl") +include("groups/mcg.jl") end # module New diff --git a/src/groups/mcg.jl b/src/groups/mcg.jl new file mode 100644 index 0000000..f03aa1c --- /dev/null +++ b/src/groups/mcg.jl @@ -0,0 +1,67 @@ +include("symplectic_twists.jl") + +struct SurfaceGroup{T, S, R} <: AbstractFPGroup + genus::Int + boundaries::Int + gens::Vector{T} + relations::Vector{<:Pair{S,S}} + rws::R +end + +function SurfaceGroup(genus::Integer, boundaries::Integer) + @assert genus > 1 + + ltrs = String[] + for i in 1:genus + subscript = join('₀'+d for d in reverse(digits(i))) + append!(ltrs, ["a" * subscript, "A" * subscript, "b" * subscript, "B" * subscript]) + end + Al = Alphabet(reverse!(ltrs)) + + for i in 1:genus + subscript = join('₀'+d for d in reverse(digits(i))) + KnuthBendix.set_inversion!(Al, "a" * subscript, "A" * subscript) + KnuthBendix.set_inversion!(Al, "b" * subscript, "B" * subscript) + end + + if boundaries == 0 + word = Int[] + + for i in reverse(1:genus) + x = 4 * i + append!(word, [x, x-2, x-1, x-3]) + end + comms = Word(word) + rels = [ comms => one(comms) ] + + rws = RewritingSystem(rels, KnuthBendix.RecursivePathOrder(Al)) + KnuthBendix.knuthbendix!(rws) + elseif boundaries == 1 + S = typeof(one(Word(Int[]))) + rels = Pair{S, S}[] + rws = RewritingSystem(rels, KnuthBendix.LenLex(Al)) + else + throw("Not Implemented") + end + + return SurfaceGroup(genus, boundaries, KnuthBendix.letters(Al)[2:2:end], rels, rws) +end + +rewriting(G::SurfaceGroup) = G.rws +KnuthBendix.alphabet(G::SurfaceGroup) = alphabet(rewriting(G)) +relations(G::SurfaceGroup) = G.relations + + + + + + +function mapping_class_group(genus::Integer, punctures::Integer) + Σ = surface_group(genus, punctures) + + + + return New.AutomorphismGroup(Σ, S, rws, ntuple(i -> gens(F, i), n)) +end + +KnuthBendix.alphabet(G::AutomorphismGroup{<:SurfaceGroup}) = alphabet(rewriting(G)) diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl new file mode 100644 index 0000000..8aeae4b --- /dev/null +++ b/src/groups/symplectic_twists.jl @@ -0,0 +1,131 @@ +struct SymplecticMappingClass{N, T} <: GSymbol + id::Symbol # :A, :B + i::UInt + j::UInt + minus::Bool + inv::Bool + images::NTuple{N, T} + invimages::NTuple{N, T} + + function SymplecticMappingClass{N}(G, id, i, j, minus=false, inv=false) where N + @assert i > 0 && j > 0 + id === :A && @assert i ≠ j + + g = if id === :A + Te(G, i, j) * + Ta(N, i)^-1 * + Tα(N, i) * + Ta(N, i) * + Te(G, i, j)^-1 * + Tα(N,i)^-1 * + Ta(N, j)^-1 + elseif id === :B + if !minus + if i ≠ j + x = Ta(N, j) * Ta(N, i)^-1 * Tα(N, j) * Te(G,i,j) + δ = x * Tα(N, i) * x^-1 + Tα(N, i) * Tα(N, j) * inv(δ) + else + Tα(N, i)^-1 + end + else + if i ≠ j + Ta(N, i) * Ta(N, j) * Te(G, i, j)^-1 + else + Ta(N, i) + end + end + else + throw("Type not recognized: $id") + end + + res = new(id, i, j, minus, inv, + + + ) + + return res + end +end + +_indexing(n) = [(i, j) for i = 1:n for j in 1:n if i ≠ j] +_indexing_increasing(n) = [(i, j) for i = 1:n for j = i+1:n] + +_λs(N, A) = [ (i == j ? "aaaarggh..." : Word([A[λ(i, j)]])) for i = 1:N, j = 1:N] +_ϱs(N, A) = [ (i == j ? "aaaarggh..." : Word([A[ϱ(i, j)]])) for i = 1:N, j = 1:N] + +function Te_diagonal(G, i::Integer) + N = ngens(object(G)) + # @assert N == size(λ, 1) == size(ϱ, 1) + @assert iseven(N) + n = N ÷ 2 + j = i + 1 + @assert 1 <= i < n + + A = KnuthBendix.alphabet(G) + λ = _λs(N, A) + ϱ = _ϱs(N, A) + + # comments are for i,j = 1,2 + g = one(word_type(G)) + g *= λ[n+j, n+i] # β ↦ α*β + g *= λ[n+i, i] * inv(A, ϱ[n+i, j]) # α ↦ a*α*b^-1 + g *= inv(A, λ[n+j, n+i]) # β ↦ b*α^-1*a^-1*α*β + g *= λ[j, n+i] * inv(A, λ[j, i]) # b ↦ α + g *= inv(A, λ[j, n+i]) # b ↦ b*α^-1*a^-1*α + g *= inv(A, ϱ[j, n+i]) * ϱ[j, i] # b ↦ b*α^-1*a^-1*α*b*α^-1 + g *= ϱ[j, n+i] # b ↦ b*α^-1*a^-1*α*b*α^-1*a*α*b^-1 + return G(g) +end + +function Te_lantern(b₀::T, a₁::T, a₂::T, a₃::T, a₄::T, a₅::T) where {T} + a₀ = (a₁ * a₂ * a₃)^4 * b₀^-1 + X = a₄ * a₅ * a₃ * a₄ + b₁ = X^-1 * a₀ * X + Y = a₂ * a₃ * a₁ * a₂ + return Y^-1 * b₁ * Y # b₂ +end + +Ta(N, i::Integer) = λ[N÷2+i, i] +Tα(N, i::Integer, λ, A) = inv(A, λ[i, N÷2+i]) + +function Te(G, i, j) + @assert i ≠ j + i, j = i < j ? (i, j) : (j, i) + + N = ngens(object(G)) + + A = KnuthBendix.alphabet(G) + λ = _λs(N, A) + ϱ = _ϱs(N, A) + + if j == i + 1 + return Te_diagonal(G, i) + else + return Te_lantern( + Ta(N, i + 1, λ), + Ta(N, i, λ), + Tα(N, i, λ, A), + Te(N, i, i + 1), + Tα(N, i + 1, λ, A), + Te(N, i + 1, j), + ) + end +end + +function mcg_twists(genus::Integer) + genus < 3 && throw("Not Implemented: genus = $genus < 3") + + G = SpecialAutomorphismGroup(FreeGroup(2genus)) + A = KnuthBendix.alphabet(G) + + λ = _λs(G) + ϱ = _ϱs(G) + + Tas = [Ta(G, i, λ) for i in 1:genus] + Tαs = [Tα(G, i, λ, A) for i in 1:genus] + + Tes = [Te(G, i, j, λ, ϱ) for (i,j) in _indexing_increasing(genus)] + + return Tas, Tαs, Tes +end From 66a33e7c72da2d35b29006400c4fe6daf10a7fc4 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Mon, 7 Jun 2021 20:23:04 +0200 Subject: [PATCH 02/37] make evaluate[!] a general thing for AutomorphismGroups --- src/groups/sautFn.jl | 14 -------------- src/groups/transvections.jl | 2 +- src/new_autgroups.jl | 16 +++++++++++++++- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/groups/sautFn.jl b/src/groups/sautFn.jl index 17ba331..e6a22d9 100644 --- a/src/groups/sautFn.jl +++ b/src/groups/sautFn.jl @@ -18,17 +18,3 @@ function relations(G::AutomorphismGroup{<:FreeGroup}) n = length(KnuthBendix.alphabet(object(G))) ÷ 2 return last(gersten_relations(n, commutative = false)) end - -evaluate(f::FPGroupElement{<:AutomorphismGroup{<:FreeGroup}}) = evaluate!(domain(f), f) - -function evaluate!( - t::NTuple{N,T}, - f::FPGroupElement{<:AutomorphismGroup{<:FreeGroup}}, - tmp = one(first(t)), -) where {T<:FPGroupElement,N} - A = alphabet(f) - for idx in word(f) - t = @inbounds evaluate!(t, A[idx], alphabet(object(parent(f))), tmp)::NTuple{N,T} - end - return t -end diff --git a/src/groups/transvections.jl b/src/groups/transvections.jl index 06a3737..17b0d89 100644 --- a/src/groups/transvections.jl +++ b/src/groups/transvections.jl @@ -46,7 +46,7 @@ Base.:(==)(t::Transvection, s::Transvection) = t.id === s.id && t.ij == s.ij && t.inv == s.inv Base.hash(t::Transvection, h::UInt) = hash(t.id, hash(t.ij, hash(t.inv, h))) -Base.@propagate_inbounds function evaluate!(v::Tuple, t::Transvection, A::Alphabet, tmp=one(first(v))) +Base.@propagate_inbounds function evaluate!(v::NTuple{T, N}, t::Transvection, A::Alphabet, tmp=one(first(v))) where {T, N} i, j = indices(t) @assert i ≤ length(v) && j ≤ length(v) diff --git a/src/new_autgroups.jl b/src/new_autgroups.jl index 4122e58..ef09005 100644 --- a/src/new_autgroups.jl +++ b/src/new_autgroups.jl @@ -89,6 +89,20 @@ Base.show(io::IO, A::AutomorphismGroup) = print(io, "automorphism group of ", ob domain(f::FPGroupElement{<:AutomorphismGroup}) = deepcopy(parent(f).domain) # tuple(gens(object(parent(f)))...) -evaluate(f::FPGroupElement{<:AutomorphismGroup}) = throw("you need to implement `evaluate(::typeof(f))`") +evaluate(f::FPGroupElement{<:AutomorphismGroup}) = evaluate!(domain(f), f) +function evaluate!( + t::NTuple{N,T}, + f::FPGroupElement{<:AutomorphismGroup{<:Group}}, + tmp = one(first(t)), +) where {N, T} + A = alphabet(f) + AF = alphabet(object(parent(f))) + for idx in word(f) + t = @inbounds evaluate!(t, A[idx], AF, tmp)::NTuple{N,T} + end + return t end + +evaluate!(t::NTuple{N, T}, s::GSymbol, A, tmp=one(first(t))) where {N, T} = throw("you need to implement `evaluate!(::$(typeof(t)), ::$(typeof(s)), ::Alphabet, tmp=one(first(t)))`") + From 979ffaccfa859f67d06f228404b68f84741b4509 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Mon, 7 Jun 2021 20:25:39 +0200 Subject: [PATCH 03/37] some eye-candy --- src/new_autgroups.jl | 11 +++++++++++ src/new_types.jl | 20 +++++++++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/new_autgroups.jl b/src/new_autgroups.jl index ef09005..4147cbf 100644 --- a/src/new_autgroups.jl +++ b/src/new_autgroups.jl @@ -84,6 +84,17 @@ Base.show(io::IO, ::Type{<:FPGroupElement{<:AutomorphismGroup{T}}}) where {T} = Base.show(io::IO, A::AutomorphismGroup) = print(io, "automorphism group of ", object(A)) + +function Base.show(io::IO, ::MIME"text/plain", a::FPGroupElement{<:AutomorphismGroup}) + println(io, " ┌ $(a):") + im = evaluate(a) + d = domain(a) + for (x, imx) in zip(d, im[1:end-1]) + println(io, " │ $x ↦ $imx") + end + print(io, " └ $(last(d)) ↦ $(last(im))") +end + ## Automorphism Evaluation domain(f::FPGroupElement{<:AutomorphismGroup}) = deepcopy(parent(f).domain) diff --git a/src/new_types.jl b/src/new_types.jl index ca34915..0412cad 100644 --- a/src/new_types.jl +++ b/src/new_types.jl @@ -36,6 +36,14 @@ Base.@propagate_inbounds function (G::AbstractFPGroup)(word::AbstractVector{<:In return FPGroupElement(word_type(G)(word), G) end +function Base.show(io::IO, G::AbstractFPGroup) + print(io, "⟨") + join(io, gens(G), ", ") + print(io, " | ") + join(io, relations(G), ", ") + print(io, "⟩") +end + ## Group Interface Base.one(G::AbstractFPGroup) = FPGroupElement(one(word_type(G)), G) @@ -140,7 +148,7 @@ function FreeGroup(n::Integer) sizehint!(symbols, 2n) sizehint!(inverses, 2n) for i in 1:n - push!(symbols, Symbol(:f, i), Symbol(:F, i)) + push!(symbols, Symbol(:f, subscriptify(i)), Symbol(:F, subscriptify(i))) push!(inverses, 2i, 2i-1) end return FreeGroup(symbols[1:2:2n], Alphabet(symbols, inverses)) @@ -190,15 +198,9 @@ function FPGroup( return FPGroup(G.gens, rels, rws) end -function Base.show(io::IO, G::FPGroup) - print(io, "⟨") - join(io, gens(G), ", ") - print(io, " | ") - join(io, relations(G), ", ") - print(io, "⟩") -end - ## GSymbol aka letter of alphabet abstract type GSymbol end Base.literal_pow(::typeof(^), t::GSymbol, ::Val{-1}) = inv(t) + +subscriptify(i::Integer) = join('₀'+d for d in reverse(digits(i))) From d2388540958815dc01b09d8f9549faf5ff9b7d08 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Mon, 7 Jun 2021 20:26:18 +0200 Subject: [PATCH 04/37] first working version of Automorphisms of surface groups --- src/groups/mcg.jl | 47 ++++-- src/groups/symplectic_twists.jl | 251 +++++++++++++++++++++----------- 2 files changed, 202 insertions(+), 96 deletions(-) diff --git a/src/groups/mcg.jl b/src/groups/mcg.jl index f03aa1c..82d7a17 100644 --- a/src/groups/mcg.jl +++ b/src/groups/mcg.jl @@ -8,6 +8,15 @@ struct SurfaceGroup{T, S, R} <: AbstractFPGroup rws::R end +genus(S::SurfaceGroup) = S.genus + +function Base.show(io::IO, S::SurfaceGroup) + print(io, "π₁ of the orientable surface of genus $(genus(S))") + if S.boundaries > 0 + print(io, " with $(S.boundaries) boundary components") + end +end + function SurfaceGroup(genus::Integer, boundaries::Integer) @assert genus > 1 @@ -32,36 +41,50 @@ function SurfaceGroup(genus::Integer, boundaries::Integer) append!(word, [x, x-2, x-1, x-3]) end comms = Word(word) - rels = [ comms => one(comms) ] + word_rels = [ comms => one(comms) ] - rws = RewritingSystem(rels, KnuthBendix.RecursivePathOrder(Al)) + rws = RewritingSystem(word_rels, KnuthBendix.RecursivePathOrder(Al)) KnuthBendix.knuthbendix!(rws) elseif boundaries == 1 S = typeof(one(Word(Int[]))) - rels = Pair{S, S}[] - rws = RewritingSystem(rels, KnuthBendix.LenLex(Al)) + word_rels = Pair{S, S}[] + rws = RewritingSystem(word_rels, KnuthBendix.LenLex(Al)) else throw("Not Implemented") end + F = FreeGroup(alphabet(rws)) + rels = [F(lhs)=>F(rhs) for (lhs,rhs) in word_rels] + return SurfaceGroup(genus, boundaries, KnuthBendix.letters(Al)[2:2:end], rels, rws) end -rewriting(G::SurfaceGroup) = G.rws -KnuthBendix.alphabet(G::SurfaceGroup) = alphabet(rewriting(G)) -relations(G::SurfaceGroup) = G.relations +rewriting(S::SurfaceGroup) = S.rws +KnuthBendix.alphabet(S::SurfaceGroup) = alphabet(rewriting(S)) +relations(S::SurfaceGroup) = S.relations +function symplectic_twists(π₁Σ::SurfaceGroup) + g = genus(π₁Σ) + saut = SpecialAutomorphismGroup(FreeGroup(2g)) + Aij = [SymplecticMappingClass(π₁Σ, saut, :A, i, j) for i in 1:g for j in 1:g if i≠j] + Bij = [SymplecticMappingClass(π₁Σ, saut, :B, i, j) for i in 1:g for j in i+1:g] + mBij = [SymplecticMappingClass(π₁Σ, saut, :B, i, j, minus=true) for i in 1:g for j in i+1:g] -function mapping_class_group(genus::Integer, punctures::Integer) - Σ = surface_group(genus, punctures) + Bii = [SymplecticMappingClass(π₁Σ, saut, :B, i, i) for i in 1:g] + mBii = [SymplecticMappingClass(π₁Σ, saut, :B, i, i, minus=true) for i in 1:g] - - return New.AutomorphismGroup(Σ, S, rws, ntuple(i -> gens(F, i), n)) + return [Aij; Bij; mBij; Bii; mBii] end -KnuthBendix.alphabet(G::AutomorphismGroup{<:SurfaceGroup}) = alphabet(rewriting(G)) +KnuthBendix.alphabet(G::AutomorphismGroup{<:SurfaceGroup}) = rewriting(G) + +function AutomorphismGroup(π₁Σ::SurfaceGroup; kwargs...) + S = vcat(symplectic_twists(π₁Σ)...) + A = Alphabet(S) + return AutomorphismGroup(π₁Σ, S, A, ntuple(i->gens(π₁Σ, i), 2genus(π₁Σ))) +end diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index 8aeae4b..b2a769b 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -1,73 +1,32 @@ -struct SymplecticMappingClass{N, T} <: GSymbol - id::Symbol # :A, :B - i::UInt - j::UInt - minus::Bool - inv::Bool - images::NTuple{N, T} - invimages::NTuple{N, T} - - function SymplecticMappingClass{N}(G, id, i, j, minus=false, inv=false) where N - @assert i > 0 && j > 0 - id === :A && @assert i ≠ j - - g = if id === :A - Te(G, i, j) * - Ta(N, i)^-1 * - Tα(N, i) * - Ta(N, i) * - Te(G, i, j)^-1 * - Tα(N,i)^-1 * - Ta(N, j)^-1 - elseif id === :B - if !minus - if i ≠ j - x = Ta(N, j) * Ta(N, i)^-1 * Tα(N, j) * Te(G,i,j) - δ = x * Tα(N, i) * x^-1 - Tα(N, i) * Tα(N, j) * inv(δ) - else - Tα(N, i)^-1 - end - else - if i ≠ j - Ta(N, i) * Ta(N, j) * Te(G, i, j)^-1 - else - Ta(N, i) - end - end - else - throw("Type not recognized: $id") - end - - res = new(id, i, j, minus, inv, - - - ) - - return res - end +struct ΡΛ + id::Symbol + A::Alphabet + N::Int end -_indexing(n) = [(i, j) for i = 1:n for j in 1:n if i ≠ j] -_indexing_increasing(n) = [(i, j) for i = 1:n for j = i+1:n] +function Base.getindex(rl::ΡΛ, i::Integer, j::Integer) + @assert 1 ≤ i ≤ rl.N + @assert 1 ≤ j ≤ rl.N + @assert i ≠ j + @assert rl.id ∈ (:λ, :ϱ) + rl.id == :λ && return Word([rl.A[λ(i, j)]]) + rl.id == :ϱ && return Word([rl.A[ϱ(i, j)]]) +end -_λs(N, A) = [ (i == j ? "aaaarggh..." : Word([A[λ(i, j)]])) for i = 1:N, j = 1:N] -_ϱs(N, A) = [ (i == j ? "aaaarggh..." : Word([A[ϱ(i, j)]])) for i = 1:N, j = 1:N] +function Te_diagonal(λ::ΡΛ, ϱ::ΡΛ, i::Integer) + @assert λ.N == ϱ.N + @assert λ.id == :λ && ϱ.id == :ϱ -function Te_diagonal(G, i::Integer) - N = ngens(object(G)) - # @assert N == size(λ, 1) == size(ϱ, 1) + N = λ.N @assert iseven(N) n = N ÷ 2 j = i + 1 @assert 1 <= i < n - A = KnuthBendix.alphabet(G) - λ = _λs(N, A) - ϱ = _ϱs(N, A) + A = λ.A # comments are for i,j = 1,2 - g = one(word_type(G)) + g = one(Word(Int[])) g *= λ[n+j, n+i] # β ↦ α*β g *= λ[n+i, i] * inv(A, ϱ[n+i, j]) # α ↦ a*α*b^-1 g *= inv(A, λ[n+j, n+i]) # β ↦ b*α^-1*a^-1*α*β @@ -75,40 +34,44 @@ function Te_diagonal(G, i::Integer) g *= inv(A, λ[j, n+i]) # b ↦ b*α^-1*a^-1*α g *= inv(A, ϱ[j, n+i]) * ϱ[j, i] # b ↦ b*α^-1*a^-1*α*b*α^-1 g *= ϱ[j, n+i] # b ↦ b*α^-1*a^-1*α*b*α^-1*a*α*b^-1 - return G(g) + return g end -function Te_lantern(b₀::T, a₁::T, a₂::T, a₃::T, a₄::T, a₅::T) where {T} - a₀ = (a₁ * a₂ * a₃)^4 * b₀^-1 +function Te_lantern(A::Alphabet, b₀::T, a₁::T, a₂::T, a₃::T, a₄::T, a₅::T) where {T} + a₀ = (a₁ * a₂ * a₃)^4 * inv(A, b₀) X = a₄ * a₅ * a₃ * a₄ - b₁ = X^-1 * a₀ * X + b₁ = inv(A, X) * a₀ * X Y = a₂ * a₃ * a₁ * a₂ - return Y^-1 * b₁ * Y # b₂ + return inv(A, Y) * b₁ * Y # b₂ end -Ta(N, i::Integer) = λ[N÷2+i, i] -Tα(N, i::Integer, λ, A) = inv(A, λ[i, N÷2+i]) +Ta(λ::ΡΛ, i::Integer) = (@assert λ.id == :λ; +λ[λ.N÷2+i, i]) +Tα(λ::ΡΛ, i::Integer) = (@assert λ.id == :λ; +inv(λ.A, λ[i, λ.N÷2+i])) -function Te(G, i, j) +function Te(λ::ΡΛ, ϱ::ΡΛ, i, j) @assert i ≠ j i, j = i < j ? (i, j) : (j, i) - N = ngens(object(G)) + @assert λ.N == ϱ.N + @assert λ.A == ϱ.A + @assert λ.id == :λ && ϱ.id == :ϱ - A = KnuthBendix.alphabet(G) - λ = _λs(N, A) - ϱ = _ϱs(N, A) + @assert 1 ≤ i ≤ λ.N + @assert 1 ≤ j ≤ λ.N if j == i + 1 - return Te_diagonal(G, i) + return Te_diagonal(λ, ϱ, i) else return Te_lantern( - Ta(N, i + 1, λ), - Ta(N, i, λ), - Tα(N, i, λ, A), - Te(N, i, i + 1), - Tα(N, i + 1, λ, A), - Te(N, i + 1, j), + λ.A, + Ta(λ, i + 1), + Ta(λ, i), + Tα(λ, i), + Te(λ, ϱ, i, i + 1), + Tα(λ, i + 1), + Te(λ, ϱ, i + 1, j), ) end end @@ -116,16 +79,136 @@ end function mcg_twists(genus::Integer) genus < 3 && throw("Not Implemented: genus = $genus < 3") - G = SpecialAutomorphismGroup(FreeGroup(2genus)) + G = SpecialAutomorphismGroup(FreeGroup(2genus), maxrules = 1000) A = KnuthBendix.alphabet(G) - λ = _λs(G) - ϱ = _ϱs(G) + λ = ΡΛ(:λ, A, 2genus) + ϱ = ΡΛ(:ϱ, A, 2genus) - Tas = [Ta(G, i, λ) for i in 1:genus] - Tαs = [Tα(G, i, λ, A) for i in 1:genus] + Tas = [Ta(λ, i) for i in 1:genus] + Tαs = [Tα(λ, i) for i in 1:genus] - Tes = [Te(G, i, j, λ, ϱ) for (i,j) in _indexing_increasing(genus)] + idcs = ((i, j) for i in 1:genus for j in i+1:genus) + Tes = [Te(λ, ϱ, i, j) for (i, j) in idcs] return Tas, Tαs, Tes end + +struct SymplecticMappingClass{N,T} <: GSymbol + id::Symbol # :A, :B + i::UInt + j::UInt + minus::Bool + inv::Bool + images::NTuple{N,T} + invimages::NTuple{N,T} +end + +function SymplecticMappingClass( + Σ::SurfaceGroup, + sautFn, + id::Symbol, + i::Integer, + j::Integer; + minus = false, + inverse = false, +) + @assert i > 0 && j > 0 + id === :A && @assert i ≠ j + @assert 2genus(Σ) == ngens(object(sautFn)) + + A = KnuthBendix.alphabet(sautFn) + λ = ΡΛ(:λ, A, 2genus(Σ)) + ϱ = ΡΛ(:ϱ, A, 2genus(Σ)) + + w = if id === :A + Te(λ, ϱ, i, j) * + inv(A, Ta(λ, i)) * + Tα(λ, i) * + Ta(λ, i) * + inv(A, Te(λ, ϱ, i, j)) * + inv(A, Tα(λ, i)) * + inv(A, Ta(λ, j)) + elseif id === :B + if !minus + if i ≠ j + x = Ta(λ, j) * inv(A, Ta(λ, i)) * Tα(λ, j) * Te(λ, ϱ, i, j) + δ = x * Tα(λ, i) * inv(A, x) + Tα(λ, i) * Tα(λ, j) * inv(A, δ) + else + inv(A, Tα(λ, i)) + end + else + if i ≠ j + Ta(λ, i) * Ta(λ, j) * inv(A, Te(λ, ϱ, i, j)) + else + Ta(λ, i) + end + end + else + throw("Type not recognized: $id") + end + + g = sautFn(w) + + d = ntuple(i->gens(Σ, i), ngens(Σ)) + + img = evaluate!(deepcopy(d), g) + invim = evaluate!(d, inv(g)) + + img, invim = inverse ? (invim, img) : (img, invim) + + res = SymplecticMappingClass(id, UInt(i), UInt(j), minus, inverse, img, invim) + + return res +end + +function Base.show(io::IO, smc::SymplecticMappingClass) + smc.minus && print(io, 'm') + if smc.i < 10 && smc.j < 10 + print(io, smc.id, subscriptify(smc.i), subscriptify(smc.j)) + else + print(io, smc.id, subscriptify(smc.i), ".", subscriptify(smc.j)) + end + smc.inv && print(io, "^-1") +end + +function Base.inv(m::SymplecticMappingClass) + return SymplecticMappingClass(m.id, m.i, m.j, m.minus, !m.inv, m.invimages, m.images) +end + +function evaluate!( + t::NTuple{N,T}, + smc::SymplecticMappingClass, + A::Alphabet, + tmp = one(first(t)), +) where {N,T} + img = smc.inv ? smc.invimages : smc.images + + # need a map from generators to letters of the alphabet! + # TODO: move to SymplecticMappingClass + gens_idcs = let G = parent(first(t)) + Dict(A[G.gens[i]] => i for i in 1:ngens(G)) + end + + for elt in t + copyto!(tmp, elt) + resize!(word(elt), 0) + for idx in word(tmp) + # @show idx + k = if haskey(gens_idcs, idx) + img[gens_idcs[idx]] + else + inv(img[gens_idcs[inv(A, idx)]]) + end + append!(word(elt), word(k)) + end + _setnormalform!(elt, false) + _setvalidhash!(elt, false) + + normalform!(tmp, elt) + copyto!(elt, tmp) + end + + return t +end From e48b06f6c4ee5c70df371074b146d5eb3824807c Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Thu, 10 Jun 2021 12:54:27 +0200 Subject: [PATCH 05/37] move tests for AutFn --- test/{automorphisms.jl => AutFn.jl} | 0 test/runtests.jl | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) rename test/{automorphisms.jl => AutFn.jl} (100%) diff --git a/test/automorphisms.jl b/test/AutFn.jl similarity index 100% rename from test/automorphisms.jl rename to test/AutFn.jl diff --git a/test/runtests.jl b/test/runtests.jl index 49e864d..d9ffe03 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -33,7 +33,8 @@ using LinearAlgebra include("free_groups.jl") include("fp_groups.jl") - include("automorphisms.jl") + + include("AutFn.jl") include("benchmarks.jl") end end From 33086db01d6629220ee6594ce716b39bd5c5e36f Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Thu, 10 Jun 2021 12:55:21 +0200 Subject: [PATCH 06/37] =?UTF-8?q?add=20tests=20for=20Aut(=CE=A3=E2=82=84.?= =?UTF-8?q?=E2=82=81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Project.toml | 3 +- src/groups/mcg.jl | 4 +- test/AutSigma_41.jl | 193 ++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 2 + 4 files changed, 199 insertions(+), 3 deletions(-) create mode 100644 test/AutSigma_41.jl diff --git a/Project.toml b/Project.toml index 30d990a..b7fe35a 100644 --- a/Project.toml +++ b/Project.toml @@ -22,7 +22,8 @@ julia = "1.3, 1.4, 1.5" [extras] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test", "BenchmarkTools"] +test = ["Test", "BenchmarkTools", "PermutationGroups"] diff --git a/src/groups/mcg.jl b/src/groups/mcg.jl index 82d7a17..536574a 100644 --- a/src/groups/mcg.jl +++ b/src/groups/mcg.jl @@ -1,5 +1,3 @@ -include("symplectic_twists.jl") - struct SurfaceGroup{T, S, R} <: AbstractFPGroup genus::Int boundaries::Int @@ -8,6 +6,8 @@ struct SurfaceGroup{T, S, R} <: AbstractFPGroup rws::R end +include("symplectic_twists.jl") + genus(S::SurfaceGroup) = S.genus function Base.show(io::IO, S::SurfaceGroup) diff --git a/test/AutSigma_41.jl b/test/AutSigma_41.jl new file mode 100644 index 0000000..11c3e6a --- /dev/null +++ b/test/AutSigma_41.jl @@ -0,0 +1,193 @@ +using PermutationGroups + +@testset "Wajnryb presentation for Σ₄₁" begin + + genus = 4 + + G = New.SpecialAutomorphismGroup(New.FreeGroup(2genus)) + + T = let G = G; (Tas, Tαs, Tes) = New.mcg_twists(genus) + Ta = G.(Tas) + Tα = G.(Tαs) + Tes = G.(Tes) + + [Ta; Tα; Tes] + end + + a1 = T[1]^-1 # Ta₁ + a2 = T[5]^-1 # Tα₁ + a3 = T[9]^-1 # Te₁₂ + a4 = T[6]^-1 # Tα₂ + a5 = T[12]^-1 # Te₂₃ + a6 = T[7]^-1 # Tα₃ + a7 = T[14]^-1 # Te₃₄ + a8 = T[8]^-1 # Tα₄ + + b0 = T[2]^-1 # Ta₂ + a0 = (a1*a2*a3)^4*b0^-1 # from the 3-chain relation + X = a4*a5*a3*a4 # auxillary, not present in the Primer + b1 = X^-1*a0*X + b2 = T[10]^-1 # Te₁₃ + + As = T[[1,5,9,6,12,7,14,8]] # the inverses of the elements a + + @testset "commutation relations" begin + for (i, ai) in enumerate(As) #the element ai here is actually the inverse of ai before. It does not matter for commutativity. Also, a0 is as defined before. + for (j, aj) in enumerate(As) + if abs(i-j) > 1 + @test ai*aj == aj*ai + elseif i ≠ j + @test ai*aj != aj*ai + end + end + if i != 4 + @test a0*ai == ai*a0 + end + end + end + + @testset "braid relations" begin + for (i, ai) in enumerate(As) #the element ai here is actually the inverse of ai before. It does not matter for braid relations. + for (j, aj) in enumerate(As) + if abs(i-j) == 1 + @test ai*aj*ai == aj*ai*aj + end + end + end + @test a0*a4*a0 == a4*a0*a4 # here, a0 and a4 are as before + end + + @testset "Lantern relation" begin + + @testset "b2 definition" begin + @test b2 == (a2*a3*a1*a2)^-1*b1*(a2*a3*a1*a2) + + # some additional tests, checking what explicitly happens to the generators of the π₁ under b2 + d = New.domain(b2) + im = New.evaluate(b2) + z = im[7]*d[7]^-1 + + @test im[1] == d[1] + @test im[2] == z*d[2]*z^-1 + @test im[3] == z*d[3]*z^-1 + @test im[4] == d[4] + + @test im[5] == d[5]*z^-1 + @test im[6] == z*d[6]*z^-1 + @test im[7] == z*d[7] + @test im[8] == d[8] + + end + + @testset "b2: commutation relations" begin + @test b2*a1 == a1*b2 + @test b2*a2 != a2*b2 + @test b2*a3 == a3*b2 + @test b2*a4 == a4*b2 + @test b2*a5 == a5*b2 + @test b2*a6 != a6*b2 + end + + @testset "b2: braid relations" begin + @test a2*b2*a2 == b2*a2*b2 + @test a6*b2*a6 == b2*a6*b2 + end + + @testset "lantern" begin + u = (a6*a5)^-1*b1*(a6*a5) + x = (a6*a5*a4*a3*a2*u*a1^-1*a2^-1*a3^-1*a4^-1) # yet another auxillary + # x = (a4^-1*a3^-1*a2^-1*a1^-1*u*a2*a3*a4*a5*a6) + @time New.evaluate(x) + b3 = x*a0*x^-1 + @time New.evaluate(b3) + @test a0*b2*b1 == a1*a3*a5*b3 + end + end + + + @testset "Te₁₂ definition" begin + G = parent(first(T)) + F₈ = New.object(G) + (a, b, c, d, α, β, γ, δ) = gens(F₈) + + A = KnuthBendix.alphabet(G) + + λ = [i == j ? one(G) : G([A[New.λ(i,j)]]) for i in 1:8, j in 1:8] + ϱ = [i == j ? one(G) : G([A[New.ϱ(i,j)]]) for i in 1:8, j in 1:8] + + g = one(G) + # @show g + # @show g(Groups.domain(G)) + + # β ↦ α*β + g *= λ[6,5] + @test New.evaluate(g)[6] == α*β + + # α ↦ a*α*b^-1 + g *= λ[5,1]*inv(ϱ[5,2]) + @test New.evaluate(g)[5] == a*α*b^-1 + + # β ↦ b*α^-1*a^-1*α*β + g *= inv(λ[6,5]) + @test New.evaluate(g)[6] == b*α^-1*a^-1*α*β + + # b ↦ α + g *= λ[2,5]*inv(λ[2,1]); + @test New.evaluate(g)[2] == α + + # b ↦ b*α^-1*a^-1*α + g *= inv(λ[2,5]); + @test New.evaluate(g)[2] == b*α^-1*a^-1*α + + # b ↦ b*α^-1*a^-1*α*b*α^-1 + g *= inv(ϱ[2,5])*ϱ[2,1]; + @test New.evaluate(g)[2] == b*α^-1*a^-1*α*b*α^-1 + + # b ↦ b*α^-1*a^-1*α*b*α^-1*a*α*b^-1 + g *= ϱ[2,5]; + @test New.evaluate(g)[2] == b*α^-1*a^-1*α*b*α^-1*a*α*b^-1 + + x = b*α^-1*a^-1*α + @test New.evaluate(g) == # (a, b, c, d, α, β, γ, δ) + (a, x*b*x^-1, c, d, α*x^-1, x*β, γ, δ) + @test g == T[9] + end + + Base.conj(t::New.Transvection, p::Perm) = + New.Transvection(t.id, t.i^p, t.j^p, t.inv) + + function Base.conj(elt::New.FPGroupElement, p::Perm) + G = parent(elt) + A = New.alphabet(elt) + return G([A[conj(A[idx], p)] for idx in New.word(elt)]) + end + + @testset "Te₂₃ definition" begin + Te₁₂, Te₂₃ = T[9], T[12] + G = parent(Te₁₂) + F₈ = New.object(G) + (a, b, c, d, α, β, γ, δ) = gens(F₈) + + img_Te₂₃ = New.evaluate(Te₂₃) + y = c*β^-1*b^-1*β + @test img_Te₂₃ == (a, b, y*c*y^-1, d, α, β*y^-1, y*γ, δ) + + σ = perm"(1,2,3)(5,6,7)(8)" + Te₂₃_σ = conj(Te₁₂, σ) + # @test New.word(Te₂₃_σ) == New.word(Te₂₃) + + @test New.evaluate(Te₂₃_σ) == New.evaluate(Te₂₃) + @test Te₂₃ == Te₂₃_σ + end + + @testset "Te₃₄ definition" begin + Te₁₂, Te₃₄ = T[9], T[14] + G = parent(Te₁₂) + F₈ = New.object(G) + (a, b, c, d, α, β, γ, δ) = Groups.gens(F₈) + + σ = perm"(1,3)(2,4)(5,7)(6,8)" + Te₃₄_σ = conj(Te₁₂, σ) + @test Te₃₄ == Te₃₄_σ + end +end diff --git a/test/runtests.jl b/test/runtests.jl index d9ffe03..031e565 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -35,6 +35,8 @@ using LinearAlgebra include("fp_groups.jl") include("AutFn.jl") + include("AutSigma_41.jl") + include("benchmarks.jl") end end From ef1a2f88da1df57c106ee17ebfe3c9f336ddba1a Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 19 Jun 2021 00:38:11 +0200 Subject: [PATCH 07/37] don't run benchmarks during CI --- test/runtests.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 031e565..c7633d5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -37,6 +37,8 @@ using LinearAlgebra include("AutFn.jl") include("AutSigma_41.jl") - include("benchmarks.jl") + if !haskey(ENV, "CI") + include("benchmarks.jl") + end end end From 29b6675a8ab820672de7d8ddd0ee58c6b6880dfd Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 29 Jun 2021 16:52:35 +0200 Subject: [PATCH 08/37] introduce AbstractFPGroupElement --- src/autgroups.jl | 14 +++++++------- src/groups/mcg.jl | 2 +- src/hashing.jl | 16 ++++++++-------- src/normalform.jl | 6 +++--- src/types.jl | 37 +++++++++++++++++++++---------------- 5 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/autgroups.jl b/src/autgroups.jl index d214005..5df39de 100644 --- a/src/autgroups.jl +++ b/src/autgroups.jl @@ -14,7 +14,7 @@ end object(G::AutomorphismGroup) = G.group rewriting(G::AutomorphismGroup) = G.rws -function equality_data(f::FPGroupElement{<:AutomorphismGroup}) +function equality_data(f::AbstractFPGroupElement{<:AutomorphismGroup}) imf = evaluate(f) # return normalform!.(imf) @@ -26,7 +26,7 @@ function equality_data(f::FPGroupElement{<:AutomorphismGroup}) return imf end -function Base.:(==)(g::A, h::A) where {A<:FPGroupElement{<:AutomorphismGroup}} +function Base.:(==)(g::A, h::A) where {A<:AbstractFPGroupElement{<:AutomorphismGroup}} @assert parent(g) === parent(h) if _isvalidhash(g) && _isvalidhash(h) @@ -70,7 +70,7 @@ function Base.:(==)(g::A, h::A) where {A<:FPGroupElement{<:AutomorphismGroup}} return equal end -function Base.isone(g::FPGroupElement{<:AutomorphismGroup}) +function Base.isone(g::AbstractFPGroupElement{<:AutomorphismGroup}) if length(word(g)) > 8 normalform!(g) end @@ -79,21 +79,21 @@ end # eye-candy -Base.show(io::IO, ::Type{<:FPGroupElement{<:AutomorphismGroup{T}}}) where {T} = +Base.show(io::IO, ::Type{<:AbstractFPGroupElement{<:AutomorphismGroup{T}}}) where {T} = print(io, "Automorphism{$T,…}") Base.show(io::IO, A::AutomorphismGroup) = print(io, "automorphism group of ", object(A)) ## Automorphism Evaluation -domain(f::FPGroupElement{<:AutomorphismGroup}) = deepcopy(parent(f).domain) +domain(f::AbstractFPGroupElement{<:AutomorphismGroup}) = deepcopy(parent(f).domain) # tuple(gens(object(parent(f)))...) -evaluate(f::FPGroupElement{<:AutomorphismGroup}) = evaluate!(domain(f), f) +evaluate(f::AbstractFPGroupElement{<:AutomorphismGroup}) = evaluate!(domain(f), f) function evaluate!( t::NTuple{N,T}, - f::FPGroupElement{<:AutomorphismGroup{<:Group}}, + f::AbstractFPGroupElement{<:AutomorphismGroup{<:Group}}, tmp = one(first(t)), ) where {N, T} A = alphabet(f) diff --git a/src/groups/mcg.jl b/src/groups/mcg.jl index 536574a..37f40a4 100644 --- a/src/groups/mcg.jl +++ b/src/groups/mcg.jl @@ -43,7 +43,7 @@ function SurfaceGroup(genus::Integer, boundaries::Integer) comms = Word(word) word_rels = [ comms => one(comms) ] - rws = RewritingSystem(word_rels, KnuthBendix.RecursivePathOrder(Al)) + rws = KnuthBendix.RewritingSystem(word_rels, KnuthBendix.RecursivePathOrder(Al)) KnuthBendix.knuthbendix!(rws) elseif boundaries == 1 S = typeof(one(Word(Int[]))) diff --git a/src/hashing.jl b/src/hashing.jl index b8d7e4b..ed729b1 100644 --- a/src/hashing.jl +++ b/src/hashing.jl @@ -1,6 +1,6 @@ ## Hashing -equality_data(g::FPGroupElement) = (normalform!(g); word(g)) +equality_data(g::AbstractFPGroupElement) = (normalform!(g); word(g)) bitget(h::UInt, n::Int) = Bool((h & (1 << n)) >> n) bitclear(h::UInt, n::Int) = h & ~(1 << n) @@ -14,30 +14,30 @@ bitset(h::UInt, v::Bool, n::Int) = v ? bitset(h, n) : bitclear(h, n) # * `savedhash & 2` (the second bit): is the hash valid? const __BITFLAGS_MASK = ~(~(UInt(0)) << 2) -isnormalform(g::FPGroupElement) = bitget(g.savedhash, 0) -_isvalidhash(g::FPGroupElement) = bitget(g.savedhash, 1) +isnormalform(g::AbstractFPGroupElement) = bitget(g.savedhash, 0) +_isvalidhash(g::AbstractFPGroupElement) = bitget(g.savedhash, 1) _setnormalform(h::UInt, v::Bool) = bitset(h, v, 0) _setvalidhash(h::UInt, v::Bool) = bitset(h, v, 1) -_setnormalform!(g::FPGroupElement, v::Bool) = g.savedhash = _setnormalform(g.savedhash, v) -_setvalidhash!(g::FPGroupElement, v::Bool) = g.savedhash = _setvalidhash(g.savedhash, v) +_setnormalform!(g::AbstractFPGroupElement, v::Bool) = g.savedhash = _setnormalform(g.savedhash, v) +_setvalidhash!(g::AbstractFPGroupElement, v::Bool) = g.savedhash = _setvalidhash(g.savedhash, v) # To update hash use this internal method, possibly only after computing the # normal form of `g`: -function _update_savedhash!(g::FPGroupElement, data) +function _update_savedhash!(g::AbstractFPGroupElement, data) h = hash(data, hash(parent(g))) h = (h << count_ones(__BITFLAGS_MASK)) | (__BITFLAGS_MASK & g.savedhash) g.savedhash = _setvalidhash(h, true) return g end -function Base.hash(g::FPGroupElement, h::UInt) +function Base.hash(g::AbstractFPGroupElement, h::UInt) _isvalidhash(g) || _update_savedhash!(g, equality_data(g)) return hash(g.savedhash >> count_ones(__BITFLAGS_MASK), h) end -function Base.copyto!(res::FPGroupElement, g::FPGroupElement) +function Base.copyto!(res::AbstractFPGroupElement, g::AbstractFPGroupElement) @assert parent(res) === parent(g) resize!(word(res), length(word(g))) copyto!(word(res), word(g)) diff --git a/src/normalform.jl b/src/normalform.jl index f1d67ff..0b799e3 100644 --- a/src/normalform.jl +++ b/src/normalform.jl @@ -2,7 +2,7 @@ normalform!(g::FPGroupElement) Compute the normal form of `g`, possibly modifying `g` in-place. """ -@inline function normalform!(g::FPGroupElement) +@inline function normalform!(g::AbstractFPGroupElement) isnormalform(g) && return g let w = one(word(g)) @@ -21,7 +21,7 @@ end normalform!(res::GEl, g::GEl) where GEl<:FPGroupElement Compute the normal fom of `g`, storing it in `res`. """ -function normalform!(res::GEl, g::GEl) where {GEl<:FPGroupElement} +function normalform!(res::GEl, g::GEl) where {GEl<:AbstractFPGroupElement} @boundscheck @assert parent(res) === parent(g) if isnormalform(g) copyto!(res, g) @@ -40,7 +40,7 @@ Append the normal form of `g` to word `res`, modifying `res` in place. Defaults to the rewriting in the free group. """ -@inline function normalform!(res::AbstractWord, g::FPGroupElement) +@inline function normalform!(res::AbstractWord, g::AbstractFPGroupElement) isone(res) && isnormalform(g) && return append!(res, word(g)) return KnuthBendix.rewrite_from_left!(res, word(g), rewriting(parent(g))) end diff --git a/src/types.jl b/src/types.jl index bed9221..7b39048 100644 --- a/src/types.jl +++ b/src/types.jl @@ -57,10 +57,12 @@ Base.isfinite(::AbstractFPGroup) = (@warn "using generic isfinite(::AbstractFPGr ## FPGroupElement -mutable struct FPGroupElement{G<:AbstractFPGroup,W<:AbstractWord} <: GroupElement +abstract type AbstractFPGroupElement{Gr} <: GroupElement end + +mutable struct FPGroupElement{Gr<:AbstractFPGroup,W<:AbstractWord} <: AbstractFPGroupElement{Gr} word::W savedhash::UInt - parent::G + parent::Gr FPGroupElement(word::W, G::AbstractFPGroup) where {W<:AbstractWord} = new{typeof(G),W}(word, UInt(0), G) @@ -69,22 +71,22 @@ mutable struct FPGroupElement{G<:AbstractFPGroup,W<:AbstractWord} <: GroupElemen new{typeof(G),W}(word, hash, G) end -word(f::FPGroupElement) = f.word +word(f::AbstractFPGroupElement) = f.word #convenience -KnuthBendix.alphabet(g::FPGroupElement) = alphabet(parent(g)) +KnuthBendix.alphabet(g::AbstractFPGroupElement) = alphabet(parent(g)) -function Base.show(io::IO, f::FPGroupElement) +function Base.show(io::IO, f::AbstractFPGroupElement) f = normalform!(f) KnuthBendix.print_repr(io, word(f), alphabet(f)) end ## GroupElement Interface for FPGroupElement -Base.parent(f::FPGroupElement) = f.parent -GroupsCore.parent_type(::Type{<:FPGroupElement{G}}) where {G} = G +Base.parent(f::AbstractFPGroupElement) = f.parent +GroupsCore.parent_type(::Type{<:AbstractFPGroupElement{G}}) where {G} = G -function Base.:(==)(g::FPGroupElement, h::FPGroupElement) +function Base.:(==)(g::AbstractFPGroupElement, h::AbstractFPGroupElement) @boundscheck @assert parent(g) === parent(h) normalform!(g) normalform!(h) @@ -96,17 +98,20 @@ function Base.deepcopy_internal(g::FPGroupElement, stackdict::IdDict) return FPGroupElement(copy(word(g)), g.savedhash, parent(g)) end -Base.inv(g::FPGroupElement) = (G = parent(g); FPGroupElement(inv(alphabet(G), word(g)), G)) - -function Base.:(*)(g::FPGroupElement, h::FPGroupElement) - @boundscheck @assert parent(g) === parent(h) - return FPGroupElement(word(g) * word(h), parent(g)) +function Base.inv(g::GEl) where GEl <: AbstractFPGroupElement + G = parent(g) + return GEl(inv(alphabet(G), word(g)), G) end -GroupsCore.isfiniteorder(g::FPGroupElement) = isone(g) ? true : (@warn "using generic isfiniteorder(::FPGroupElement): the returned `false` might be wrong"; false) +function Base.:(*)(g::GEl, h::GEl) where GEl<:AbstractFPGroupElement + @boundscheck @assert parent(g) === parent(h) + return GEl(word(g) * word(h), parent(g)) +end + +GroupsCore.isfiniteorder(g::AbstractFPGroupElement) = isone(g) ? true : (@warn "using generic isfiniteorder(::AbstractFPGroupElement): the returned `false` might be wrong"; false) # additional methods: -Base.isone(g::FPGroupElement) = (normalform!(g); isempty(word(g))) +Base.isone(g::AbstractFPGroupElement) = (normalform!(g); isempty(word(g))) ## Free Groups @@ -157,7 +162,7 @@ relations(F::FreeGroup) = Pair{eltype(F)}[] # these are mathematically correct Base.isfinite(::FreeGroup) = false -GroupsCore.isfiniteorder(g::FPGroupElement{<:FreeGroup}) = isone(g) ? true : false +GroupsCore.isfiniteorder(g::AbstractFPGroupElement{<:FreeGroup}) = isone(g) ? true : false ## FP Groups From 2f5401be837ba32433db9e5abfa48cf88d1a146e Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 29 Jun 2021 16:54:00 +0200 Subject: [PATCH 09/37] add maxrules to SpecialAutomorphismGroup --- src/groups/sautFn.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/groups/sautFn.jl b/src/groups/sautFn.jl index c47612f..ad71282 100644 --- a/src/groups/sautFn.jl +++ b/src/groups/sautFn.jl @@ -7,8 +7,10 @@ function SpecialAutomorphismGroup(F::FreeGroup; ordering = KnuthBendix.LenLex, k A, rels = gersten_relations(n, commutative = false) S = KnuthBendix.letters(A)[1:2(n^2-n)] + maxrules = 1000*n + rws = KnuthBendix.RewritingSystem(rels, ordering(A)) - KnuthBendix.knuthbendix!(rws; kwargs...) + @time KnuthBendix.knuthbendix!(rws; maxrules=maxrules, kwargs...) return AutomorphismGroup(F, S, rws, ntuple(i -> gens(F, i), n)) end From b191457526bfe6a5ad1a84aa99e59356a84a5e07 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Mon, 5 Jul 2021 15:05:03 +0200 Subject: [PATCH 10/37] fix: AbstractFPGroupElement change order of args in FPGroupElement --- src/types.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/types.jl b/src/types.jl index 7b39048..7486a8a 100644 --- a/src/types.jl +++ b/src/types.jl @@ -64,11 +64,11 @@ mutable struct FPGroupElement{Gr<:AbstractFPGroup,W<:AbstractWord} <: AbstractFP savedhash::UInt parent::Gr - FPGroupElement(word::W, G::AbstractFPGroup) where {W<:AbstractWord} = - new{typeof(G),W}(word, UInt(0), G) - - FPGroupElement(word::W, hash::UInt, G::AbstractFPGroup) where {W<:AbstractWord} = + FPGroupElement(word::W, G::AbstractFPGroup, hash::UInt=UInt(0)) where {W<:AbstractWord} = new{typeof(G),W}(word, hash, G) + + FPGroupElement{Gr, W}(word::AbstractWord, G::Gr) where {Gr, W} = + new{Gr, W}(word, UInt(0), G) end word(f::AbstractFPGroupElement) = f.word @@ -95,7 +95,7 @@ function Base.:(==)(g::AbstractFPGroupElement, h::AbstractFPGroupElement) end function Base.deepcopy_internal(g::FPGroupElement, stackdict::IdDict) - return FPGroupElement(copy(word(g)), g.savedhash, parent(g)) + return FPGroupElement(copy(word(g)), parent(g), g.savedhash) end function Base.inv(g::GEl) where GEl <: AbstractFPGroupElement From 939d231bbe8fb78b7e84e0b9c47c0aa96320125d Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Mon, 5 Jul 2021 15:05:37 +0200 Subject: [PATCH 11/37] pretty printing for automorphisms --- src/autgroups.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/autgroups.jl b/src/autgroups.jl index 5df39de..0361116 100644 --- a/src/autgroups.jl +++ b/src/autgroups.jl @@ -84,6 +84,16 @@ Base.show(io::IO, ::Type{<:AbstractFPGroupElement{<:AutomorphismGroup{T}}}) wher Base.show(io::IO, A::AutomorphismGroup) = print(io, "automorphism group of ", object(A)) +function Base.show(io::IO, ::MIME"text/plain", a::AbstractFPGroupElement{<:AutomorphismGroup}) + println(io, " ┌ $(a):") + d = domain(a) + im = evaluate(a) + for (x, imx) in zip(d, im[1:end-1]) + println(io, " │ $x ↦ $imx") + end + println(io, " └ $(last(d)) ↦ $(last(im))") +end + ## Automorphism Evaluation domain(f::AbstractFPGroupElement{<:AutomorphismGroup}) = deepcopy(parent(f).domain) From 15a737bfa5e197614c520e8735a3e530553dd37a Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Mon, 5 Jul 2021 15:05:57 +0200 Subject: [PATCH 12/37] add PermAut(omorphisms) --- src/groups/transvections.jl | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/groups/transvections.jl b/src/groups/transvections.jl index e405359..e01d4fc 100644 --- a/src/groups/transvections.jl +++ b/src/groups/transvections.jl @@ -81,3 +81,26 @@ Base.@propagate_inbounds function evaluate!(v::NTuple{T, N}, t::Transvection, A: return v end + +struct PermAut <: GSymbol + perm::Vector{UInt8} + + function PermAut(p::AbstractVector{<:Integer}) + @assert sort(p) == 1:length(p) + return new(p) + end +end + +function Base.show(io::IO, p::PermAut) + print(io, 'σ') + join(io, (subscriptify(Int(i)) for i in p.perm)) +end + +Base.inv(p::PermAut) = PermAut(invperm(p.perm)) + +Base.:(==)(p::PermAut, q::PermAut) = p.perm == q.perm +Base.hash(p::PermAut, h::UInt) = hash(p.perm, hash(PermAut, h)) + +Base.@propagate_inbounds function evaluate!(v::NTuple{T, N}, p::PermAut, ::Alphabet, tmp=one(first(v))) where {T, N} + return v[p.perm] +end From 79a4450ef9a3186d6fd7716b10fe85c454fecf4f Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 7 Jul 2021 10:32:13 +0200 Subject: [PATCH 13/37] create mcg_twists from Aut(F_n) directly --- src/groups/symplectic_twists.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index b2a769b..14a7f97 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -76,10 +76,13 @@ function Te(λ::ΡΛ, ϱ::ΡΛ, i, j) end end -function mcg_twists(genus::Integer) +function mcg_twists(G::AutomorphismGroup{<:FreeGroup}) + + @assert iseven(ngens(object(G))) + genus = ngens(object(G)) ÷ 2 + genus < 3 && throw("Not Implemented: genus = $genus < 3") - G = SpecialAutomorphismGroup(FreeGroup(2genus), maxrules = 1000) A = KnuthBendix.alphabet(G) λ = ΡΛ(:λ, A, 2genus) From 3629b07626404d5058e486c73d14f5a74e9c57a3 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 7 Jul 2021 10:33:34 +0200 Subject: [PATCH 14/37] add gens_idcs to SymplecticMappingClass --- src/groups/symplectic_twists.jl | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index 14a7f97..08a9134 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -105,6 +105,7 @@ struct SymplecticMappingClass{N,T} <: GSymbol inv::Bool images::NTuple{N,T} invimages::NTuple{N,T} + gens_idcs::Dict{Int, Int} end function SymplecticMappingClass( @@ -161,7 +162,9 @@ function SymplecticMappingClass( img, invim = inverse ? (invim, img) : (img, invim) - res = SymplecticMappingClass(id, UInt(i), UInt(j), minus, inverse, img, invim) + gens_idcs = Dict(alphabet(Σ)[Σ.gens[i]] => i for i in 1:ngens(Σ)) + + res = SymplecticMappingClass(id, UInt(i), UInt(j), minus, inverse, img, invim, gens_idcs) return res end @@ -177,7 +180,7 @@ function Base.show(io::IO, smc::SymplecticMappingClass) end function Base.inv(m::SymplecticMappingClass) - return SymplecticMappingClass(m.id, m.i, m.j, m.minus, !m.inv, m.invimages, m.images) + return SymplecticMappingClass(m.id, m.i, m.j, m.minus, !m.inv, m.invimages, m.images, m.gens_idcs) end function evaluate!( @@ -186,23 +189,17 @@ function evaluate!( A::Alphabet, tmp = one(first(t)), ) where {N,T} - img = smc.inv ? smc.invimages : smc.images - - # need a map from generators to letters of the alphabet! - # TODO: move to SymplecticMappingClass - gens_idcs = let G = parent(first(t)) - Dict(A[G.gens[i]] => i for i in 1:ngens(G)) - end + img = smc.images for elt in t copyto!(tmp, elt) resize!(word(elt), 0) for idx in word(tmp) # @show idx - k = if haskey(gens_idcs, idx) - img[gens_idcs[idx]] + k = if haskey(smc.gens_idcs, idx) + img[smc.gens_idcs[idx]] else - inv(img[gens_idcs[inv(A, idx)]]) + inv(img[smc.gens_idcs[inv(A, idx)]]) end append!(word(elt), word(k)) end From 99fd238cf246d136be0a6f4335d816737928fdb6 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 7 Jul 2021 10:34:42 +0200 Subject: [PATCH 15/37] =?UTF-8?q?permute=20=CF=80=E2=82=81=CE=A3=20generat?= =?UTF-8?q?ors=20to=20maintain=20coherent=20order=20with=20Aut(F=5Fn)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/groups/symplectic_twists.jl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index 08a9134..00e37af 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -155,10 +155,15 @@ function SymplecticMappingClass( g = sautFn(w) - d = ntuple(i->gens(Σ, i), ngens(Σ)) + perm = let g = genus(Σ) + [reverse(1+1:2:2g); reverse(1:2:2g)] + end - img = evaluate!(deepcopy(d), g) - invim = evaluate!(d, inv(g)) + d = ntuple(i->gens(Σ, i), ngens(Σ))[perm] + + + img = evaluate!(deepcopy(d), g)[invperm(perm)] + invim = evaluate!(d, inv(g))[invperm(perm)] img, invim = inverse ? (invim, img) : (img, invim) From eef13c9afa124da568f845c57f07212dbbe49881 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 7 Jul 2021 10:36:40 +0200 Subject: [PATCH 16/37] Since SMC act from the left(!) distinguish Aut(Left|Right)Perm --- src/groups/symplectic_twists.jl | 51 +++++++++++++++++++++++++++++++++ src/groups/transvections.jl | 14 ++++----- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index 00e37af..74cd8ba 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -217,3 +217,54 @@ function evaluate!( return t end + +struct PermLeftAut <: GSymbol + perm::Vector{UInt8} + + function PermLeftAut(p::AbstractVector{<:Integer}) + @assert sort(p) == 1:length(p) + return new(p) + end +end + +function Base.show(io::IO, p::PermLeftAut) + print(io, "σˡ") + join(io, (subscriptify(Int(i)) for i in p.perm)) +end + +Base.inv(p::PermLeftAut) = PermLeftAut(invperm(p.perm)) + +Base.:(==)(p::PermLeftAut, q::PermLeftAut) = p.perm == q.perm +Base.hash(p::PermLeftAut, h::UInt) = hash(p.perm, hash(PermLeftAut, h)) + +function evaluate!(v::NTuple{T, N}, p::PermLeftAut, ::Alphabet, tmp=one(first(v))) where {T, N} + + G = parent(first(v)) + A = alphabet(G) + img = gens(G)[p.perm] + gens_idcs = Dict(A[A[first(word(im))]] => img[i] for (i,im) in enumerate(gens(G))) + + for elt in v + copyto!(tmp, elt) + resize!(word(elt), 0) + for idx in word(tmp) + # @show idx + if haskey(gens_idcs, idx) + k = gens_idcs[idx] + append!(word(elt), word(k)) + elseif haskey(gens_idcs, inv(A, idx)) + k = inv(gens_idcs[inv(A, idx)]) + append!(word(elt), word(k)) + else + push!(word(elt), idx) + end + end + _setnormalform!(elt, false) + _setvalidhash!(elt, false) + + normalform!(tmp, elt) + copyto!(elt, tmp) + end + + return v +end diff --git a/src/groups/transvections.jl b/src/groups/transvections.jl index e01d4fc..1b9a358 100644 --- a/src/groups/transvections.jl +++ b/src/groups/transvections.jl @@ -82,25 +82,25 @@ Base.@propagate_inbounds function evaluate!(v::NTuple{T, N}, t::Transvection, A: return v end -struct PermAut <: GSymbol +struct PermRightAut <: GSymbol perm::Vector{UInt8} - function PermAut(p::AbstractVector{<:Integer}) + function PermRightAut(p::AbstractVector{<:Integer}) @assert sort(p) == 1:length(p) return new(p) end end -function Base.show(io::IO, p::PermAut) +function Base.show(io::IO, p::PermRightAut) print(io, 'σ') join(io, (subscriptify(Int(i)) for i in p.perm)) end -Base.inv(p::PermAut) = PermAut(invperm(p.perm)) +Base.inv(p::PermRightAut) = PermRightAut(invperm(p.perm)) -Base.:(==)(p::PermAut, q::PermAut) = p.perm == q.perm -Base.hash(p::PermAut, h::UInt) = hash(p.perm, hash(PermAut, h)) +Base.:(==)(p::PermRightAut, q::PermRightAut) = p.perm == q.perm +Base.hash(p::PermRightAut, h::UInt) = hash(p.perm, hash(PermRightAut, h)) -Base.@propagate_inbounds function evaluate!(v::NTuple{T, N}, p::PermAut, ::Alphabet, tmp=one(first(v))) where {T, N} +function evaluate!(v::NTuple{T, N}, p::PermRightAut, ::Alphabet, tmp=one(first(v))) where {T, N} return v[p.perm] end From 9415906aa2827fb39ad5b2daecb8f6f1aa0c4373 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 7 Jul 2021 10:41:34 +0200 Subject: [PATCH 17/37] update AutSigma_41 tests --- Project.toml | 4 +- test/AutSigma_41.jl | 230 ++++++++++++++++++++++++++++++-------------- test/runtests.jl | 1 + 3 files changed, 161 insertions(+), 74 deletions(-) diff --git a/Project.toml b/Project.toml index cd77d3f..7e007b9 100644 --- a/Project.toml +++ b/Project.toml @@ -15,13 +15,15 @@ AbstractAlgebra = "0.15, 0.16" GroupsCore = "^0.3" KnuthBendix = "^0.2.1" OrderedCollections = "1" +PermutationGroups = "^0.3" ThreadsX = "^0.1.0" julia = "1.3, 1.4, 1.5, 1.6" [extras] AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test", "BenchmarkTools", "AbstractAlgebra"] +test = ["Test", "BenchmarkTools", "AbstractAlgebra", "PermutationGroups"] diff --git a/test/AutSigma_41.jl b/test/AutSigma_41.jl index 11c3e6a..f4f92d2 100644 --- a/test/AutSigma_41.jl +++ b/test/AutSigma_41.jl @@ -1,12 +1,21 @@ using PermutationGroups +using Groups.KnuthBendix @testset "Wajnryb presentation for Σ₄₁" begin genus = 4 - G = New.SpecialAutomorphismGroup(New.FreeGroup(2genus)) + G = SpecialAutomorphismGroup(FreeGroup(2genus)) - T = let G = G; (Tas, Tαs, Tes) = New.mcg_twists(genus) + # symplectic pairing goes like this: + # in the free Group: + # f1 ↔ f5 + # f2 ↔ f6 + # f3 ↔ f7 + # f4 ↔ f8 + + T = let G = G + (Tas, Tαs, Tes) = Groups.mcg_twists(G) Ta = G.(Tas) Tα = G.(Tαs) Tes = G.(Tes) @@ -24,24 +33,58 @@ using PermutationGroups a8 = T[8]^-1 # Tα₄ b0 = T[2]^-1 # Ta₂ - a0 = (a1*a2*a3)^4*b0^-1 # from the 3-chain relation - X = a4*a5*a3*a4 # auxillary, not present in the Primer - b1 = X^-1*a0*X + a0 = (a1 * a2 * a3)^4 * b0^-1 # from the 3-chain relation + X = a4 * a5 * a3 * a4 # auxillary, not present in the Primer + b1 = X^-1 * a0 * X b2 = T[10]^-1 # Te₁₃ - As = T[[1,5,9,6,12,7,14,8]] # the inverses of the elements a + As = T[[1, 5, 9, 6, 12, 7, 14, 8]] # the inverses of the elements a + + + @testset "preserving relator" begin + F = Groups.object(G) + + R = prod(commutator(gens(F, i), gens(F, i+genus)) for i in 1:genus) + + ## TODO: how to evaluate automorphisms properly??!!! + + for g in T + w = one(word(g)) + + dg = Groups.domain(g) + gens_idcs = first.(word.(dg)) + img = evaluate(g) + A = alphabet(first(dg)) + + ltrs_map = Vector{eltype(dg)}(undef, length(KnuthBendix.letters(A))) + + for i in 1:length(KnuthBendix.letters(A)) + if i in gens_idcs + ltrs_map[i] = img[findfirst(==(i), gens_idcs)] + else + ltrs_map[i] = inv(img[findfirst(==(inv(A, i)), gens_idcs)]) + end + end + + for l in word(R) + append!(w, word(ltrs_map[l])) + end + + @test F(w) == R + end + end @testset "commutation relations" begin for (i, ai) in enumerate(As) #the element ai here is actually the inverse of ai before. It does not matter for commutativity. Also, a0 is as defined before. for (j, aj) in enumerate(As) - if abs(i-j) > 1 - @test ai*aj == aj*ai + if abs(i - j) > 1 + @test ai * aj == aj * ai elseif i ≠ j - @test ai*aj != aj*ai + @test ai * aj != aj * ai end end if i != 4 - @test a0*ai == ai*a0 + @test a0 * ai == ai * a0 end end end @@ -49,145 +92,186 @@ using PermutationGroups @testset "braid relations" begin for (i, ai) in enumerate(As) #the element ai here is actually the inverse of ai before. It does not matter for braid relations. for (j, aj) in enumerate(As) - if abs(i-j) == 1 - @test ai*aj*ai == aj*ai*aj + if abs(i - j) == 1 + @test ai * aj * ai == aj * ai * aj end end end - @test a0*a4*a0 == a4*a0*a4 # here, a0 and a4 are as before + @test a0 * a4 * a0 == a4 * a0 * a4 # here, a0 and a4 are as before end @testset "Lantern relation" begin @testset "b2 definition" begin - @test b2 == (a2*a3*a1*a2)^-1*b1*(a2*a3*a1*a2) + @test b2 == (a2 * a3 * a1 * a2)^-1 * b1 * (a2 * a3 * a1 * a2) # some additional tests, checking what explicitly happens to the generators of the π₁ under b2 - d = New.domain(b2) - im = New.evaluate(b2) - z = im[7]*d[7]^-1 + d = Groups.domain(b2) + im = evaluate(b2) + z = im[7] * d[7]^-1 @test im[1] == d[1] - @test im[2] == z*d[2]*z^-1 - @test im[3] == z*d[3]*z^-1 + @test im[2] == z * d[2] * z^-1 + @test im[3] == z * d[3] * z^-1 @test im[4] == d[4] - @test im[5] == d[5]*z^-1 - @test im[6] == z*d[6]*z^-1 - @test im[7] == z*d[7] + @test im[5] == d[5] * z^-1 + @test im[6] == z * d[6] * z^-1 + @test im[7] == z * d[7] @test im[8] == d[8] end @testset "b2: commutation relations" begin - @test b2*a1 == a1*b2 - @test b2*a2 != a2*b2 - @test b2*a3 == a3*b2 - @test b2*a4 == a4*b2 - @test b2*a5 == a5*b2 - @test b2*a6 != a6*b2 + @test b2 * a1 == a1 * b2 + @test b2 * a2 != a2 * b2 + @test b2 * a3 == a3 * b2 + @test b2 * a4 == a4 * b2 + @test b2 * a5 == a5 * b2 + @test b2 * a6 != a6 * b2 end @testset "b2: braid relations" begin - @test a2*b2*a2 == b2*a2*b2 - @test a6*b2*a6 == b2*a6*b2 + @test a2 * b2 * a2 == b2 * a2 * b2 + @test a6 * b2 * a6 == b2 * a6 * b2 end @testset "lantern" begin - u = (a6*a5)^-1*b1*(a6*a5) - x = (a6*a5*a4*a3*a2*u*a1^-1*a2^-1*a3^-1*a4^-1) # yet another auxillary + u = (a6 * a5)^-1 * b1 * (a6 * a5) + x = (a6 * a5 * a4 * a3 * a2 * u * a1^-1 * a2^-1 * a3^-1 * a4^-1) # yet another auxillary # x = (a4^-1*a3^-1*a2^-1*a1^-1*u*a2*a3*a4*a5*a6) - @time New.evaluate(x) - b3 = x*a0*x^-1 - @time New.evaluate(b3) - @test a0*b2*b1 == a1*a3*a5*b3 + @time evaluate(x) + b3 = x * a0 * x^-1 + @time evaluate(b3) + @test a0 * b2 * b1 == a1 * a3 * a5 * b3 end end @testset "Te₁₂ definition" begin G = parent(first(T)) - F₈ = New.object(G) - (a, b, c, d, α, β, γ, δ) = gens(F₈) + F₈ = Groups.object(G) + (a, b, c, d, α, β, γ, δ) = Groups.gens(F₈) - A = KnuthBendix.alphabet(G) + A = alphabet(G) - λ = [i == j ? one(G) : G([A[New.λ(i,j)]]) for i in 1:8, j in 1:8] - ϱ = [i == j ? one(G) : G([A[New.ϱ(i,j)]]) for i in 1:8, j in 1:8] + λ = [i == j ? one(G) : G([A[Groups.λ(i, j)]]) for i in 1:8, j in 1:8] + ϱ = [i == j ? one(G) : G([A[Groups.ϱ(i, j)]]) for i in 1:8, j in 1:8] g = one(G) - # @show g - # @show g(Groups.domain(G)) # β ↦ α*β - g *= λ[6,5] - @test New.evaluate(g)[6] == α*β + g *= λ[6, 5] + @test evaluate(g)[6] == α * β # α ↦ a*α*b^-1 - g *= λ[5,1]*inv(ϱ[5,2]) - @test New.evaluate(g)[5] == a*α*b^-1 + g *= λ[5, 1] * inv(ϱ[5, 2]) + @test evaluate(g)[5] == a * α * b^-1 # β ↦ b*α^-1*a^-1*α*β - g *= inv(λ[6,5]) - @test New.evaluate(g)[6] == b*α^-1*a^-1*α*β + g *= inv(λ[6, 5]) + @test evaluate(g)[6] == b * α^-1 * a^-1 * α * β # b ↦ α - g *= λ[2,5]*inv(λ[2,1]); - @test New.evaluate(g)[2] == α + g *= λ[2, 5] * inv(λ[2, 1]) + @test evaluate(g)[2] == α # b ↦ b*α^-1*a^-1*α - g *= inv(λ[2,5]); - @test New.evaluate(g)[2] == b*α^-1*a^-1*α + g *= inv(λ[2, 5]) + @test evaluate(g)[2] == b * α^-1 * a^-1 * α # b ↦ b*α^-1*a^-1*α*b*α^-1 - g *= inv(ϱ[2,5])*ϱ[2,1]; - @test New.evaluate(g)[2] == b*α^-1*a^-1*α*b*α^-1 + g *= inv(ϱ[2, 5]) * ϱ[2, 1] + @test evaluate(g)[2] == b * α^-1 * a^-1 * α * b * α^-1 # b ↦ b*α^-1*a^-1*α*b*α^-1*a*α*b^-1 - g *= ϱ[2,5]; - @test New.evaluate(g)[2] == b*α^-1*a^-1*α*b*α^-1*a*α*b^-1 + g *= ϱ[2, 5] + @test evaluate(g)[2] == b * α^-1 * a^-1 * α * b * α^-1 * a * α * b^-1 - x = b*α^-1*a^-1*α - @test New.evaluate(g) == # (a, b, c, d, α, β, γ, δ) - (a, x*b*x^-1, c, d, α*x^-1, x*β, γ, δ) + x = b * α^-1 * a^-1 * α + @test evaluate(g) == + (a, x * b * x^-1, c, d, α * x^-1, x * β, γ, δ) + # (a, b, c, d, α, β, γ, δ) @test g == T[9] end - Base.conj(t::New.Transvection, p::Perm) = - New.Transvection(t.id, t.i^p, t.j^p, t.inv) + Base.conj(t::Groups.Transvection, p::Perm) = + Groups.Transvection(t.id, t.i^p, t.j^p, t.inv) - function Base.conj(elt::New.FPGroupElement, p::Perm) + function Base.conj(elt::FPGroupElement, p::Perm) G = parent(elt) - A = New.alphabet(elt) - return G([A[conj(A[idx], p)] for idx in New.word(elt)]) + A = alphabet(elt) + return G([A[conj(A[idx], p)] for idx in word(elt)]) end @testset "Te₂₃ definition" begin Te₁₂, Te₂₃ = T[9], T[12] G = parent(Te₁₂) - F₈ = New.object(G) - (a, b, c, d, α, β, γ, δ) = gens(F₈) + F₈ = Groups.object(G) + (a, b, c, d, α, β, γ, δ) = Groups.gens(F₈) - img_Te₂₃ = New.evaluate(Te₂₃) - y = c*β^-1*b^-1*β - @test img_Te₂₃ == (a, b, y*c*y^-1, d, α, β*y^-1, y*γ, δ) + img_Te₂₃ = evaluate(Te₂₃) + y = c * β^-1 * b^-1 * β + @test img_Te₂₃ == (a, b, y * c * y^-1, d, α, β * y^-1, y * γ, δ) σ = perm"(1,2,3)(5,6,7)(8)" Te₂₃_σ = conj(Te₁₂, σ) - # @test New.word(Te₂₃_σ) == New.word(Te₂₃) + # @test word(Te₂₃_σ) == word(Te₂₃) - @test New.evaluate(Te₂₃_σ) == New.evaluate(Te₂₃) + @test evaluate(Te₂₃_σ) == evaluate(Te₂₃) @test Te₂₃ == Te₂₃_σ end @testset "Te₃₄ definition" begin Te₁₂, Te₃₄ = T[9], T[14] G = parent(Te₁₂) - F₈ = New.object(G) + F₈ = Groups.object(G) (a, b, c, d, α, β, γ, δ) = Groups.gens(F₈) σ = perm"(1,3)(2,4)(5,7)(6,8)" Te₃₄_σ = conj(Te₁₂, σ) @test Te₃₄ == Te₃₄_σ end + + @testset "hyperelliptic τ is central" begin + + A = alphabet(G) + λ = Groups.ΡΛ(:λ, A, 2genus) + ϱ = Groups.ΡΛ(:ϱ, A, 2genus) + + import Groups: Ta, Tα, Te + + halftwists = map(1:genus-1) do i + j = i + 1 + x = Ta(λ, j) * inv(A, Ta(λ, i)) * Tα(λ, j) * Te(λ, ϱ, i, j) + δ = x * Tα(λ, i) * inv(A, x) + c = + inv(A, Ta(λ, j)) * + Te(λ, ϱ, i, j) * + Tα(λ, i)^2 * + inv(A, δ) * + inv(A, Ta(λ, j)) * + Ta(λ, i) * + δ + z = + Te(λ, ϱ, j, i) * + inv(A, Ta(λ, i)) * + Tα(λ, i) * + Ta(λ, i) * + inv(A, Te(λ, ϱ, j, i)) + + G(Ta(λ, i) * inv(A, Ta(λ, j) * Tα(λ, j))^6 * (Ta(λ, j) * Tα(λ, j) * z)^4 * c) + end + + τ = (G(Ta(λ, 1) * Tα(λ, 1))^6) * prod(halftwists, init = one(G)) + + # τ^genus is trivial but only in autπ₁Σ₄ + # here we check its centrality + + + + + τᵍ = τ^genus + @test_broken all(a * τᵍ == τᵍ * a for a in Groups.gens(G)) + end end diff --git a/test/runtests.jl b/test/runtests.jl index e69bce8..2e41ab8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -26,6 +26,7 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl")) include("fp_groups.jl") include("AutFn.jl") + include("AutSigma_41.jl") # if !haskey(ENV, "CI") # include("benchmarks.jl") From 0c60dce7319d89514e052423652c58a4617f7cbc Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 7 Jul 2021 12:11:45 +0200 Subject: [PATCH 18/37] fix test_throws --- test/fp_groups.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/fp_groups.jl b/test/fp_groups.jl index 89d83b5..4e4f624 100644 --- a/test/fp_groups.jl +++ b/test/fp_groups.jl @@ -46,7 +46,10 @@ @test h isa FPGroupElement @test_throws AssertionError h == g - @test_throws AssertionError h*g + @test_throws MethodError h*g + + H′ = FPGroup(G, [aG^2=>cG, bG*cG=>aG], maxrules=200) + @test_throws AssertionError one(H) == one(H′) Groups.normalform!(h) @test h == H([5]) From a742bf32ec5a7479030436a9faa9a10ff418675f Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Fri, 9 Jul 2021 16:46:15 +0200 Subject: [PATCH 19/37] add code for forward evaluation --- src/autgroups.jl | 58 +++++++++++++++++++++++++++++++++++++++++++ test/AutFn.jl | 64 +++++++++++++++++++++++------------------------- 2 files changed, 88 insertions(+), 34 deletions(-) diff --git a/src/autgroups.jl b/src/autgroups.jl index 0361116..53db353 100644 --- a/src/autgroups.jl +++ b/src/autgroups.jl @@ -115,3 +115,61 @@ function evaluate!( end evaluate!(t::NTuple{N, T}, s::GSymbol, A, tmp=one(first(t))) where {N, T} = throw("you need to implement `evaluate!(::$(typeof(t)), ::$(typeof(s)), ::Alphabet, tmp=one(first(t)))`") + +# forward evaluate by substitution + +struct LettersMap{T, A} + indices_map::Dict{Int, T} + A::A +end + +function LettersMap(a::FPGroupElement{<:AutomorphismGroup}) + dom = domain(a) + @assert all(isone ∘ length ∘ word, dom) + A = alphabet(first(dom)) + first_letters = first.(word.(dom)) + img = evaluate!(dom, a) + + # (dom[i] → img[i] is a map from domain to images) + # we need a map from alphabet indices → (gens, gens⁻¹) → images + # here we do it for elements of the domai + # (trusting it's a set of generators that define a) + @assert length(dom) == length(img) + + indices_map = Dict(A[A[fl]] => word(im) for (fl, im) in zip(first_letters, img)) + # inverses of generators are dealt lazily in getindex + + return LettersMap(indices_map, A) +end + + +function Base.getindex(lm::LettersMap, i::Integer) + # here i is an index of an alphabet + @boundscheck 1 ≤ i ≤ length(KnuthBendix.letters(lm.A)) + + if !haskey(lm.indices_map, i) + img = if haskey(lm.indices_map, inv(lm.A, i)) + inv(lm.A, lm.indices_map[inv(lm.A, i)]) + else + @warn "LetterMap: neither $i nor its inverse has assigned value" + one(valtype(lm.indices_map)) + end + lm.indices_map[i] = img + end + return lm.indices_map[i] +end + +function (a::FPGroupElement{<:AutomorphismGroup})(g::FPGroupElement) + @assert object(parent(a)) === parent(g) + img_w = evaluate(word(g), LettersMap(a)) + return parent(g)(img_w) +end + +evaluate(w::AbstractWord, lm::LettersMap) = evaluate!(one(w), w, lm) + +function evaluate!(res::AbstractWord, w::AbstractWord, lm::LettersMap) + for i in w + append!(res, lm[i]) + end + return res +end diff --git a/test/AutFn.jl b/test/AutFn.jl index a33dc65..a269163 100644 --- a/test/AutFn.jl +++ b/test/AutFn.jl @@ -144,6 +144,36 @@ @test all(g->isone(g*inv(g)), B_2) end + @testset "Forward evaluate" begin + N = 3 + F = FreeGroup(N) + G = SpecialAutomorphismGroup(F) + + a = gens(G, 1) # ϱ₁₂ + + f = gens(F) + + @test a(f[1]) == f[1]*f[2] + @test all(a(f[i]) == f[i] for i in 2:length(f)) + + S = let s = gens(G) + [s; inv.(s)] + end + + @test all( + map(first(Groups.wlmetric_ball(S, radius=2))) do g + lm = Groups.LettersMap(g) + img = evaluate(g) + + fimg = [F(lm[first(word(s))]) for s in gens(F)] + + succeeded = all(img .== fimg) + @assert succeeded "forward evaluation of $(word(g)) failed: \n img=$img\n fimg=$(tuple(fimg...))" + succeeded + end + ) + end + @testset "GroupsCore conformance" begin test_Group_interface(A) g = A(rand(1:length(alphabet(A)), 10)) @@ -153,37 +183,3 @@ end end - -# using Random -# using GroupsCore -# -# A = New.SpecialAutomorphismGroup(FreeGroup(4), maxrules=2000, ordering=KnuthBendix.RecursivePathOrder) -# -# # for seed in 1:1000 -# let seed = 68 -# N = 14 -# Random.seed!(seed) -# g = A(rand(1:length(KnuthBendix.alphabet(A)), N)) -# h = A(rand(1:length(KnuthBendix.alphabet(A)), N)) -# @info "seed=$seed" g h -# @time isone(g*inv(g)) -# @time isone(inv(g)*g) -# @info "" length(word(New.normalform!(g*inv(g)))) length(word(New.normalform!(inv(g)*g))) -# a = commutator(g, h, g) -# b = conj(inv(g), h) * conj(conj(g, h), g) -# -# @info length(word(a)) -# @info length(word(b)) -# -# w = a*inv(b) -# @info length(word(w)) -# New.normalform!(w) -# @info length(word(w)) -# -# -# # -# # @time ima = evaluate(a) -# # @time imb = evaluate(b) -# # @info "" a b ima imb -# # @time a == b -# end From a258c563d1eb42484ff49d5558747c3bf51aa535 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Mon, 12 Jul 2021 11:11:39 +0200 Subject: [PATCH 20/37] make smc acting on the right --- src/groups/symplectic_twists.jl | 59 +++++++++++---------------------- test/AutSigma_41.jl | 6 +--- 2 files changed, 20 insertions(+), 45 deletions(-) diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index 74cd8ba..bef6f3e 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -97,17 +97,21 @@ function mcg_twists(G::AutomorphismGroup{<:FreeGroup}) return Tas, Tαs, Tes end -struct SymplecticMappingClass{N,T} <: GSymbol +struct SymplecticMappingClass{T, F} <: GSymbol id::Symbol # :A, :B i::UInt j::UInt minus::Bool inv::Bool - images::NTuple{N,T} - invimages::NTuple{N,T} - gens_idcs::Dict{Int, Int} + autFn_word::T + perm::Vector{Int} + f::F end +Base.:(==)(a::SymplecticMappingClass, b::SymplecticMappingClass) = a.autFn_word == b.autFn_word + +Base.hash(a::SymplecticMappingClass, h::UInt) = hash(a.autFn_word, h) + function SymplecticMappingClass( Σ::SurfaceGroup, sautFn, @@ -153,23 +157,13 @@ function SymplecticMappingClass( throw("Type not recognized: $id") end - g = sautFn(w) + a = sautFn(w) + g = genus(Σ) + perm = [2g:-2:1; (2g-1):-2:1] - perm = let g = genus(Σ) - [reverse(1+1:2:2g); reverse(1:2:2g)] - end + f(t) = evaluate!(t, a) - d = ntuple(i->gens(Σ, i), ngens(Σ))[perm] - - - img = evaluate!(deepcopy(d), g)[invperm(perm)] - invim = evaluate!(d, inv(g))[invperm(perm)] - - img, invim = inverse ? (invim, img) : (img, invim) - - gens_idcs = Dict(alphabet(Σ)[Σ.gens[i]] => i for i in 1:ngens(Σ)) - - res = SymplecticMappingClass(id, UInt(i), UInt(j), minus, inverse, img, invim, gens_idcs) + res = SymplecticMappingClass(id, UInt(i), UInt(j), minus, inverse, a, perm, f) return res end @@ -185,7 +179,9 @@ function Base.show(io::IO, smc::SymplecticMappingClass) end function Base.inv(m::SymplecticMappingClass) - return SymplecticMappingClass(m.id, m.i, m.j, m.minus, !m.inv, m.invimages, m.images, m.gens_idcs) + inv_w = inv(m.autFn_word) + f(t) = evaluate!(t, inv_w) + return SymplecticMappingClass(m.id, m.i, m.j, m.minus, !m.inv, inv_w, m.perm, f) end function evaluate!( @@ -194,27 +190,10 @@ function evaluate!( A::Alphabet, tmp = one(first(t)), ) where {N,T} - img = smc.images - - for elt in t - copyto!(tmp, elt) - resize!(word(elt), 0) - for idx in word(tmp) - # @show idx - k = if haskey(smc.gens_idcs, idx) - img[smc.gens_idcs[idx]] - else - inv(img[smc.gens_idcs[inv(A, idx)]]) - end - append!(word(elt), word(k)) - end - _setnormalform!(elt, false) - _setvalidhash!(elt, false) - - normalform!(tmp, elt) - copyto!(elt, tmp) - end + t = smc.f(t[smc.perm])[invperm(smc.perm)] + # t = evaluate!(t[smc.perm], smc.autFn_word, tmp) + # t = t[invperm(smc.perm)] return t end diff --git a/test/AutSigma_41.jl b/test/AutSigma_41.jl index f4f92d2..d49b807 100644 --- a/test/AutSigma_41.jl +++ b/test/AutSigma_41.jl @@ -7,8 +7,7 @@ using Groups.KnuthBendix G = SpecialAutomorphismGroup(FreeGroup(2genus)) - # symplectic pairing goes like this: - # in the free Group: + # symplectic pairing in the free Group goes like this: # f1 ↔ f5 # f2 ↔ f6 # f3 ↔ f7 @@ -268,9 +267,6 @@ using Groups.KnuthBendix # τ^genus is trivial but only in autπ₁Σ₄ # here we check its centrality - - - τᵍ = τ^genus @test_broken all(a * τᵍ == τᵍ * a for a in Groups.gens(G)) end From 27e768639d4c74ac95ab530f63559c8a848e2960 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 17 Jul 2021 20:11:32 +0200 Subject: [PATCH 21/37] first try at compiled automorphisms --- src/autgroups.jl | 61 +++++++++++++++++++++++++++++++++ src/groups/symplectic_twists.jl | 8 ++--- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/autgroups.jl b/src/autgroups.jl index 53db353..7c30686 100644 --- a/src/autgroups.jl +++ b/src/autgroups.jl @@ -173,3 +173,64 @@ function evaluate!(res::AbstractWord, w::AbstractWord, lm::LettersMap) end return res end + +# compile automorphisms + +compiled(a) = eval(generated_evaluate(a)) + +function generated_evaluate(a::FPGroupElement{<:AutomorphismGroup}) + lm = Groups.LettersMap(a) + d = Groups.domain(a) + @assert all(length.(word.(d)) .== 1) + A = alphabet(first(d)) + first_ltrs = first.(word.(d)) + + args = [Expr(:call, :*) for _ in first_ltrs] + + for (idx, letter) in enumerate(first_ltrs) + for l in lm[letter] + k = findfirst(==(l), first_ltrs) + if k !== nothing + push!(args[idx].args, :(d[$k])) + continue + end + k = findfirst(==(inv(A, l)), first_ltrs) + if k !== nothing + push!(args[idx].args, :(inv(d[$k]))) + continue + end + throw("Letter $l doesn't seem to be mapped anywhere!") + end + end + locals = Dict{Expr, Symbol}() + locals_counter = 0 + for (i,v) in enumerate(args) + @assert length(v.args) >= 2 + if length(v.args) > 2 + for (j, a) in pairs(v.args) + if a isa Expr && a.head == :call "$a" + @assert a.args[1] == :inv + if !(a in keys(locals)) + locals[a] = Symbol("var_#$locals_counter") + locals_counter += 1 + end + v.args[j] = locals[a] + end + end + else + args[i] = v.args[2] + end + end + + q = quote + $([:(local $v = $k) for (k,v) in locals]...) + end + + # return args, locals + + return :(d -> begin + @boundscheck @assert length(d) == $(length(d)) + $q + @inbounds tuple($(args...)) + end) +end diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index bef6f3e..d9f5929 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -161,7 +161,8 @@ function SymplecticMappingClass( g = genus(Σ) perm = [2g:-2:1; (2g-1):-2:1] - f(t) = evaluate!(t, a) + f = compiled(a) + # f = t -> evaluate!(t, a) res = SymplecticMappingClass(id, UInt(i), UInt(j), minus, inverse, a, perm, f) @@ -180,7 +181,8 @@ end function Base.inv(m::SymplecticMappingClass) inv_w = inv(m.autFn_word) - f(t) = evaluate!(t, inv_w) + # f(t) = evaluate!(t, inv_w) + f = compiled(inv_w) return SymplecticMappingClass(m.id, m.i, m.j, m.minus, !m.inv, inv_w, m.perm, f) end @@ -192,8 +194,6 @@ function evaluate!( ) where {N,T} t = smc.f(t[smc.perm])[invperm(smc.perm)] - # t = evaluate!(t[smc.perm], smc.autFn_word, tmp) - # t = t[invperm(smc.perm)] return t end From bcce338754e5ce8935a2d81147fb06725b31ea16 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 17 Jul 2021 20:23:41 +0200 Subject: [PATCH 22/37] refactor wlmetric_ball --- src/wl_ball.jl | 52 +++++++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/src/wl_ball.jl b/src/wl_ball.jl index f7f4e12..ebb370c 100644 --- a/src/wl_ball.jl +++ b/src/wl_ball.jl @@ -6,50 +6,34 @@ word-length metric on the group generated by `S`. The ball is centered at `cente (by default: the identity element). `radius` and `op` keywords specify the radius and multiplication operation to be used. """ -function wlmetric_ball_serial(S::AbstractVector{T}; radius = 2, op = *) where {T} - @assert radius > 0 - old = unique!([one(first(S)), S...]) - sizes = [1, length(old)] - for i in 2:radius - new = collect(op(o, s) for o in @view(old[sizes[end-1]:end]) for s in S) - append!(old, new) - resize!(new, 0) - old = unique!(old) - push!(sizes, length(old)) - end - return old, sizes[2:end] +function wlmetric_ball_serial(S::AbstractVector{T}, center::T=one(first(S)); radius = 2, op = *) where {T} + @assert radius >= 1 + old = unique!([center, [center*s for s in S]...]) + return _wlmetric_ball(S, old, radius, op, collect, unique!) end -function wlmetric_ball_thr(S::AbstractVector{T}; radius = 2, op = *) where {T} - @assert radius > 0 - old = unique!([one(first(S)), S...]) +function wlmetric_ball_thr(S::AbstractVector{T}, center::T=one(first(S)); radius = 2, op = *) where {T} + @assert radius >= 1 + old = unique!([center, [center*s for s in S]...]) + return _wlmetric_ball(S, old, radius, op, ThreadsX.collect, ThreadsX.unique) +end + +function _wlmetric_ball(S, old, radius, op, collect, unique) sizes = [1, length(old)] for r in 2:radius - begin - new = - ThreadsX.collect(op(o, s) for o in @view(old[sizes[end-1]:end]) for s in S) - ThreadsX.foreach(hash, new) + old = let old = old, S = S, + new = collect( + (g = op(o, s); hash(g); g) + for o in @view(old[sizes[end-1]:end]) for s in S + ) + append!(old, new) + unique(old) end - append!(old, new) - resize!(new, 0) - old = ThreadsX.unique(old) push!(sizes, length(old)) end return old, sizes[2:end] end -function wlmetric_ball_serial(S::AbstractVector{T}, center::T; radius = 2, op = *) where {T} - E, sizes = wlmetric_ball_serial(S, radius = radius, op = op) - isone(center) && return E, sizes - return c .* E, sizes -end - -function wlmetric_ball_thr(S::AbstractVector{T}, center::T; radius = 2, op = *) where {T} - E, sizes = wlmetric_ball_thr(S, radius = radius, op = op) - isone(center) && return E, sizes - return c .* E, sizes -end - function wlmetric_ball( S::AbstractVector{T}, center::T = one(first(S)); From ed1e3f9709bcb777291262899ad4ecd35a035f8a Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sun, 18 Jul 2021 18:36:07 +0200 Subject: [PATCH 23/37] store invperm in SMC to avoid allocation in evaluate --- src/groups/symplectic_twists.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index d9f5929..306f6c1 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -105,6 +105,7 @@ struct SymplecticMappingClass{T, F} <: GSymbol inv::Bool autFn_word::T perm::Vector{Int} + invperm::Vector{Int} f::F end @@ -164,7 +165,7 @@ function SymplecticMappingClass( f = compiled(a) # f = t -> evaluate!(t, a) - res = SymplecticMappingClass(id, UInt(i), UInt(j), minus, inverse, a, perm, f) + res = SymplecticMappingClass(id, UInt(i), UInt(j), minus, inverse, a, perm, invperm(perm), f) return res end @@ -183,7 +184,7 @@ function Base.inv(m::SymplecticMappingClass) inv_w = inv(m.autFn_word) # f(t) = evaluate!(t, inv_w) f = compiled(inv_w) - return SymplecticMappingClass(m.id, m.i, m.j, m.minus, !m.inv, inv_w, m.perm, f) + return SymplecticMappingClass(m.id, m.i, m.j, m.minus, !m.inv, inv_w, m.perm, m.invperm, f) end function evaluate!( @@ -193,7 +194,7 @@ function evaluate!( tmp = one(first(t)), ) where {N,T} - t = smc.f(t[smc.perm])[invperm(smc.perm)] + t = smc.f(t[smc.perm])[smc.invperm] return t end From ef4470833a83048aceb2564282d97cee1683abdc Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sun, 18 Jul 2021 19:55:11 +0200 Subject: [PATCH 24/37] remove init kwarg from prod (unsupported on julia-1.3) --- test/AutSigma_41.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/AutSigma_41.jl b/test/AutSigma_41.jl index d49b807..bf54b43 100644 --- a/test/AutSigma_41.jl +++ b/test/AutSigma_41.jl @@ -262,7 +262,7 @@ using Groups.KnuthBendix G(Ta(λ, i) * inv(A, Ta(λ, j) * Tα(λ, j))^6 * (Ta(λ, j) * Tα(λ, j) * z)^4 * c) end - τ = (G(Ta(λ, 1) * Tα(λ, 1))^6) * prod(halftwists, init = one(G)) + τ = (G(Ta(λ, 1) * Tα(λ, 1))^6) * prod(halftwists) # τ^genus is trivial but only in autπ₁Σ₄ # here we check its centrality From 3235b009598f4c41d5a47bb3d0f1db3332b9bc4a Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 20 Jul 2021 10:16:59 +0200 Subject: [PATCH 25/37] =?UTF-8?q?fix=20test=20of=20the=20centrality=20of?= =?UTF-8?q?=20hyperelliptic=20=CF=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/AutSigma_41.jl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/AutSigma_41.jl b/test/AutSigma_41.jl index bf54b43..13ad464 100644 --- a/test/AutSigma_41.jl +++ b/test/AutSigma_41.jl @@ -99,6 +99,11 @@ using Groups.KnuthBendix @test a0 * a4 * a0 == a4 * a0 * a4 # here, a0 and a4 are as before end + @testset "3-chain relation" begin + x = a4*a3*a2*a1*a1*a2*a3*a4 # auxillary; does not have a name in the Primer + @test b0 == x*a0*x^-1 + end + @testset "Lantern relation" begin @testset "b2 definition" begin @@ -268,6 +273,14 @@ using Groups.KnuthBendix # here we check its centrality τᵍ = τ^genus - @test_broken all(a * τᵍ == τᵍ * a for a in Groups.gens(G)) + + symplectic_gens = let genus = genus, G = G + π₁Σ = Groups.SurfaceGroup(genus, 0) + autπ₁Σ = AutomorphismGroup(π₁Σ) + letters = alphabet(autπ₁Σ).letters + G.(word(l.autFn_word) for l in letters) + end + + @test all(sg * τᵍ == τᵍ * sg for sg in symplectic_gens) end end From 6b354d449b340ea8786e6bbe4cd4f176380d27a7 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 20 Jul 2021 10:39:21 +0200 Subject: [PATCH 26/37] remove new_types.jl again --- src/new_types.jl | 206 ----------------------------------------------- 1 file changed, 206 deletions(-) delete mode 100644 src/new_types.jl diff --git a/src/new_types.jl b/src/new_types.jl deleted file mode 100644 index 0412cad..0000000 --- a/src/new_types.jl +++ /dev/null @@ -1,206 +0,0 @@ -using GroupsCore -# using Groups -# import Groups.AbstractFPGroup -import KnuthBendix -import KnuthBendix: AbstractWord, Alphabet, Word, RewritingSystem -import KnuthBendix: alphabet -using Random - -## "Abstract" definitions - -""" - AbstractFPGroup - -An Abstract type representing finitely presented groups. Every instance `` must implement - * `KnuthBendix.alphabet(G::MyFPGroup)` - * `rewriting(G::MyFPGroup)` : return the rewriting object which must implement - > `KnuthBendix.rewrite_from_left!(u, v, rewriting(G))`. -By default `alphabet(G)` is returned, which amounts to free rewriting in `G`. - * `relations(G::MyFPGroup)` : return a set of defining relations. - -AbstractFPGroup may also override `word_type(::Type{MyFPGroup}) = Word{UInt16}`, -which controls the word type used for group elements. If a group has more than `255` generators you need to define e.g. -> `word_type(::Type{MyFPGroup}) = Word{UInt16}` -""" -abstract type AbstractFPGroup <: GroupsCore.Group end - -word_type(G::AbstractFPGroup) = word_type(typeof(G)) -# the default: -word_type(::Type{<:AbstractFPGroup}) = Word{UInt8} - -# the default (results in free rewriting) -rewriting(G::AbstractFPGroup) = alphabet(G) - -Base.@propagate_inbounds function (G::AbstractFPGroup)(word::AbstractVector{<:Integer}) - @boundscheck @assert all(l -> 1 <= l <= length(KnuthBendix.alphabet(G)), word) - return FPGroupElement(word_type(G)(word), G) -end - -function Base.show(io::IO, G::AbstractFPGroup) - print(io, "⟨") - join(io, gens(G), ", ") - print(io, " | ") - join(io, relations(G), ", ") - print(io, "⟩") -end - -## Group Interface - -Base.one(G::AbstractFPGroup) = FPGroupElement(one(word_type(G)), G) - -Base.eltype(::Type{FPG}) where {FPG<:AbstractFPGroup} = FPGroupElement{FPG,word_type(FPG)} - -include("iteration.jl") - -GroupsCore.ngens(G::AbstractFPGroup) = length(G.gens) - -function GroupsCore.gens(G::AbstractFPGroup, i::Integer) - @boundscheck 1 <= i <= GroupsCore.ngens(G) - l = alphabet(G)[G.gens[i]] - return FPGroupElement(word_type(G)([l]), G) -end -GroupsCore.gens(G::AbstractFPGroup) = [gens(G, i) for i in 1:GroupsCore.ngens(G)] - -# TODO: ProductReplacementAlgorithm -function Base.rand(rng::Random.AbstractRNG, rs::Random.SamplerTrivial{<:AbstractFPGroup}) - l = rand(10:100) - G = rs[] - nletters = length(alphabet(G)) - return FPGroupElement(word_type(G)(rand(1:nletters, l)), G) -end - -Base.isfinite(::AbstractFPGroup) = (@warn "using generic isfinite(::AbstractFPGroup): the returned `false` might be wrong"; false) - -## FPGroupElement - -mutable struct FPGroupElement{G<:AbstractFPGroup,W<:AbstractWord} <: GroupElement - word::W - savedhash::UInt - parent::G - - FPGroupElement(word::W, G::AbstractFPGroup) where {W<:AbstractWord} = - new{typeof(G),W}(word, UInt(0), G) - - FPGroupElement(word::W, hash::UInt, G::AbstractFPGroup) where {W<:AbstractWord} = - new{typeof(G),W}(word, hash, G) -end - -word(f::FPGroupElement) = f.word - -#convenience -KnuthBendix.alphabet(g::FPGroupElement) = alphabet(parent(g)) - -function Base.show(io::IO, f::FPGroupElement) - f = normalform!(f) - KnuthBendix.print_repr(io, word(f), alphabet(f)) -end - -## GroupElement Interface for FPGroupElement - -Base.parent(f::FPGroupElement) = f.parent -GroupsCore.parent_type(::Type{<:FPGroupElement{G}}) where {G} = G - -function Base.:(==)(g::FPGroupElement, h::FPGroupElement) - @boundscheck @assert parent(g) === parent(h) - normalform!(g) - normalform!(h) - hash(g) != hash(h) && return false - return word(g) == word(h) -end - -function Base.deepcopy_internal(g::FPGroupElement, stackdict::IdDict) - return FPGroupElement(copy(word(g)), g.savedhash, parent(g)) -end - -Base.inv(g::FPGroupElement) = (G = parent(g); FPGroupElement(inv(alphabet(G), word(g)), G)) - -function Base.:(*)(g::FPGroupElement, h::FPGroupElement) - @boundscheck @assert parent(g) === parent(h) - return FPGroupElement(word(g) * word(h), parent(g)) -end - -GroupsCore.isfiniteorder(g::FPGroupElement) = isone(g) ? true : (@warn "using generic isfiniteorder(::FPGroupElement): the returned `false` might be wrong"; false) - -# additional methods: -Base.isone(g::FPGroupElement) = (normalform!(g); isempty(word(g))) - -## Free Groups - -struct FreeGroup{T} <: AbstractFPGroup - gens::Vector{T} - alphabet::KnuthBendix.Alphabet{T} - - function FreeGroup(gens, A::KnuthBendix.Alphabet) where {W} - @assert length(gens) == length(unique(gens)) - @assert all(l -> l in KnuthBendix.letters(A), gens) - return new{eltype(gens)}(gens, A) - end -end - -function FreeGroup(A::Alphabet) - @boundscheck @assert all(KnuthBendix.hasinverse(l, A) for l in KnuthBendix.letters(A)) - return FreeGroup(KnuthBendix.letters(A), A) -end - -function FreeGroup(n::Integer) - symbols = Symbol[] - inverses = Int[] - sizehint!(symbols, 2n) - sizehint!(inverses, 2n) - for i in 1:n - push!(symbols, Symbol(:f, subscriptify(i)), Symbol(:F, subscriptify(i))) - push!(inverses, 2i, 2i-1) - end - return FreeGroup(symbols[1:2:2n], Alphabet(symbols, inverses)) -end - -Base.show(io::IO, F::FreeGroup) = print(io, "free group on $(ngens(F)) generators") - -# mandatory methods: -KnuthBendix.alphabet(F::FreeGroup) = F.alphabet -relations(F::FreeGroup) = Pair{eltype(F)}[] - -# GroupsCore interface: -# these are mathematically correct -Base.isfinite(::FreeGroup) = false - -GroupsCore.isfiniteorder(g::FPGroupElement{<:FreeGroup}) = isone(g) ? true : false - -## FP Groups - -struct FPGroup{T,R,S} <: AbstractFPGroup - gens::Vector{T} - relations::Vector{Pair{S,S}} - rws::R -end - -KnuthBendix.alphabet(G::FPGroup) = alphabet(rewriting(G)) -rewriting(G::FPGroup) = G.rws - -relations(G::FPGroup) = G.relations - -function FPGroup( - G::AbstractFPGroup, - rels::AbstractVector{<:Pair{GEl,GEl}}; - ordering = KnuthBendix.LenLex, - kwargs..., -) where {GEl<:FPGroupElement} - - O = ordering(alphabet(G)) - for (lhs, rhs) in rels - @assert parent(lhs) === parent(rhs) === G - end - word_rels = [word(lhs) => word(rhs) for (lhs, rhs) in [relations(G); rels]] - rws = RewritingSystem(word_rels, O) - - KnuthBendix.knuthbendix!(rws; kwargs...) - - return FPGroup(G.gens, rels, rws) -end - -## GSymbol aka letter of alphabet - -abstract type GSymbol end -Base.literal_pow(::typeof(^), t::GSymbol, ::Val{-1}) = inv(t) - -subscriptify(i::Integer) = join('₀'+d for d in reverse(digits(i))) From 596744e5b87a7559e7b1639981046c681276328c Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 20 Jul 2021 11:02:12 +0200 Subject: [PATCH 27/37] use the forward evaluation to test preserving of the relator --- test/AutSigma_41.jl | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/test/AutSigma_41.jl b/test/AutSigma_41.jl index 13ad464..8423152 100644 --- a/test/AutSigma_41.jl +++ b/test/AutSigma_41.jl @@ -44,32 +44,9 @@ using Groups.KnuthBendix F = Groups.object(G) R = prod(commutator(gens(F, i), gens(F, i+genus)) for i in 1:genus) - - ## TODO: how to evaluate automorphisms properly??!!! - + for g in T - w = one(word(g)) - - dg = Groups.domain(g) - gens_idcs = first.(word.(dg)) - img = evaluate(g) - A = alphabet(first(dg)) - - ltrs_map = Vector{eltype(dg)}(undef, length(KnuthBendix.letters(A))) - - for i in 1:length(KnuthBendix.letters(A)) - if i in gens_idcs - ltrs_map[i] = img[findfirst(==(i), gens_idcs)] - else - ltrs_map[i] = inv(img[findfirst(==(inv(A, i)), gens_idcs)]) - end - end - - for l in word(R) - append!(w, word(ltrs_map[l])) - end - - @test F(w) == R + @test g(R) == R end end From 156e4a2af3b42b929db1cc4da745cffe99367d86 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 21 Jul 2021 16:26:22 +0200 Subject: [PATCH 28/37] renumerate Dehn Twists --- src/groups/symplectic_twists.jl | 31 +++++++----- test/AutSigma_41.jl | 88 ++++++++------------------------- 2 files changed, 39 insertions(+), 80 deletions(-) diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index 306f6c1..6951e19 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -13,7 +13,7 @@ function Base.getindex(rl::ΡΛ, i::Integer, j::Integer) rl.id == :ϱ && return Word([rl.A[ϱ(i, j)]]) end -function Te_diagonal(λ::ΡΛ, ϱ::ΡΛ, i::Integer) +function Te_diagonal(λ::Groups.ΡΛ, ϱ::Groups.ΡΛ, i::Integer) @assert λ.N == ϱ.N @assert λ.id == :λ && ϱ.id == :ϱ @@ -25,15 +25,20 @@ function Te_diagonal(λ::ΡΛ, ϱ::ΡΛ, i::Integer) A = λ.A - # comments are for i,j = 1,2 + NI = (2n - 2i) + 1 + NJ = (2n - 2j) + 1 + I = (2n - 2i) + 2 + J = (2n - 2j) + 2 + g = one(Word(Int[])) - g *= λ[n+j, n+i] # β ↦ α*β - g *= λ[n+i, i] * inv(A, ϱ[n+i, j]) # α ↦ a*α*b^-1 - g *= inv(A, λ[n+j, n+i]) # β ↦ b*α^-1*a^-1*α*β - g *= λ[j, n+i] * inv(A, λ[j, i]) # b ↦ α - g *= inv(A, λ[j, n+i]) # b ↦ b*α^-1*a^-1*α - g *= inv(A, ϱ[j, n+i]) * ϱ[j, i] # b ↦ b*α^-1*a^-1*α*b*α^-1 - g *= ϱ[j, n+i] # b ↦ b*α^-1*a^-1*α*b*α^-1*a*α*b^-1 + g *= λ[NJ, NI] # β ↦ α*β + g *= λ[NI, I] * inv(A, ϱ[NI, J]) # α ↦ a*α*b^-1 + g *= inv(A, λ[NJ, NI]) # β ↦ b*α^-1*a^-1*α*β + g *= λ[J, NI] * inv(A, λ[J, I]) # b ↦ α + g *= inv(A, λ[J, NI]) # b ↦ b*α^-1*a^-1*α + g *= inv(A, ϱ[J, NI]) * ϱ[J, I] # b ↦ b*α^-1*a^-1*α*b*α^-1 + g *= ϱ[J, NI] # b ↦ b*α^-1*a^-1*α*b*α^-1*a*α*b^-1 + return g end @@ -45,10 +50,10 @@ function Te_lantern(A::Alphabet, b₀::T, a₁::T, a₂::T, a₃::T, a₄::T, a return inv(A, Y) * b₁ * Y # b₂ end -Ta(λ::ΡΛ, i::Integer) = (@assert λ.id == :λ; -λ[λ.N÷2+i, i]) -Tα(λ::ΡΛ, i::Integer) = (@assert λ.id == :λ; -inv(λ.A, λ[i, λ.N÷2+i])) +Ta(λ::Groups.ΡΛ, i::Integer) = (@assert λ.id == :λ; λ[λ.N-2i+1, λ.N-2i+2]) + +Tα(λ::Groups.ΡΛ, i::Integer) = (@assert λ.id == :λ; inv(λ.A, λ[λ.N-2i+2, λ.N-2i+1])) + function Te(λ::ΡΛ, ϱ::ΡΛ, i, j) @assert i ≠ j diff --git a/test/AutSigma_41.jl b/test/AutSigma_41.jl index 8423152..16e3a2f 100644 --- a/test/AutSigma_41.jl +++ b/test/AutSigma_41.jl @@ -7,6 +7,8 @@ using Groups.KnuthBendix G = SpecialAutomorphismGroup(FreeGroup(2genus)) + T = Groups.mcg_twists(autF) + # symplectic pairing in the free Group goes like this: # f1 ↔ f5 # f2 ↔ f6 @@ -43,8 +45,8 @@ using Groups.KnuthBendix @testset "preserving relator" begin F = Groups.object(G) - R = prod(commutator(gens(F, i), gens(F, i+genus)) for i in 1:genus) - + R = prod(commutator(gens(F,2i+1), gens(F,2i+2)) for i in 0:genus-1) + for g in T @test g(R) == R end @@ -88,19 +90,17 @@ using Groups.KnuthBendix # some additional tests, checking what explicitly happens to the generators of the π₁ under b2 d = Groups.domain(b2) - im = evaluate(b2) - z = im[7] * d[7]^-1 - - @test im[1] == d[1] - @test im[2] == z * d[2] * z^-1 - @test im[3] == z * d[3] * z^-1 - @test im[4] == d[4] - - @test im[5] == d[5] * z^-1 - @test im[6] == z * d[6] * z^-1 - @test im[7] == z * d[7] - @test im[8] == d[8] + img = evaluate(b2) + z = img[3] * d[3]^-1 + @test img[1] == d[1] + @test img[2] == d[2] + @test img[3] == z * d[3] + @test img[4] == z * d[4] * z^-1 + @test img[5] == z * d[5] * z^-1 + @test img[6] == z * d[6] * z^-1 + @test img[7] == d[7] * z^-1 + @test img[8] == d[8] end @testset "b2: commutation relations" begin @@ -128,54 +128,6 @@ using Groups.KnuthBendix end end - - @testset "Te₁₂ definition" begin - G = parent(first(T)) - F₈ = Groups.object(G) - (a, b, c, d, α, β, γ, δ) = Groups.gens(F₈) - - A = alphabet(G) - - λ = [i == j ? one(G) : G([A[Groups.λ(i, j)]]) for i in 1:8, j in 1:8] - ϱ = [i == j ? one(G) : G([A[Groups.ϱ(i, j)]]) for i in 1:8, j in 1:8] - - g = one(G) - - # β ↦ α*β - g *= λ[6, 5] - @test evaluate(g)[6] == α * β - - # α ↦ a*α*b^-1 - g *= λ[5, 1] * inv(ϱ[5, 2]) - @test evaluate(g)[5] == a * α * b^-1 - - # β ↦ b*α^-1*a^-1*α*β - g *= inv(λ[6, 5]) - @test evaluate(g)[6] == b * α^-1 * a^-1 * α * β - - # b ↦ α - g *= λ[2, 5] * inv(λ[2, 1]) - @test evaluate(g)[2] == α - - # b ↦ b*α^-1*a^-1*α - g *= inv(λ[2, 5]) - @test evaluate(g)[2] == b * α^-1 * a^-1 * α - - # b ↦ b*α^-1*a^-1*α*b*α^-1 - g *= inv(ϱ[2, 5]) * ϱ[2, 1] - @test evaluate(g)[2] == b * α^-1 * a^-1 * α * b * α^-1 - - # b ↦ b*α^-1*a^-1*α*b*α^-1*a*α*b^-1 - g *= ϱ[2, 5] - @test evaluate(g)[2] == b * α^-1 * a^-1 * α * b * α^-1 * a * α * b^-1 - - x = b * α^-1 * a^-1 * α - @test evaluate(g) == - (a, x * b * x^-1, c, d, α * x^-1, x * β, γ, δ) - # (a, b, c, d, α, β, γ, δ) - @test g == T[9] - end - Base.conj(t::Groups.Transvection, p::Perm) = Groups.Transvection(t.id, t.i^p, t.j^p, t.inv) @@ -189,13 +141,15 @@ using Groups.KnuthBendix Te₁₂, Te₂₃ = T[9], T[12] G = parent(Te₁₂) F₈ = Groups.object(G) - (a, b, c, d, α, β, γ, δ) = Groups.gens(F₈) + (δ, d, γ, c, β, b, α, a) = Groups.gens(F₈) + + Groups.domain(Te₁₂) img_Te₂₃ = evaluate(Te₂₃) y = c * β^-1 * b^-1 * β - @test img_Te₂₃ == (a, b, y * c * y^-1, d, α, β * y^-1, y * γ, δ) + @test img_Te₂₃ == (δ, d, y * γ, y * c * y^-1, β * y^-1, b, α, a) - σ = perm"(1,2,3)(5,6,7)(8)" + σ = perm"(7,5,3)(8,6,4)" Te₂₃_σ = conj(Te₁₂, σ) # @test word(Te₂₃_σ) == word(Te₂₃) @@ -207,9 +161,9 @@ using Groups.KnuthBendix Te₁₂, Te₃₄ = T[9], T[14] G = parent(Te₁₂) F₈ = Groups.object(G) - (a, b, c, d, α, β, γ, δ) = Groups.gens(F₈) + (δ, d, γ, c, β, b, α, a) = Groups.gens(F₈) - σ = perm"(1,3)(2,4)(5,7)(6,8)" + σ = perm"(7,3)(8,4)(5,1)(6,2)" Te₃₄_σ = conj(Te₁₂, σ) @test Te₃₄ == Te₃₄_σ end From 4014b4f7b82145106cb996d435edc092c840b27b Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Fri, 13 Aug 2021 13:48:25 +0200 Subject: [PATCH 29/37] remove Alphabet arg from evaluate --- src/autgroups.jl | 9 ++++----- src/groups/symplectic_twists.jl | 3 +-- src/groups/transvections.jl | 13 +++++++++---- test/AutFn.jl | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/autgroups.jl b/src/autgroups.jl index 7c30686..6ac0165 100644 --- a/src/autgroups.jl +++ b/src/autgroups.jl @@ -105,16 +105,15 @@ function evaluate!( t::NTuple{N,T}, f::AbstractFPGroupElement{<:AutomorphismGroup{<:Group}}, tmp = one(first(t)), -) where {N, T} +) where {N, T<:FPGroupElement} A = alphabet(f) - AF = alphabet(object(parent(f))) for idx in word(f) - t = @inbounds evaluate!(t, A[idx], AF, tmp)::NTuple{N,T} + t = @inbounds evaluate!(t, A[idx], tmp)::NTuple{N,T} end return t end -evaluate!(t::NTuple{N, T}, s::GSymbol, A, tmp=one(first(t))) where {N, T} = throw("you need to implement `evaluate!(::$(typeof(t)), ::$(typeof(s)), ::Alphabet, tmp=one(first(t)))`") +evaluate!(t::NTuple{N, T}, s::GSymbol, tmp=nothing) where {N, T} = throw("you need to implement `evaluate!(::$(typeof(t)), ::$(typeof(s)), ::Alphabet, tmp=one(first(t)))`") # forward evaluate by substitution @@ -132,7 +131,7 @@ function LettersMap(a::FPGroupElement{<:AutomorphismGroup}) # (dom[i] → img[i] is a map from domain to images) # we need a map from alphabet indices → (gens, gens⁻¹) → images - # here we do it for elements of the domai + # here we do it for elements of the domain # (trusting it's a set of generators that define a) @assert length(dom) == length(img) diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index 6951e19..140216c 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -195,8 +195,7 @@ end function evaluate!( t::NTuple{N,T}, smc::SymplecticMappingClass, - A::Alphabet, - tmp = one(first(t)), + tmp=nothing, ) where {N,T} t = smc.f(t[smc.perm])[smc.invperm] diff --git a/src/groups/transvections.jl b/src/groups/transvections.jl index 1b9a358..dc56f2f 100644 --- a/src/groups/transvections.jl +++ b/src/groups/transvections.jl @@ -45,9 +45,15 @@ Base.:(==)(t::Transvection, s::Transvection) = t.id === s.id && t.ij == s.ij && t.inv == s.inv Base.hash(t::Transvection, h::UInt) = hash(t.id, hash(t.ij, hash(t.inv, h))) -Base.@propagate_inbounds function evaluate!(v::NTuple{T, N}, t::Transvection, A::Alphabet, tmp=one(first(v))) where {T, N} +Base.@propagate_inbounds @inline function evaluate!( + v::NTuple{T, N}, + t::Transvection, + tmp=one(first(v)) +) where {T, N} i, j = indices(t) - @assert i ≤ length(v) && j ≤ length(v) + @assert 1 ≤ i ≤ length(v) && 1 ≤ j ≤ length(v) + + A = alphabet(parent(first(v))) @inbounds begin if t.id === :ϱ @@ -101,6 +107,5 @@ Base.inv(p::PermRightAut) = PermRightAut(invperm(p.perm)) Base.:(==)(p::PermRightAut, q::PermRightAut) = p.perm == q.perm Base.hash(p::PermRightAut, h::UInt) = hash(p.perm, hash(PermRightAut, h)) -function evaluate!(v::NTuple{T, N}, p::PermRightAut, ::Alphabet, tmp=one(first(v))) where {T, N} - return v[p.perm] +evaluate!(v::NTuple{T,N}, p::PermRightAut, tmp=nothing) where {T,N} = v[p.perm] end diff --git a/test/AutFn.jl b/test/AutFn.jl index a269163..b8a3f1d 100644 --- a/test/AutFn.jl +++ b/test/AutFn.jl @@ -47,7 +47,7 @@ r = Groups.Transvection(:ϱ,i,j) l = Groups.Transvection(:λ,i,j) - (t::Groups.Transvection)(v::Tuple) = Groups.evaluate!(v, t, A4) + (t::Groups.Transvection)(v::Tuple) = Groups.evaluate!(v, t) @test r(deepcopy(D)) == (a*b, b, c, d) @test inv(r)(deepcopy(D)) == (a*b^-1,b, c, d) From 67e7a3105e2ba237e9a6972ae580ca1629188a56 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Fri, 13 Aug 2021 13:51:45 +0200 Subject: [PATCH 30/37] =?UTF-8?q?add=20rotation=5Felement=20and=20correct?= =?UTF-8?q?=20Te,=20Ta,=20T=CE=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/groups/symplectic_twists.jl | 108 +++++++++++++++++++++++++------- 1 file changed, 87 insertions(+), 21 deletions(-) diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index 140216c..89e1190 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -5,8 +5,8 @@ struct ΡΛ end function Base.getindex(rl::ΡΛ, i::Integer, j::Integer) - @assert 1 ≤ i ≤ rl.N - @assert 1 ≤ j ≤ rl.N + @assert 1 ≤ i ≤ rl.N "Got $i > $(rl.N)" + @assert 1 ≤ j ≤ rl.N "Got $j > $(rl.N)" @assert i ≠ j @assert rl.id ∈ (:λ, :ϱ) rl.id == :λ && return Word([rl.A[λ(i, j)]]) @@ -19,11 +19,16 @@ function Te_diagonal(λ::Groups.ΡΛ, ϱ::Groups.ΡΛ, i::Integer) N = λ.N @assert iseven(N) + A = λ.A n = N ÷ 2 j = i + 1 - @assert 1 <= i < n - A = λ.A + if i == n + τ = rotation_element(λ, ϱ) + return inv(A, τ) * Te_diagonal(λ, ϱ, 1) * τ + end + + @assert 1 <= i < n NI = (2n - 2i) + 1 NJ = (2n - 2j) + 1 @@ -44,43 +49,104 @@ end function Te_lantern(A::Alphabet, b₀::T, a₁::T, a₂::T, a₃::T, a₄::T, a₅::T) where {T} a₀ = (a₁ * a₂ * a₃)^4 * inv(A, b₀) - X = a₄ * a₅ * a₃ * a₄ - b₁ = inv(A, X) * a₀ * X + X = a₄ * a₅ * a₃ * a₄ # from Primer + b₁ = inv(A, X) * a₀ * X # from Primer Y = a₂ * a₃ * a₁ * a₂ - return inv(A, Y) * b₁ * Y # b₂ + return inv(A, Y) * b₁ * Y # b₂ from Primer end -Ta(λ::Groups.ΡΛ, i::Integer) = (@assert λ.id == :λ; λ[λ.N-2i+1, λ.N-2i+2]) - -Tα(λ::Groups.ΡΛ, i::Integer) = (@assert λ.id == :λ; inv(λ.A, λ[λ.N-2i+2, λ.N-2i+1])) +function Ta(λ::Groups.ΡΛ, i::Integer) + @assert λ.id == :λ; + return λ[mod1(λ.N-2i+1, λ.N), mod1(λ.N-2i+2, λ.N)] +end +function Tα(λ::Groups.ΡΛ, i::Integer) + @assert λ.id == :λ; + return inv(λ.A, λ[mod1(λ.N-2i+2, λ.N), mod1(λ.N-2i+1, λ.N)]) +end function Te(λ::ΡΛ, ϱ::ΡΛ, i, j) @assert i ≠ j - i, j = i < j ? (i, j) : (j, i) @assert λ.N == ϱ.N @assert λ.A == ϱ.A @assert λ.id == :λ && ϱ.id == :ϱ + @assert iseven(λ.N) + genus = λ.N÷2 + i = mod1(i, genus) + j = mod1(j, genus) + @assert 1 ≤ i ≤ λ.N @assert 1 ≤ j ≤ λ.N - if j == i + 1 + A = λ.A + + if mod(j - (i + 1), genus) == 0 return Te_diagonal(λ, ϱ, i) else - return Te_lantern( - λ.A, - Ta(λ, i + 1), - Ta(λ, i), - Tα(λ, i), - Te(λ, ϱ, i, i + 1), - Tα(λ, i + 1), - Te(λ, ϱ, i + 1, j), - ) + return inv(A, Te_lantern( + A, + # Our notation: # Primer notation: + inv(A, Ta(λ, i + 1)), # b₀ + inv(A, Ta(λ, i)), # a₁ + inv(A, Tα(λ, i)), # a₂ + inv(A, Te_diagonal(λ, ϱ, i)), # a₃ + inv(A, Tα(λ, i + 1)), # a₄ + inv(A, Te(λ, ϱ, i + 1, j)), # a₅ + )) end end +""" + rotation_element(G::AutomorphismGroup{<:FreeGroup}) +Return the element of `G` which corresponds to shifting generators of the free group. + +In the corresponding mapping class group this element acts by rotation of the surface anti-clockwise. +""" +function rotation_element(G::AutomorphismGroup{<:FreeGroup}) + + A = alphabet(G) + @assert iseven(ngens(object(G))) + genus = ngens(object(G)) ÷ 2 + + λ = ΡΛ(:λ, A, 2genus) + ϱ = ΡΛ(:ϱ, A, 2genus) + + return G(rotation_element(λ, ϱ)) +end + +function rotation_element(λ::ΡΛ, ϱ::ΡΛ) + @assert iseven(λ.N) + genus = λ.N÷2 + A = λ.A + + halftwists = map(1:genus-1) do i + j = i + 1 + x = Ta(λ, j) * inv(A, Ta(λ, i)) * Tα(λ, j) * Te_diagonal(λ, ϱ, i) + δ = x * Tα(λ, i) * inv(A, x) + c = + inv(A, Ta(λ, j)) * + Te(λ, ϱ, i, j) * + Tα(λ, i)^2 * + inv(A, δ) * + inv(A, Ta(λ, j)) * + Ta(λ, i) * + δ + z = + Te_diagonal(λ, ϱ, i) * + inv(A, Ta(λ, i)) * + Tα(λ, i) * + Ta(λ, i) * + inv(A, Te_diagonal(λ, ϱ, i)) + + Ta(λ, i) * inv(A, Ta(λ, j) * Tα(λ, j))^6 * (Ta(λ, j) * Tα(λ, j) * z)^4 * c + end + + τ = (Ta(λ, 1) * Tα(λ, 1))^6 * prod(halftwists) + return τ +end + function mcg_twists(G::AutomorphismGroup{<:FreeGroup}) @assert iseven(ngens(object(G))) From 1d4e21968725729d6a9a20c81430f65e694f3a21 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Fri, 13 Aug 2021 13:53:48 +0200 Subject: [PATCH 31/37] remove PermLeftAut --- src/groups/symplectic_twists.jl | 49 --------------------------------- 1 file changed, 49 deletions(-) diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index 89e1190..44e4ac9 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -267,54 +267,5 @@ function evaluate!( t = smc.f(t[smc.perm])[smc.invperm] return t end - -struct PermLeftAut <: GSymbol - perm::Vector{UInt8} - - function PermLeftAut(p::AbstractVector{<:Integer}) - @assert sort(p) == 1:length(p) - return new(p) end end - -function Base.show(io::IO, p::PermLeftAut) - print(io, "σˡ") - join(io, (subscriptify(Int(i)) for i in p.perm)) -end - -Base.inv(p::PermLeftAut) = PermLeftAut(invperm(p.perm)) - -Base.:(==)(p::PermLeftAut, q::PermLeftAut) = p.perm == q.perm -Base.hash(p::PermLeftAut, h::UInt) = hash(p.perm, hash(PermLeftAut, h)) - -function evaluate!(v::NTuple{T, N}, p::PermLeftAut, ::Alphabet, tmp=one(first(v))) where {T, N} - - G = parent(first(v)) - A = alphabet(G) - img = gens(G)[p.perm] - gens_idcs = Dict(A[A[first(word(im))]] => img[i] for (i,im) in enumerate(gens(G))) - - for elt in v - copyto!(tmp, elt) - resize!(word(elt), 0) - for idx in word(tmp) - # @show idx - if haskey(gens_idcs, idx) - k = gens_idcs[idx] - append!(word(elt), word(k)) - elseif haskey(gens_idcs, inv(A, idx)) - k = inv(gens_idcs[inv(A, idx)]) - append!(word(elt), word(k)) - else - push!(word(elt), idx) - end - end - _setnormalform!(elt, false) - _setvalidhash!(elt, false) - - normalform!(tmp, elt) - copyto!(elt, tmp) - end - - return v -end From 0fbdbbde14f99f19b39ed44674069304e83cf82b Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Fri, 13 Aug 2021 13:55:35 +0200 Subject: [PATCH 32/37] =?UTF-8?q?fix=20inverses=20and=20symplectic=20twist?= =?UTF-8?q?s=20to=20finally=20arrive=20at=20SAut(=CF=80=E2=82=81=CE=A3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/groups/mcg.jl | 29 +++++++++++++++++++++-------- src/groups/symplectic_twists.jl | 30 ++++++++++++++---------------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/groups/mcg.jl b/src/groups/mcg.jl index 37f40a4..5d9041f 100644 --- a/src/groups/mcg.jl +++ b/src/groups/mcg.jl @@ -20,10 +20,18 @@ end function SurfaceGroup(genus::Integer, boundaries::Integer) @assert genus > 1 + # The (confluent) rewriting systems comes from + # S. Hermiller, Rewriting systems for Coxeter groups + # Journal of Pure and Applied Algebra + # Volume 92, Issue 2, 7 March 1994, Pages 137-148 + # https://doi.org/10.1016/0022-4049(94)90019-1 + # Note: the notation is "inverted": + # a_g of the article becomes A_g here. + ltrs = String[] for i in 1:genus subscript = join('₀'+d for d in reverse(digits(i))) - append!(ltrs, ["a" * subscript, "A" * subscript, "b" * subscript, "B" * subscript]) + append!(ltrs, ["A" * subscript, "a" * subscript, "B" * subscript, "b" * subscript]) end Al = Alphabet(reverse!(ltrs)) @@ -66,17 +74,17 @@ relations(S::SurfaceGroup) = S.relations function symplectic_twists(π₁Σ::SurfaceGroup) g = genus(π₁Σ) - saut = SpecialAutomorphismGroup(FreeGroup(2g)) + saut = SpecialAutomorphismGroup(FreeGroup(2g), maxrules=100) - Aij = [SymplecticMappingClass(π₁Σ, saut, :A, i, j) for i in 1:g for j in 1:g if i≠j] + Aij = [SymplecticMappingClass(saut, :A, i, j) for i in 1:g for j in 1:g if i≠j] - Bij = [SymplecticMappingClass(π₁Σ, saut, :B, i, j) for i in 1:g for j in i+1:g] + Bij = [SymplecticMappingClass(saut, :B, i, j) for i in 1:g for j in 1:g if i≠j] - mBij = [SymplecticMappingClass(π₁Σ, saut, :B, i, j, minus=true) for i in 1:g for j in i+1:g] + mBij = [SymplecticMappingClass(saut, :B, i, j, minus=true) for i in 1:g for j in 1:g if i≠j] - Bii = [SymplecticMappingClass(π₁Σ, saut, :B, i, i) for i in 1:g] + Bii = [SymplecticMappingClass(saut, :B, i, i) for i in 1:g] - mBii = [SymplecticMappingClass(π₁Σ, saut, :B, i, i, minus=true) for i in 1:g] + mBii = [SymplecticMappingClass(saut, :B, i, i, minus=true) for i in 1:g] return [Aij; Bij; mBij; Bii; mBii] end @@ -86,5 +94,10 @@ KnuthBendix.alphabet(G::AutomorphismGroup{<:SurfaceGroup}) = rewriting(G) function AutomorphismGroup(π₁Σ::SurfaceGroup; kwargs...) S = vcat(symplectic_twists(π₁Σ)...) A = Alphabet(S) - return AutomorphismGroup(π₁Σ, S, A, ntuple(i->gens(π₁Σ, i), 2genus(π₁Σ))) + + # this is to fix the definitions of symplectic twists: + # with i->gens(π₁Σ, i) the corresponding automorphisms return + # reversed words + domain = ntuple(i->inv(gens(π₁Σ, i)), 2genus(π₁Σ)) + return AutomorphismGroup(π₁Σ, S, A, domain) end diff --git a/src/groups/symplectic_twists.jl b/src/groups/symplectic_twists.jl index 44e4ac9..f9f36dd 100644 --- a/src/groups/symplectic_twists.jl +++ b/src/groups/symplectic_twists.jl @@ -175,8 +175,6 @@ struct SymplecticMappingClass{T, F} <: GSymbol minus::Bool inv::Bool autFn_word::T - perm::Vector{Int} - invperm::Vector{Int} f::F end @@ -185,8 +183,7 @@ Base.:(==)(a::SymplecticMappingClass, b::SymplecticMappingClass) = a.autFn_word Base.hash(a::SymplecticMappingClass, h::UInt) = hash(a.autFn_word, h) function SymplecticMappingClass( - Σ::SurfaceGroup, - sautFn, + sautFn::AutomorphismGroup{<:FreeGroup}, id::Symbol, i::Integer, j::Integer; @@ -195,11 +192,12 @@ function SymplecticMappingClass( ) @assert i > 0 && j > 0 id === :A && @assert i ≠ j - @assert 2genus(Σ) == ngens(object(sautFn)) + @assert iseven(ngens(object(sautFn))) + genus = ngens(object(sautFn))÷2 - A = KnuthBendix.alphabet(sautFn) - λ = ΡΛ(:λ, A, 2genus(Σ)) - ϱ = ΡΛ(:ϱ, A, 2genus(Σ)) + A = alphabet(sautFn) + λ = ΡΛ(:λ, A, 2genus) + ϱ = ΡΛ(:ϱ, A, 2genus) w = if id === :A Te(λ, ϱ, i, j) * @@ -229,14 +227,14 @@ function SymplecticMappingClass( throw("Type not recognized: $id") end + # w is a word defined in the context of A (= alphabet(sautFn)) + # so this "coercion" is correct a = sautFn(w) - g = genus(Σ) - perm = [2g:-2:1; (2g-1):-2:1] f = compiled(a) # f = t -> evaluate!(t, a) - res = SymplecticMappingClass(id, UInt(i), UInt(j), minus, inverse, a, perm, invperm(perm), f) + res = SymplecticMappingClass(id, UInt(i), UInt(j), minus, inverse, a, f) return res end @@ -255,7 +253,7 @@ function Base.inv(m::SymplecticMappingClass) inv_w = inv(m.autFn_word) # f(t) = evaluate!(t, inv_w) f = compiled(inv_w) - return SymplecticMappingClass(m.id, m.i, m.j, m.minus, !m.inv, inv_w, m.perm, m.invperm, f) + return SymplecticMappingClass(m.id, m.i, m.j, m.minus, !m.inv, inv_w, f) end function evaluate!( @@ -263,9 +261,9 @@ function evaluate!( smc::SymplecticMappingClass, tmp=nothing, ) where {N,T} - - t = smc.f(t[smc.perm])[smc.invperm] + t = smc.f(t) + for i in 1:N + normalform!(t[i]) + end return t -end - end end From 5ed9357ba5161dd297b707378b32769ef67d134f Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Fri, 13 Aug 2021 13:55:54 +0200 Subject: [PATCH 33/37] export gens --- src/Groups.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Groups.jl b/src/Groups.jl index c2f5594..a2275e8 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -10,7 +10,7 @@ import Random import OrderedCollections: OrderedSet export Alphabet, AutomorphismGroup, FreeGroup, FreeGroup, FPGroup, FPGroupElement, SpecialAutomorphismGroup -export alphabet, evaluate, word +export alphabet, evaluate, word, gens include("types.jl") include("hashing.jl") From 960a70afeff654e234e95b5951894b03be17227e Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Fri, 13 Aug 2021 15:21:47 +0200 Subject: [PATCH 34/37] Merge branch 'master' into enh/mcg --- src/groups/transvections.jl | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/groups/transvections.jl b/src/groups/transvections.jl index dc56f2f..6a94297 100644 --- a/src/groups/transvections.jl +++ b/src/groups/transvections.jl @@ -5,27 +5,21 @@ struct Transvection <: GSymbol function Transvection(id::Symbol, i::Integer, j::Integer, inv = false) @assert id in (:ϱ, :λ) - return new(id, _indices(UInt8(i), UInt8(j)), inv) + @boundscheck @assert 0 < i <= (typemax(UInt8) >> 4) + @boundscheck @assert 0 < j <= (typemax(UInt8) >> 4) + return new(id, (convert(UInt8, i) << 4) + convert(UInt8, j), inv) end end ϱ(i, j) = Transvection(:ϱ, i, j) λ(i, j) = Transvection(:λ, i, j) -_indices(ij::UInt8) = (ij & 0xf0) >> 4, (ij & 0x0f) - -function _indices(i::UInt8, j::UInt8) - @boundscheck @assert i < typemax(i) ÷ 2 - @boundscheck @assert j < typemax(j) ÷ 2 - sizeof - return (i << 4) + j -end - -indices(t::Transvection) = Int.(_indices(t.ij)) +_tophalf(ij::UInt8) = (ij & 0xf0) >> 4 +_bothalf(ij::UInt8) = (ij & 0x0f) function Base.getproperty(t::Transvection, s::Symbol) - s === :i && return first(indices(t)) - s === :j && return last(indices(t)) + s === :i && return _tophalf(t.ij) + s === :j && return _bothalf(t.ij) return Core.getfield(t, s) end @@ -39,7 +33,8 @@ function Base.show(io::IO, t::Transvection) t.inv && print(io, "^-1") end -Base.inv(t::Transvection) = Transvection(t.id, _indices(t.ij)..., !t.inv) +Base.inv(t::Transvection) = + Transvection(t.id, _tophalf(t.ij), _bothalf(t.ij), !t.inv) Base.:(==)(t::Transvection, s::Transvection) = t.id === s.id && t.ij == s.ij && t.inv == s.inv @@ -50,7 +45,7 @@ Base.@propagate_inbounds @inline function evaluate!( t::Transvection, tmp=one(first(v)) ) where {T, N} - i, j = indices(t) + i, j = t.i, t.j @assert 1 ≤ i ≤ length(v) && 1 ≤ j ≤ length(v) A = alphabet(parent(first(v))) From baec86443753fea544e933c0ffe8adf359773ae0 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Fri, 13 Aug 2021 16:19:27 +0200 Subject: [PATCH 35/37] uncomplicate Transvections --- src/groups/transvections.jl | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/groups/transvections.jl b/src/groups/transvections.jl index 6a94297..ce6238b 100644 --- a/src/groups/transvections.jl +++ b/src/groups/transvections.jl @@ -1,28 +1,18 @@ struct Transvection <: GSymbol id::Symbol - ij::UInt8 + i::UInt16 + j::UInt16 inv::Bool function Transvection(id::Symbol, i::Integer, j::Integer, inv = false) @assert id in (:ϱ, :λ) - @boundscheck @assert 0 < i <= (typemax(UInt8) >> 4) - @boundscheck @assert 0 < j <= (typemax(UInt8) >> 4) - return new(id, (convert(UInt8, i) << 4) + convert(UInt8, j), inv) + return new(id, i, j, inv) end end ϱ(i, j) = Transvection(:ϱ, i, j) λ(i, j) = Transvection(:λ, i, j) -_tophalf(ij::UInt8) = (ij & 0xf0) >> 4 -_bothalf(ij::UInt8) = (ij & 0x0f) - -function Base.getproperty(t::Transvection, s::Symbol) - s === :i && return _tophalf(t.ij) - s === :j && return _bothalf(t.ij) - return Core.getfield(t, s) -end - function Base.show(io::IO, t::Transvection) id = if t.id === :ϱ 'ϱ' @@ -33,18 +23,18 @@ function Base.show(io::IO, t::Transvection) t.inv && print(io, "^-1") end -Base.inv(t::Transvection) = - Transvection(t.id, _tophalf(t.ij), _bothalf(t.ij), !t.inv) +Base.inv(t::Transvection) = Transvection(t.id, t.i, t.j, !t.inv) Base.:(==)(t::Transvection, s::Transvection) = - t.id === s.id && t.ij == s.ij && t.inv == s.inv -Base.hash(t::Transvection, h::UInt) = hash(t.id, hash(t.ij, hash(t.inv, h))) + t.id === s.id && t.i == s.i && t.j == s.j && t.inv == s.inv + +Base.hash(t::Transvection, h::UInt) = hash(hash(t.id, hash(t.i)), hash(t.j, hash(t.inv, h))) Base.@propagate_inbounds @inline function evaluate!( - v::NTuple{T, N}, + v::NTuple{T,N}, t::Transvection, - tmp=one(first(v)) -) where {T, N} + tmp = one(first(v)), +) where {T,N} i, j = t.i, t.j @assert 1 ≤ i ≤ length(v) && 1 ≤ j ≤ length(v) @@ -102,5 +92,4 @@ Base.inv(p::PermRightAut) = PermRightAut(invperm(p.perm)) Base.:(==)(p::PermRightAut, q::PermRightAut) = p.perm == q.perm Base.hash(p::PermRightAut, h::UInt) = hash(p.perm, hash(PermRightAut, h)) -evaluate!(v::NTuple{T,N}, p::PermRightAut, tmp=nothing) where {T,N} = v[p.perm] -end +evaluate!(v::NTuple{T,N}, p::PermRightAut, tmp = nothing) where {T,N} = v[p.perm] From b5e62c27e0145e4db6b64b6f007613090de0567f Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Fri, 13 Aug 2021 16:20:31 +0200 Subject: [PATCH 36/37] =?UTF-8?q?add=20tests=20for=20AutSigma=E2=82=83.?= =?UTF-8?q?=E2=82=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/AutSigma3.jl | 75 +++++++++++++++++++++++++++++++++++++++++++++ test/AutSigma_41.jl | 52 ++++++++----------------------- test/runtests.jl | 1 + 3 files changed, 88 insertions(+), 40 deletions(-) create mode 100644 test/AutSigma3.jl diff --git a/test/AutSigma3.jl b/test/AutSigma3.jl new file mode 100644 index 0000000..475fb57 --- /dev/null +++ b/test/AutSigma3.jl @@ -0,0 +1,75 @@ +@testset "Aut(Σ₃.₀)" begin + genus = 3 + + π₁Σ = Groups.SurfaceGroup(genus, 0) + + Groups.PermRightAut(p::Perm) = Groups.PermRightAut(p.d) + # Groups.PermLeftAut(p::Perm) = Groups.PermLeftAut(p.d) + autπ₁Σ = let autπ₁Σ = AutomorphismGroup(π₁Σ) + pauts = let p = perm"(1,3,5)(2,4,6)" + [Groups.PermRightAut(p^i) for i in 0:2] + end + T = eltype(KnuthBendix.letters(alphabet(autπ₁Σ))) + S = eltype(pauts) + + A = Alphabet(Union{T,S}[KnuthBendix.letters(alphabet(autπ₁Σ)); pauts]) + + autG = AutomorphismGroup( + π₁Σ, + autπ₁Σ.gens, + A, + ntuple(i->inv(gens(π₁Σ, i)), 2Groups.genus(π₁Σ)) + ) + + autG + end + + Al = alphabet(autπ₁Σ) + S = [gens(autπ₁Σ); inv.(gens(autπ₁Σ))] + + sautFn = let ltrs = KnuthBendix.letters(Al) + parent(first(ltrs).autFn_word) + end + + τ = Groups.rotation_element(sautFn) + + @testset "Twists" begin + A = KnuthBendix.alphabet(sautFn) + λ = Groups.ΡΛ(:λ, A, 2genus) + ϱ = Groups.ΡΛ(:ϱ, A, 2genus) + @test sautFn(Groups.Te_diagonal(λ, ϱ, 1)) == + conj(sautFn(Groups.Te_diagonal(λ, ϱ, 2)), τ) + + @test sautFn(Groups.Te_diagonal(λ, ϱ, 3)) == sautFn(Groups.Te(λ, ϱ, 3, 1)) + end + + z = let d = Groups.domain(τ) + Groups.evaluate(τ^genus) + end + + @test π₁Σ.(word.(z)) == Groups.domain(first(S)) + d = Groups.domain(first(S)) + p = perm"(1,3,5)(2,4,6)" + @test Groups.evaluate!(deepcopy(d), τ) == d^inv(p) + @test Groups.evaluate!(deepcopy(d), τ^2) == d^p + + E, sizes = Groups.wlmetric_ball(S, radius=3) + @test sizes == [49, 1813, 62971] + B2 = @view E[1:sizes[2]] + + σ = autπ₁Σ(Word([Al[Groups.PermRightAut(p)]])) + + @test conj(S[7], σ) == S[10] + @test conj(S[7], σ^2) == S[11] + @test conj(S[9], σ) == S[12] + @test conj(S[9], σ^2) == S[8] + + @test conj(S[1], σ) == S[4] + @test conj(S[1], σ^2) == S[5] + @test conj(S[3], σ) == S[6] + @test conj(S[3], σ^2) == S[2] + + B2ᶜ = [conj(b, σ) for b in B2] + @test B2ᶜ != B2 + @test Set(B2ᶜ) == Set(B2) +end diff --git a/test/AutSigma_41.jl b/test/AutSigma_41.jl index 16e3a2f..7a9e0d1 100644 --- a/test/AutSigma_41.jl +++ b/test/AutSigma_41.jl @@ -5,9 +5,10 @@ using Groups.KnuthBendix genus = 4 - G = SpecialAutomorphismGroup(FreeGroup(2genus)) + Fn = FreeGroup(2genus) + G = SpecialAutomorphismGroup(Fn) - T = Groups.mcg_twists(autF) + T = Groups.mcg_twists(G) # symplectic pairing in the free Group goes like this: # f1 ↔ f5 @@ -41,7 +42,6 @@ using Groups.KnuthBendix As = T[[1, 5, 9, 6, 12, 7, 14, 8]] # the inverses of the elements a - @testset "preserving relator" begin F = Groups.object(G) @@ -123,7 +123,12 @@ using Groups.KnuthBendix # x = (a4^-1*a3^-1*a2^-1*a1^-1*u*a2*a3*a4*a5*a6) @time evaluate(x) b3 = x * a0 * x^-1 - @time evaluate(b3) + b3im = @time evaluate(b3) + b3cim = @time let g = b3 + f = Groups.compiled(g) + f(Groups.domain(g)) + end + @test b3im == b3cim @test a0 * b2 * b1 == a1 * a3 * a5 * b3 end end @@ -170,46 +175,13 @@ using Groups.KnuthBendix @testset "hyperelliptic τ is central" begin - A = alphabet(G) - λ = Groups.ΡΛ(:λ, A, 2genus) - ϱ = Groups.ΡΛ(:ϱ, A, 2genus) - - import Groups: Ta, Tα, Te - - halftwists = map(1:genus-1) do i - j = i + 1 - x = Ta(λ, j) * inv(A, Ta(λ, i)) * Tα(λ, j) * Te(λ, ϱ, i, j) - δ = x * Tα(λ, i) * inv(A, x) - c = - inv(A, Ta(λ, j)) * - Te(λ, ϱ, i, j) * - Tα(λ, i)^2 * - inv(A, δ) * - inv(A, Ta(λ, j)) * - Ta(λ, i) * - δ - z = - Te(λ, ϱ, j, i) * - inv(A, Ta(λ, i)) * - Tα(λ, i) * - Ta(λ, i) * - inv(A, Te(λ, ϱ, j, i)) - - G(Ta(λ, i) * inv(A, Ta(λ, j) * Tα(λ, j))^6 * (Ta(λ, j) * Tα(λ, j) * z)^4 * c) - end - - τ = (G(Ta(λ, 1) * Tα(λ, 1))^6) * prod(halftwists) - - # τ^genus is trivial but only in autπ₁Σ₄ - # here we check its centrality - + τ = Groups.rotation_element(G) τᵍ = τ^genus symplectic_gens = let genus = genus, G = G π₁Σ = Groups.SurfaceGroup(genus, 0) - autπ₁Σ = AutomorphismGroup(π₁Σ) - letters = alphabet(autπ₁Σ).letters - G.(word(l.autFn_word) for l in letters) + s_twists = Groups.symplectic_twists(π₁Σ) + G.(word(t.autFn_word) for t in s_twists) end @test all(sg * τᵍ == τᵍ * sg for sg in symplectic_gens) diff --git a/test/runtests.jl b/test/runtests.jl index 2e41ab8..c0334c4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -27,6 +27,7 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl")) include("AutFn.jl") include("AutSigma_41.jl") + include("AutSigma3.jl") # if !haskey(ENV, "CI") # include("benchmarks.jl") From f00de84d2943ad44c429c546b4490d314a601caa Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 28 Sep 2021 15:47:09 +0200 Subject: [PATCH 37/37] bump to 0.7 and update compats --- Project.toml | 14 +++++++------- src/groups/sautFn.jl | 2 +- src/types.jl | 1 - 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Project.toml b/Project.toml index 7e007b9..e180329 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Groups" uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557" authors = ["Marek Kaluba "] -version = "0.6.0" +version = "0.7.0" [deps] GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" @@ -11,13 +11,13 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" ThreadsX = "ac1d9e8a-700a-412c-b207-f0111f4b6c0d" [compat] -AbstractAlgebra = "0.15, 0.16" -GroupsCore = "^0.3" -KnuthBendix = "^0.2.1" +AbstractAlgebra = "0.22" +GroupsCore = "0.4" +KnuthBendix = "0.3" OrderedCollections = "1" -PermutationGroups = "^0.3" -ThreadsX = "^0.1.0" -julia = "1.3, 1.4, 1.5, 1.6" +PermutationGroups = "0.3" +ThreadsX = "0.1" +julia = "1.3" [extras] AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" diff --git a/src/groups/sautFn.jl b/src/groups/sautFn.jl index ad71282..d7b0bd1 100644 --- a/src/groups/sautFn.jl +++ b/src/groups/sautFn.jl @@ -10,7 +10,7 @@ function SpecialAutomorphismGroup(F::FreeGroup; ordering = KnuthBendix.LenLex, k maxrules = 1000*n rws = KnuthBendix.RewritingSystem(rels, ordering(A)) - @time KnuthBendix.knuthbendix!(rws; maxrules=maxrules, kwargs...) + KnuthBendix.knuthbendix!(rws; maxrules=maxrules, kwargs...) return AutomorphismGroup(F, S, rws, ntuple(i -> gens(F, i), n)) end diff --git a/src/types.jl b/src/types.jl index 7486a8a..4dcec05 100644 --- a/src/types.jl +++ b/src/types.jl @@ -84,7 +84,6 @@ end ## GroupElement Interface for FPGroupElement Base.parent(f::AbstractFPGroupElement) = f.parent -GroupsCore.parent_type(::Type{<:AbstractFPGroupElement{G}}) where {G} = G function Base.:(==)(g::AbstractFPGroupElement, h::AbstractFPGroupElement) @boundscheck @assert parent(g) === parent(h)