mirror of
https://github.com/kalmarek/Groups.jl.git
synced 2024-11-19 06:30:29 +01:00
Merge pull request #19 from kalmarek/enh/mcg
Add broader support for automorphism groups
This commit is contained in:
commit
80f7f6e08a
17
Project.toml
17
Project.toml
@ -1,10 +1,9 @@
|
||||
name = "Groups"
|
||||
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
|
||||
authors = ["Marek Kaluba <kalmar@amu.edu.pl>"]
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
|
||||
[deps]
|
||||
AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d"
|
||||
GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120"
|
||||
KnuthBendix = "c2604015-7b3d-4a30-8a26-9074551ec60a"
|
||||
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
|
||||
@ -12,17 +11,19 @@ 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"
|
||||
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"
|
||||
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"]
|
||||
|
@ -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")
|
||||
@ -18,6 +18,7 @@ include("normalform.jl")
|
||||
include("autgroups.jl")
|
||||
|
||||
include("groups/sautFn.jl")
|
||||
include("groups/mcg.jl")
|
||||
|
||||
include("wl_ball.jl")
|
||||
end # of module Groups
|
||||
|
150
src/autgroups.jl
150
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,29 +79,157 @@ 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))
|
||||
|
||||
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::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}
|
||||
) 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
|
||||
|
||||
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 domain
|
||||
# (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
|
||||
|
||||
# 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
|
||||
|
103
src/groups/mcg.jl
Normal file
103
src/groups/mcg.jl
Normal file
@ -0,0 +1,103 @@
|
||||
struct SurfaceGroup{T, S, R} <: AbstractFPGroup
|
||||
genus::Int
|
||||
boundaries::Int
|
||||
gens::Vector{T}
|
||||
relations::Vector{<:Pair{S,S}}
|
||||
rws::R
|
||||
end
|
||||
|
||||
include("symplectic_twists.jl")
|
||||
|
||||
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
|
||||
|
||||
# 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])
|
||||
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)
|
||||
word_rels = [ comms => one(comms) ]
|
||||
|
||||
rws = KnuthBendix.RewritingSystem(word_rels, KnuthBendix.RecursivePathOrder(Al))
|
||||
KnuthBendix.knuthbendix!(rws)
|
||||
elseif boundaries == 1
|
||||
S = typeof(one(Word(Int[])))
|
||||
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(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), maxrules=100)
|
||||
|
||||
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 1:g if i≠j]
|
||||
|
||||
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]
|
||||
|
||||
mBii = [SymplecticMappingClass(saut, :B, i, i, minus=true) for i in 1:g]
|
||||
|
||||
return [Aij; Bij; mBij; Bii; mBii]
|
||||
end
|
||||
|
||||
KnuthBendix.alphabet(G::AutomorphismGroup{<:SurfaceGroup}) = rewriting(G)
|
||||
|
||||
function AutomorphismGroup(π₁Σ::SurfaceGroup; kwargs...)
|
||||
S = vcat(symplectic_twists(π₁Σ)...)
|
||||
A = Alphabet(S)
|
||||
|
||||
# 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
|
@ -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...)
|
||||
KnuthBendix.knuthbendix!(rws; maxrules=maxrules, kwargs...)
|
||||
return AutomorphismGroup(F, S, rws, ntuple(i -> gens(F, i), n))
|
||||
end
|
||||
|
||||
|
269
src/groups/symplectic_twists.jl
Normal file
269
src/groups/symplectic_twists.jl
Normal file
@ -0,0 +1,269 @@
|
||||
struct ΡΛ
|
||||
id::Symbol
|
||||
A::Alphabet
|
||||
N::Int
|
||||
end
|
||||
|
||||
function Base.getindex(rl::ΡΛ, i::Integer, j::Integer)
|
||||
@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)]])
|
||||
rl.id == :ϱ && return Word([rl.A[ϱ(i, j)]])
|
||||
end
|
||||
|
||||
function Te_diagonal(λ::Groups.ΡΛ, ϱ::Groups.ΡΛ, i::Integer)
|
||||
@assert λ.N == ϱ.N
|
||||
@assert λ.id == :λ && ϱ.id == :ϱ
|
||||
|
||||
N = λ.N
|
||||
@assert iseven(N)
|
||||
A = λ.A
|
||||
n = N ÷ 2
|
||||
j = i + 1
|
||||
|
||||
if i == n
|
||||
τ = rotation_element(λ, ϱ)
|
||||
return inv(A, τ) * Te_diagonal(λ, ϱ, 1) * τ
|
||||
end
|
||||
|
||||
@assert 1 <= i < n
|
||||
|
||||
NI = (2n - 2i) + 1
|
||||
NJ = (2n - 2j) + 1
|
||||
I = (2n - 2i) + 2
|
||||
J = (2n - 2j) + 2
|
||||
|
||||
g = one(Word(Int[]))
|
||||
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
|
||||
|
||||
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₄ # from Primer
|
||||
b₁ = inv(A, X) * a₀ * X # from Primer
|
||||
Y = a₂ * a₃ * a₁ * a₂
|
||||
return inv(A, Y) * b₁ * Y # b₂ from Primer
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
@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
|
||||
|
||||
A = λ.A
|
||||
|
||||
if mod(j - (i + 1), genus) == 0
|
||||
return Te_diagonal(λ, ϱ, i)
|
||||
else
|
||||
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)))
|
||||
genus = ngens(object(G)) ÷ 2
|
||||
|
||||
genus < 3 && throw("Not Implemented: genus = $genus < 3")
|
||||
|
||||
A = KnuthBendix.alphabet(G)
|
||||
|
||||
λ = ΡΛ(:λ, A, 2genus)
|
||||
ϱ = ΡΛ(:ϱ, A, 2genus)
|
||||
|
||||
Tas = [Ta(λ, i) for i in 1:genus]
|
||||
Tαs = [Tα(λ, i) for i in 1: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{T, F} <: GSymbol
|
||||
id::Symbol # :A, :B
|
||||
i::UInt
|
||||
j::UInt
|
||||
minus::Bool
|
||||
inv::Bool
|
||||
autFn_word::T
|
||||
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(
|
||||
sautFn::AutomorphismGroup{<:FreeGroup},
|
||||
id::Symbol,
|
||||
i::Integer,
|
||||
j::Integer;
|
||||
minus = false,
|
||||
inverse = false,
|
||||
)
|
||||
@assert i > 0 && j > 0
|
||||
id === :A && @assert i ≠ j
|
||||
@assert iseven(ngens(object(sautFn)))
|
||||
genus = ngens(object(sautFn))÷2
|
||||
|
||||
A = 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
|
||||
|
||||
# w is a word defined in the context of A (= alphabet(sautFn))
|
||||
# so this "coercion" is correct
|
||||
a = sautFn(w)
|
||||
|
||||
f = compiled(a)
|
||||
# f = t -> evaluate!(t, a)
|
||||
|
||||
res = SymplecticMappingClass(id, UInt(i), UInt(j), minus, inverse, a, f)
|
||||
|
||||
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)
|
||||
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, f)
|
||||
end
|
||||
|
||||
function evaluate!(
|
||||
t::NTuple{N,T},
|
||||
smc::SymplecticMappingClass,
|
||||
tmp=nothing,
|
||||
) where {N,T}
|
||||
t = smc.f(t)
|
||||
for i in 1:N
|
||||
normalform!(t[i])
|
||||
end
|
||||
return t
|
||||
end
|
@ -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,16 +23,22 @@ 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.@propagate_inbounds function evaluate!(v::NTuple{T, N}, t::Transvection, A::Alphabet, tmp=one(first(v))) where {T, N}
|
||||
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},
|
||||
t::Transvection,
|
||||
tmp = one(first(v)),
|
||||
) where {T,N}
|
||||
i, j = t.i, t.j
|
||||
@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 === :ϱ
|
||||
@ -76,3 +72,24 @@ Base.@propagate_inbounds function evaluate!(v::NTuple{T, N}, t::Transvection, A:
|
||||
|
||||
return v
|
||||
end
|
||||
|
||||
struct PermRightAut <: GSymbol
|
||||
perm::Vector{UInt8}
|
||||
|
||||
function PermRightAut(p::AbstractVector{<:Integer})
|
||||
@assert sort(p) == 1:length(p)
|
||||
return new(p)
|
||||
end
|
||||
end
|
||||
|
||||
function Base.show(io::IO, p::PermRightAut)
|
||||
print(io, 'σ')
|
||||
join(io, (subscriptify(Int(i)) for i in p.perm))
|
||||
end
|
||||
|
||||
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]
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
42
src/types.jl
42
src/types.jl
@ -57,34 +57,35 @@ 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)
|
||||
|
||||
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::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
|
||||
|
||||
function Base.:(==)(g::FPGroupElement, h::FPGroupElement)
|
||||
function Base.:(==)(g::AbstractFPGroupElement, h::AbstractFPGroupElement)
|
||||
@boundscheck @assert parent(g) === parent(h)
|
||||
normalform!(g)
|
||||
normalform!(h)
|
||||
@ -93,20 +94,23 @@ function Base.:(==)(g::FPGroupElement, h::FPGroupElement)
|
||||
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
|
||||
|
||||
Base.inv(g::FPGroupElement) = (G = parent(g); FPGroupElement(inv(alphabet(G), word(g)), G))
|
||||
function Base.inv(g::GEl) where GEl <: AbstractFPGroupElement
|
||||
G = parent(g)
|
||||
return GEl(inv(alphabet(G), word(g)), G)
|
||||
end
|
||||
|
||||
function Base.:(*)(g::FPGroupElement, h::FPGroupElement)
|
||||
function Base.:(*)(g::GEl, h::GEl) where GEl<:AbstractFPGroupElement
|
||||
@boundscheck @assert parent(g) === parent(h)
|
||||
return FPGroupElement(word(g) * word(h), parent(g))
|
||||
return GEl(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)
|
||||
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 +161,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
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
@ -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
|
||||
|
75
test/AutSigma3.jl
Normal file
75
test/AutSigma3.jl
Normal file
@ -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
|
189
test/AutSigma_41.jl
Normal file
189
test/AutSigma_41.jl
Normal file
@ -0,0 +1,189 @@
|
||||
using PermutationGroups
|
||||
using Groups.KnuthBendix
|
||||
|
||||
@testset "Wajnryb presentation for Σ₄₁" begin
|
||||
|
||||
genus = 4
|
||||
|
||||
Fn = FreeGroup(2genus)
|
||||
G = SpecialAutomorphismGroup(Fn)
|
||||
|
||||
T = Groups.mcg_twists(G)
|
||||
|
||||
# symplectic pairing in the free Group goes like this:
|
||||
# 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)
|
||||
|
||||
[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 "preserving relator" begin
|
||||
F = Groups.object(G)
|
||||
|
||||
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
|
||||
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
|
||||
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 "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
|
||||
@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 = Groups.domain(b2)
|
||||
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
|
||||
@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 evaluate(x)
|
||||
b3 = x * a0 * x^-1
|
||||
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
|
||||
|
||||
Base.conj(t::Groups.Transvection, p::Perm) =
|
||||
Groups.Transvection(t.id, t.i^p, t.j^p, t.inv)
|
||||
|
||||
function Base.conj(elt::FPGroupElement, p::Perm)
|
||||
G = parent(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₈ = Groups.object(G)
|
||||
(δ, d, γ, c, β, b, α, a) = Groups.gens(F₈)
|
||||
|
||||
Groups.domain(Te₁₂)
|
||||
|
||||
img_Te₂₃ = evaluate(Te₂₃)
|
||||
y = c * β^-1 * b^-1 * β
|
||||
@test img_Te₂₃ == (δ, d, y * γ, y * c * y^-1, β * y^-1, b, α, a)
|
||||
|
||||
σ = perm"(7,5,3)(8,6,4)"
|
||||
Te₂₃_σ = conj(Te₁₂, σ)
|
||||
# @test word(Te₂₃_σ) == word(Te₂₃)
|
||||
|
||||
@test evaluate(Te₂₃_σ) == evaluate(Te₂₃)
|
||||
@test Te₂₃ == Te₂₃_σ
|
||||
end
|
||||
|
||||
@testset "Te₃₄ definition" begin
|
||||
Te₁₂, Te₃₄ = T[9], T[14]
|
||||
G = parent(Te₁₂)
|
||||
F₈ = Groups.object(G)
|
||||
(δ, d, γ, c, β, b, α, a) = Groups.gens(F₈)
|
||||
|
||||
σ = perm"(7,3)(8,4)(5,1)(6,2)"
|
||||
Te₃₄_σ = conj(Te₁₂, σ)
|
||||
@test Te₃₄ == Te₃₄_σ
|
||||
end
|
||||
|
||||
@testset "hyperelliptic τ is central" begin
|
||||
|
||||
τ = Groups.rotation_element(G)
|
||||
τᵍ = τ^genus
|
||||
|
||||
symplectic_gens = let genus = genus, G = G
|
||||
π₁Σ = Groups.SurfaceGroup(genus, 0)
|
||||
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)
|
||||
end
|
||||
end
|
@ -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])
|
||||
|
@ -26,6 +26,8 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl"))
|
||||
include("fp_groups.jl")
|
||||
|
||||
include("AutFn.jl")
|
||||
include("AutSigma_41.jl")
|
||||
include("AutSigma3.jl")
|
||||
|
||||
# if !haskey(ENV, "CI")
|
||||
# include("benchmarks.jl")
|
||||
|
Loading…
Reference in New Issue
Block a user