Compare commits

...

14 Commits

27 changed files with 622 additions and 285 deletions

1
.JuliaFormatter.toml Symbolic link
View File

@ -0,0 +1 @@
../.JuliaFormatter.toml

View File

@ -1,7 +1,7 @@
name = "Groups" name = "Groups"
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557" uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
authors = ["Marek Kaluba <kalmar@amu.edu.pl>"] authors = ["Marek Kaluba <kalmar@amu.edu.pl>"]
version = "0.7.4" version = "0.7.5"
[deps] [deps]
Folds = "41a02a25-b8f0-4f67-bc48-60067656b558" Folds = "41a02a25-b8f0-4f67-bc48-60067656b558"

View File

@ -1,5 +1,5 @@
# Groups # Groups
[![CI](https://github.com/kalmarek/Groups.jl/actions/workflows/runtests.yml/badge.svg)](https://github.com/kalmarek/Groups.jl/actions/workflows/runtests.yml) [![CI](https://github.com/kalmarek/Groups.jl/actions/workflows/ci.yml/badge.svg)](https://github.com/kalmarek/Groups.jl/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/kalmarek/Groups.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/kalmarek/Groups.jl) [![codecov](https://codecov.io/gh/kalmarek/Groups.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/kalmarek/Groups.jl)
An implementation of finitely-presented groups together with normalization (using Knuth-Bendix procedure). An implementation of finitely-presented groups together with normalization (using Knuth-Bendix procedure).

View File

@ -14,7 +14,14 @@ import KnuthBendix: alphabet, ordering
export MatrixGroups export MatrixGroups
export Alphabet, AutomorphismGroup, FreeGroup, FreeGroup, FPGroup, FPGroupElement, SpecialAutomorphismGroup, Homomorphism export Alphabet,
AutomorphismGroup,
FreeGroup,
FreeGroup,
FPGroup,
FPGroupElement,
SpecialAutomorphismGroup,
Homomorphism
export alphabet, evaluate, word, gens export alphabet, evaluate, word, gens

View File

@ -1,7 +1,8 @@
function _abelianize( function _abelianize(
i::Integer, i::Integer,
source::AutomorphismGroup{<:FreeGroup}, source::AutomorphismGroup{<:FreeGroup},
target::MatrixGroups.SpecialLinearGroup{N,T}) where {N,T} target::MatrixGroups.SpecialLinearGroup{N,T},
) where {N,T}
n = ngens(object(source)) n = ngens(object(source))
@assert n == N @assert n == N
aut = alphabet(source)[i] aut = alphabet(source)[i]
@ -12,7 +13,7 @@ function _abelianize(
eij = MatrixGroups.ElementaryMatrix{N}( eij = MatrixGroups.ElementaryMatrix{N}(
aut.j, aut.j,
aut.i, aut.i,
ifelse(aut.inv, -one(T), one(T)) ifelse(aut.inv, -one(T), one(T)),
) )
k = alphabet(target)[eij] k = alphabet(target)[eij]
return word_type(target)([k]) return word_type(target)([k])
@ -24,7 +25,8 @@ end
function _abelianize( function _abelianize(
i::Integer, i::Integer,
source::AutomorphismGroup{<:Groups.SurfaceGroup}, source::AutomorphismGroup{<:Groups.SurfaceGroup},
target::MatrixGroups.SpecialLinearGroup{N,T}) where {N,T} target::MatrixGroups.SpecialLinearGroup{N,T},
) where {N,T}
n = ngens(Groups.object(source)) n = ngens(Groups.object(source))
@assert n == N @assert n == N
g = alphabet(source)[i].autFn_word g = alphabet(source)[i].autFn_word
@ -39,7 +41,7 @@ end
function Groups._abelianize( function Groups._abelianize(
i::Integer, i::Integer,
source::AutomorphismGroup{<:Groups.SurfaceGroup}, source::AutomorphismGroup{<:Groups.SurfaceGroup},
target::MatrixGroups.SymplecticGroup{N,T} target::MatrixGroups.SymplecticGroup{N,T},
) where {N,T} ) where {N,T}
@assert iseven(N) @assert iseven(N)
As = alphabet(source) As = alphabet(source)
@ -50,10 +52,10 @@ function Groups._abelianize(
MatrixGroups.SpecialLinearGroup{2genus}(T) MatrixGroups.SpecialLinearGroup{2genus}(T)
end end
ab = Groups.Homomorphism(Groups._abelianize, source, SlN, check=false) ab = Groups.Homomorphism(Groups._abelianize, source, SlN; check = false)
matrix_spn_map = let S = gens(target) matrix_spn_map = let S = gens(target)
Dict(MatrixGroups.matrix_repr(g) => word(g) for g in union(S, inv.(S))) Dict(MatrixGroups.matrix(g) => word(g) for g in union(S, inv.(S)))
end end
# renumeration: # renumeration:
@ -63,7 +65,7 @@ function Groups._abelianize(
p = [reverse(2:2:N); reverse(1:2:N)] p = [reverse(2:2:N); reverse(1:2:N)]
g = source([i]) g = source([i])
Mg = MatrixGroups.matrix_repr(ab(g))[p, p] Mg = MatrixGroups.matrix(ab(g))[p, p]
return matrix_spn_map[Mg] return matrix_spn_map[Mg]
end end

View File

@ -1,4 +1,4 @@
function gersten_alphabet(n::Integer; commutative::Bool=true) function gersten_alphabet(n::Integer; commutative::Bool = true)
indexing = [(i, j) for i in 1:n for j in 1:n if i j] indexing = [(i, j) for i in 1:n for j in 1:n if i j]
S = [ϱ(i, j) for (i, j) in indexing] S = [ϱ(i, j) for (i, j) in indexing]
@ -40,12 +40,17 @@ function _hexagonal_rule(
return W(T[A[x], A[inv(y)], A[z]]) => W(T[A[z], A[w^-1], A[x]]) return W(T[A[x], A[inv(y)], A[z]]) => W(T[A[z], A[w^-1], A[x]])
end end
gersten_relations(n::Integer; commutative) = function gersten_relations(n::Integer; commutative)
gersten_relations(Word{UInt8}, n, commutative=commutative) return gersten_relations(Word{UInt8}, n; commutative = commutative)
end
function gersten_relations(::Type{W}, n::Integer; commutative) where {W<:AbstractWord} function gersten_relations(
::Type{W},
n::Integer;
commutative,
) where {W<:AbstractWord}
@assert n > 1 "Gersten relations are defined only for n>1, got n=$n" @assert n > 1 "Gersten relations are defined only for n>1, got n=$n"
A = gersten_alphabet(n, commutative=commutative) A = gersten_alphabet(n; commutative = commutative)
@assert length(A) <= typemax(eltype(W)) "Type $W can not represent words over alphabet with $(length(A)) letters." @assert length(A) <= typemax(eltype(W)) "Type $W can not represent words over alphabet with $(length(A)) letters."
rels = Pair{W,W}[] rels = Pair{W,W}[]
@ -74,7 +79,10 @@ function gersten_relations(::Type{W}, n::Integer; commutative) where {W<:Abstrac
for (i, j, k) in Iterators.product(1:n, 1:n, 1:n) for (i, j, k) in Iterators.product(1:n, 1:n, 1:n)
if (i j && k i && k j) if (i j && k i && k j)
push!(rels, _pentagonal_rule(W, A, ϱ(i, j)^-1, ϱ(j, k)^-1, ϱ(i, k)^-1)) push!(
rels,
_pentagonal_rule(W, A, ϱ(i, j)^-1, ϱ(j, k)^-1, ϱ(i, k)^-1),
)
push!(rels, _pentagonal_rule(W, A, ϱ(i, j)^-1, ϱ(j, k), ϱ(i, k))) push!(rels, _pentagonal_rule(W, A, ϱ(i, j)^-1, ϱ(j, k), ϱ(i, k)))
commutative && continue commutative && continue
@ -83,7 +91,10 @@ function gersten_relations(::Type{W}, n::Integer; commutative) where {W<:Abstrac
push!(rels, _pentagonal_rule(W, A, ϱ(i, j), λ(j, k)^-1, ϱ(i, k))) push!(rels, _pentagonal_rule(W, A, ϱ(i, j), λ(j, k)^-1, ϱ(i, k)))
# the same as above, but with ϱ ↔ λ: # the same as above, but with ϱ ↔ λ:
push!(rels, _pentagonal_rule(W, A, λ(i, j)^-1, λ(j, k)^-1, λ(i, k)^-1)) push!(
rels,
_pentagonal_rule(W, A, λ(i, j)^-1, λ(j, k)^-1, λ(i, k)^-1),
)
push!(rels, _pentagonal_rule(W, A, λ(i, j)^-1, λ(j, k), λ(i, k))) push!(rels, _pentagonal_rule(W, A, λ(i, j)^-1, λ(j, k), λ(i, k)))
push!(rels, _pentagonal_rule(W, A, λ(i, j), ϱ(j, k), λ(i, k)^-1)) push!(rels, _pentagonal_rule(W, A, λ(i, j), ϱ(j, k), λ(i, k)^-1))
@ -94,7 +105,10 @@ function gersten_relations(::Type{W}, n::Integer; commutative) where {W<:Abstrac
if !commutative if !commutative
for (i, j) in Iterators.product(1:n, 1:n) for (i, j) in Iterators.product(1:n, 1:n)
if i j if i j
push!(rels, _hexagonal_rule(W, A, ϱ(i, j), ϱ(j, i), λ(i, j), λ(j, i))) push!(
rels,
_hexagonal_rule(W, A, ϱ(i, j), ϱ(j, i), λ(i, j), λ(j, i)),
)
w = W([A[ϱ(i, j)], A[ϱ(j, i)^-1], A[λ(i, j)]]) w = W([A[ϱ(i, j)], A[ϱ(j, i)^-1], A[λ(i, j)]])
push!(rels, w^2 => inv(w, A)^2) push!(rels, w^2 => inv(w, A)^2)
end end

View File

@ -17,7 +17,7 @@ function Base.show(io::IO, S::SurfaceGroup)
end end
end end
function SurfaceGroup(genus::Integer, boundaries::Integer, W=Word{Int16}) function SurfaceGroup(genus::Integer, boundaries::Integer, W = Word{Int16})
@assert genus > 1 @assert genus > 1
# The (confluent) rewriting systems comes from # The (confluent) rewriting systems comes from
@ -31,7 +31,15 @@ function SurfaceGroup(genus::Integer, boundaries::Integer, W=Word{Int16})
ltrs = String[] ltrs = String[]
for i in 1:genus for i in 1:genus
subscript = join('₀' + d for d in reverse(digits(i))) 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 end
Al = Alphabet(reverse!(ltrs)) Al = Alphabet(reverse!(ltrs))
@ -51,9 +59,13 @@ function SurfaceGroup(genus::Integer, boundaries::Integer, W=Word{Int16})
comms = W(word) comms = W(word)
word_rels = [comms => one(comms)] word_rels = [comms => one(comms)]
rws = let R = KnuthBendix.RewritingSystem(word_rels, KnuthBendix.Recursive(Al)) rws =
KnuthBendix.IndexAutomaton(KnuthBendix.knuthbendix(R)) let R = KnuthBendix.RewritingSystem(
end word_rels,
KnuthBendix.Recursive(Al),
)
KnuthBendix.IndexAutomaton(KnuthBendix.knuthbendix(R))
end
elseif boundaries == 1 elseif boundaries == 1
word_rels = Pair{W,W}[] word_rels = Pair{W,W}[]
rws = let R = RewritingSystem(word_rels, KnuthBendix.LenLex(Al)) rws = let R = RewritingSystem(word_rels, KnuthBendix.LenLex(Al))
@ -66,7 +78,13 @@ function SurfaceGroup(genus::Integer, boundaries::Integer, W=Word{Int16})
F = FreeGroup(Al) F = FreeGroup(Al)
rels = [F(lhs) => F(rhs) for (lhs, rhs) in word_rels] rels = [F(lhs) => F(rhs) for (lhs, rhs) in word_rels]
return SurfaceGroup(genus, boundaries, [Al[i] for i in 2:2:length(Al)], rels, rws) return SurfaceGroup(
genus,
boundaries,
[Al[i] for i in 2:2:length(Al)],
rels,
rws,
)
end end
rewriting(S::SurfaceGroup) = S.rw rewriting(S::SurfaceGroup) = S.rw
@ -75,17 +93,26 @@ relations(S::SurfaceGroup) = S.relations
function symplectic_twists(π₁Σ::SurfaceGroup) function symplectic_twists(π₁Σ::SurfaceGroup)
g = genus(π₁Σ) g = genus(π₁Σ)
saut = SpecialAutomorphismGroup(FreeGroup(2g), max_rules=1000) saut = SpecialAutomorphismGroup(FreeGroup(2g); max_rules = 1000)
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 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] 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] return [Aij; Bij; mBij; Bii; mBii]
end end

View File

@ -1,10 +1,13 @@
include("transvections.jl") include("transvections.jl")
include("gersten_relations.jl") include("gersten_relations.jl")
function SpecialAutomorphismGroup(F::FreeGroup; ordering=KnuthBendix.LenLex, kwargs...) function SpecialAutomorphismGroup(
F::FreeGroup;
ordering = KnuthBendix.LenLex,
kwargs...,
)
n = length(alphabet(F)) ÷ 2 n = length(alphabet(F)) ÷ 2
A, rels = gersten_relations(n, commutative=false) A, rels = gersten_relations(n; commutative = false)
S = [A[i] for i in 1:2:length(A)] S = [A[i] for i in 1:2:length(A)]
max_rules = 1000 * n max_rules = 1000 * n
@ -12,7 +15,10 @@ function SpecialAutomorphismGroup(F::FreeGroup; ordering=KnuthBendix.LenLex, kwa
rws = Logging.with_logger(Logging.NullLogger()) do rws = Logging.with_logger(Logging.NullLogger()) do
rws = KnuthBendix.RewritingSystem(rels, ordering(A)) rws = KnuthBendix.RewritingSystem(rels, ordering(A))
# the rws is not confluent, let's suppress warning about it # the rws is not confluent, let's suppress warning about it
KnuthBendix.knuthbendix(rws, KnuthBendix.Settings(; max_rules=max_rules, kwargs...)) return KnuthBendix.knuthbendix(
rws,
KnuthBendix.Settings(; max_rules = max_rules, kwargs...),
)
end end
idxA = KnuthBendix.IndexAutomaton(rws) idxA = KnuthBendix.IndexAutomaton(rws)
@ -21,5 +27,5 @@ end
function relations(G::AutomorphismGroup{<:FreeGroup}) function relations(G::AutomorphismGroup{<:FreeGroup})
n = length(alphabet(object(G))) ÷ 2 n = length(alphabet(object(G))) ÷ 2
return last(gersten_relations(n, commutative=false)) return last(gersten_relations(n; commutative = false))
end end

View File

@ -47,7 +47,15 @@ function Te_diagonal(λ::Groups.ΡΛ, ϱ::Groups.ΡΛ, i::Integer)
return g return g
end end
function Te_lantern(A::Alphabet, b₀::T, a₁::T, a₂::T, a₃::T, a₄::T, a₅::T) where {T} 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(b₀, A) a₀ = (a₁ * a₂ * a₃)^4 * inv(b₀, A)
X = a₄ * a₅ * a₃ * a₄ # from Primer X = a₄ * a₅ * a₃ * a₄ # from Primer
b₁ = inv(X, A) * a₀ * X # from Primer b₁ = inv(X, A) * a₀ * X # from Primer
@ -85,7 +93,8 @@ function Te(λ::ΡΛ, ϱ::ΡΛ, i, j)
if mod(j - (i + 1), genus) == 0 if mod(j - (i + 1), genus) == 0
return Te_diagonal(λ, ϱ, i) return Te_diagonal(λ, ϱ, i)
else else
return inv(Te_lantern( return inv(
Te_lantern(
A, A,
# Our notation: # Primer notation: # Our notation: # Primer notation:
inv(Ta(λ, i + 1), A), # b₀ inv(Ta(λ, i + 1), A), # b₀
@ -94,7 +103,9 @@ function Te(λ::ΡΛ, ϱ::ΡΛ, i, j)
inv(Te_diagonal(λ, ϱ, i), A), # a₃ inv(Te_diagonal(λ, ϱ, i), A), # a₃
inv(Tα(λ, i + 1), A), # a₄ inv(Tα(λ, i + 1), A), # a₄
inv(Te(λ, ϱ, i + 1, j), A), # a₅ inv(Te(λ, ϱ, i + 1, j), A), # a₅
), A) ),
A,
)
end end
end end
@ -105,7 +116,6 @@ Return the element of `G` which corresponds to shifting generators of the free g
In the corresponding mapping class group this element acts by rotation of the surface anti-clockwise. In the corresponding mapping class group this element acts by rotation of the surface anti-clockwise.
""" """
function rotation_element(G::AutomorphismGroup{<:FreeGroup}) function rotation_element(G::AutomorphismGroup{<:FreeGroup})
A = alphabet(G) A = alphabet(G)
@assert iseven(ngens(object(G))) @assert iseven(ngens(object(G)))
genus = ngens(object(G)) ÷ 2 genus = ngens(object(G)) ÷ 2
@ -140,7 +150,10 @@ function rotation_element(λ::ΡΛ, ϱ::ΡΛ)
Ta(λ, i) * Ta(λ, i) *
inv(Te_diagonal(λ, ϱ, i), A) inv(Te_diagonal(λ, ϱ, i), A)
Ta(λ, i) * inv(Ta(λ, j) * Tα(λ, j), A)^6 * (Ta(λ, j) * Tα(λ, j) * z)^4 * c return Ta(λ, i) *
inv(Ta(λ, j) * Tα(λ, j), A)^6 *
(Ta(λ, j) * Tα(λ, j) * z)^4 *
c
end end
τ = (Ta(λ, 1) * Tα(λ, 1))^6 * prod(halftwists) τ = (Ta(λ, 1) * Tα(λ, 1))^6 * prod(halftwists)
@ -148,7 +161,6 @@ function rotation_element(λ::ΡΛ, ϱ::ΡΛ)
end end
function mcg_twists(G::AutomorphismGroup{<:FreeGroup}) function mcg_twists(G::AutomorphismGroup{<:FreeGroup})
@assert iseven(ngens(object(G))) @assert iseven(ngens(object(G)))
genus = ngens(object(G)) ÷ 2 genus = ngens(object(G)) ÷ 2
@ -178,7 +190,9 @@ struct SymplecticMappingClass{T,F} <: GSymbol
f::F f::F
end end
Base.:(==)(a::SymplecticMappingClass, b::SymplecticMappingClass) = a.autFn_word == b.autFn_word function Base.:(==)(a::SymplecticMappingClass, b::SymplecticMappingClass)
return a.autFn_word == b.autFn_word
end
Base.hash(a::SymplecticMappingClass, h::UInt) = hash(a.autFn_word, h) Base.hash(a::SymplecticMappingClass, h::UInt) = hash(a.autFn_word, h)
@ -187,8 +201,8 @@ function SymplecticMappingClass(
id::Symbol, id::Symbol,
i::Integer, i::Integer,
j::Integer; j::Integer;
minus=false, minus = false,
inverse=false inverse = false,
) )
@assert i > 0 && j > 0 @assert i > 0 && j > 0
id === :A && @assert i j id === :A && @assert i j
@ -246,7 +260,7 @@ function Base.show(io::IO, smc::SymplecticMappingClass)
else else
print(io, smc.id, subscriptify(smc.i), ".", subscriptify(smc.j)) print(io, smc.id, subscriptify(smc.i), ".", subscriptify(smc.j))
end end
smc.inv && print(io, "^-1") return smc.inv && print(io, "^-1")
end end
function Base.inv(m::SymplecticMappingClass) function Base.inv(m::SymplecticMappingClass)
@ -259,7 +273,7 @@ end
function evaluate!( function evaluate!(
t::NTuple{N,T}, t::NTuple{N,T},
smc::SymplecticMappingClass, smc::SymplecticMappingClass,
tmp=nothing, tmp = nothing,
) where {N,T} ) where {N,T}
t = smc.f(t) t = smc.f(t)
for i in 1:N for i in 1:N

View File

@ -4,7 +4,7 @@ struct Transvection <: GSymbol
j::UInt16 j::UInt16
inv::Bool inv::Bool
function Transvection(id::Symbol, i::Integer, j::Integer, inv=false) function Transvection(id::Symbol, i::Integer, j::Integer, inv = false)
@assert id in (:ϱ, ) @assert id in (:ϱ, )
return new(id, i, j, inv) return new(id, i, j, inv)
end end
@ -20,20 +20,23 @@ function Base.show(io::IO, t::Transvection)
'λ' 'λ'
end end
print(io, id, subscriptify(t.i), '.', subscriptify(t.j)) print(io, id, subscriptify(t.i), '.', subscriptify(t.j))
t.inv && print(io, "^-1") return t.inv && print(io, "^-1")
end end
Base.inv(t::Transvection) = Transvection(t.id, t.i, t.j, !t.inv) Base.inv(t::Transvection) = Transvection(t.id, t.i, t.j, !t.inv)
Base.:(==)(t::Transvection, s::Transvection) = function Base.:(==)(t::Transvection, s::Transvection)
t.id === s.id && t.i == s.i && t.j == s.j && t.inv == s.inv return t.id === s.id && t.i == s.i && t.j == s.j && t.inv == s.inv
end
Base.hash(t::Transvection, h::UInt) = hash(hash(t.id, hash(t.i)), hash(t.j, hash(t.inv, h))) function Base.hash(t::Transvection, h::UInt)
return hash(hash(t.id, hash(t.i)), hash(t.j, hash(t.inv, h)))
end
Base.@propagate_inbounds @inline function evaluate!( Base.@propagate_inbounds @inline function evaluate!(
v::NTuple{T,N}, v::NTuple{T,N},
t::Transvection, t::Transvection,
tmp=one(first(v)), tmp = one(first(v)),
) where {T,N} ) where {T,N}
i, j = t.i, t.j i, j = t.i, t.j
@assert 1 i length(v) && 1 j length(v) @assert 1 i length(v) && 1 j length(v)
@ -84,7 +87,7 @@ end
function Base.show(io::IO, p::PermRightAut) function Base.show(io::IO, p::PermRightAut)
print(io, 'σ') print(io, 'σ')
join(io, (subscriptify(Int(i)) for i in p.perm)) return join(io, (subscriptify(Int(i)) for i in p.perm))
end end
Base.inv(p::PermRightAut) = PermRightAut(invperm(p.perm)) Base.inv(p::PermRightAut) = PermRightAut(invperm(p.perm))
@ -92,4 +95,6 @@ Base.inv(p::PermRightAut) = PermRightAut(invperm(p.perm))
Base.:(==)(p::PermRightAut, q::PermRightAut) = p.perm == q.perm Base.:(==)(p::PermRightAut, q::PermRightAut) = p.perm == q.perm
Base.hash(p::PermRightAut, h::UInt) = hash(p.perm, hash(PermRightAut, h)) 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] function evaluate!(v::NTuple{T,N}, p::PermRightAut, tmp = nothing) where {T,N}
return v[p.perm]
end

View File

@ -1,5 +1,5 @@
function KnuthBendix.Alphabet(S::AbstractVector{<:GSymbol}) function KnuthBendix.Alphabet(S::AbstractVector{<:GSymbol})
S = unique!([S; inv.(S)]) S = union(S, inv.(S))
inversions = [findfirst(==(inv(s)), S) for s in S] inversions = [findfirst(==(inv(s)), S) for s in S]
return Alphabet(S, inversions) return Alphabet(S, inversions)
end end
@ -26,7 +26,10 @@ function equality_data(f::AbstractFPGroupElement{<:AutomorphismGroup})
return imf return imf
end end
function Base.:(==)(g::A, h::A) where {A<:AbstractFPGroupElement{<:AutomorphismGroup}} function Base.:(==)(
g::A,
h::A,
) where {A<:AbstractFPGroupElement{<:AutomorphismGroup}}
@assert parent(g) === parent(h) @assert parent(g) === parent(h)
if _isvalidhash(g) && _isvalidhash(h) if _isvalidhash(g) && _isvalidhash(h)
@ -79,32 +82,46 @@ end
# eye-candy # eye-candy
Base.show(io::IO, ::Type{<:FPGroupElement{<:AutomorphismGroup{T}}}) where {T} = function Base.show(
print(io, "Automorphism{$T, …}") io::IO,
::Type{<:FPGroupElement{<:AutomorphismGroup{T}}},
) where {T}
return print(io, "Automorphism{$T, …}")
end
Base.show(io::IO, A::AutomorphismGroup) = print(io, "automorphism group of ", object(A)) function Base.show(io::IO, A::AutomorphismGroup)
return print(io, "automorphism group of ", object(A))
end
function Base.show(io::IO, ::MIME"text/plain", a::AbstractFPGroupElement{<:AutomorphismGroup}) function Base.show(
io::IO,
::MIME"text/plain",
a::AbstractFPGroupElement{<:AutomorphismGroup},
)
println(io, "$(a):") println(io, "$(a):")
d = domain(a) d = domain(a)
im = evaluate(a) im = evaluate(a)
for (x, imx) in zip(d, im[1:end-1]) for (x, imx) in zip(d, im[1:end-1])
println(io, "$x$imx") println(io, "$x$imx")
end end
println(io, "$(last(d))$(last(im))") return println(io, "$(last(d))$(last(im))")
end end
## Automorphism Evaluation ## Automorphism Evaluation
domain(f::AbstractFPGroupElement{<:AutomorphismGroup}) = deepcopy(parent(f).domain) function domain(f::AbstractFPGroupElement{<:AutomorphismGroup})
return deepcopy(parent(f).domain)
end
# tuple(gens(object(parent(f)))...) # tuple(gens(object(parent(f)))...)
evaluate(f::AbstractFPGroupElement{<:AutomorphismGroup}) = evaluate!(domain(f), f) function evaluate(f::AbstractFPGroupElement{<:AutomorphismGroup})
return evaluate!(domain(f), f)
end
function evaluate!( function evaluate!(
t::NTuple{N,T}, t::NTuple{N,T},
f::AbstractFPGroupElement{<:AutomorphismGroup{<:Group}}, f::AbstractFPGroupElement{<:AutomorphismGroup{<:Group}},
tmp=one(first(t)), tmp = one(first(t)),
) where {N,T<:FPGroupElement} ) where {N,T<:FPGroupElement}
A = alphabet(f) A = alphabet(f)
for idx in word(f) for idx in word(f)
@ -113,7 +130,11 @@ function evaluate!(
return t return t
end end
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)))`") function 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)))`",
)
end
# forward evaluate by substitution # forward evaluate by substitution
@ -135,13 +156,13 @@ function LettersMap(a::FPGroupElement{<:AutomorphismGroup})
# (trusting it's a set of generators that define a) # (trusting it's a set of generators that define a)
@assert length(dom) == length(img) @assert length(dom) == length(img)
indices_map = Dict(A[A[fl]] => word(im) for (fl, im) in zip(first_letters, 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 # inverses of generators are dealt lazily in getindex
return LettersMap(indices_map, A) return LettersMap(indices_map, A)
end end
function Base.getindex(lm::LettersMap, i::Integer) function Base.getindex(lm::LettersMap, i::Integer)
# here i is an index of an alphabet # here i is an index of an alphabet
@boundscheck 1 i length(lm.A) @boundscheck 1 i length(lm.A)

View File

@ -12,25 +12,29 @@ struct DirectPowerElement{GEl,N,Gr<:GroupsCore.Group} <: GroupsCore.GroupElement
parent::DirectPower{Gr,N,GEl} parent::DirectPower{Gr,N,GEl}
end end
DirectPowerElement( function DirectPowerElement(
elts::AbstractVector{<:GroupsCore.GroupElement}, elts::AbstractVector{<:GroupsCore.GroupElement},
G::DirectPower, G::DirectPower,
) = DirectPowerElement(ntuple(i -> elts[i], _nfold(G)), G) )
return DirectPowerElement(ntuple(i -> elts[i], _nfold(G)), G)
end
_nfold(::DirectPower{Gr,N}) where {Gr,N} = N _nfold(::DirectPower{Gr,N}) where {Gr,N} = N
Base.one(G::DirectPower) = function Base.one(G::DirectPower)
DirectPowerElement(ntuple(_ -> one(G.group), _nfold(G)), G) return DirectPowerElement(ntuple(_ -> one(G.group), _nfold(G)), G)
end
Base.eltype(::Type{<:DirectPower{Gr,N,GEl}}) where {Gr,N,GEl} = function Base.eltype(::Type{<:DirectPower{Gr,N,GEl}}) where {Gr,N,GEl}
DirectPowerElement{GEl,N,Gr} return DirectPowerElement{GEl,N,Gr}
end
function Base.iterate(G::DirectPower) function Base.iterate(G::DirectPower)
itr = Iterators.ProductIterator(ntuple(i -> G.group, _nfold(G))) itr = Iterators.ProductIterator(ntuple(i -> G.group, _nfold(G)))
res = iterate(itr) res = iterate(itr)
@assert res !== nothing @assert res !== nothing
elt = DirectPowerElement(first(res), G) elt = DirectPowerElement(first(res), G)
return elt, (iterator=itr, state=last(res)) return elt, (iterator = itr, state = last(res))
end end
function Base.iterate(G::DirectPower, state) function Base.iterate(G::DirectPower, state)
@ -38,7 +42,7 @@ function Base.iterate(G::DirectPower, state)
res = iterate(itr, st) res = iterate(itr, st)
res === nothing && return nothing res === nothing && return nothing
elt = DirectPowerElement(first(res), G) elt = DirectPowerElement(first(res), G)
return elt, (iterator=itr, state=last(res)) return elt, (iterator = itr, state = last(res))
end end
function Base.IteratorSize(::Type{<:DirectPower{Gr,N}}) where {Gr,N} function Base.IteratorSize(::Type{<:DirectPower{Gr,N}}) where {Gr,N}
@ -49,8 +53,9 @@ end
Base.size(G::DirectPower) = ntuple(_ -> length(G.group), _nfold(G)) Base.size(G::DirectPower) = ntuple(_ -> length(G.group), _nfold(G))
GroupsCore.order(::Type{I}, G::DirectPower) where {I<:Integer} = function GroupsCore.order(::Type{I}, G::DirectPower) where {I<:Integer}
convert(I, order(I, G.group)^_nfold(G)) return convert(I, order(I, G.group)^_nfold(G))
end
GroupsCore.ngens(G::DirectPower) = _nfold(G) * ngens(G.group) GroupsCore.ngens(G::DirectPower) = _nfold(G) * ngens(G.group)
@ -83,13 +88,18 @@ end
GroupsCore.parent(g::DirectPowerElement) = g.parent GroupsCore.parent(g::DirectPowerElement) = g.parent
Base.:(==)(g::DirectPowerElement, h::DirectPowerElement) = function Base.:(==)(g::DirectPowerElement, h::DirectPowerElement)
(parent(g) === parent(h) && g.elts == h.elts) return (parent(g) === parent(h) && g.elts == h.elts)
end
Base.hash(g::DirectPowerElement, h::UInt) = hash(g.elts, hash(parent(g), h)) Base.hash(g::DirectPowerElement, h::UInt) = hash(g.elts, hash(parent(g), h))
Base.deepcopy_internal(g::DirectPowerElement, stackdict::IdDict) = function Base.deepcopy_internal(g::DirectPowerElement, stackdict::IdDict)
DirectPowerElement(Base.deepcopy_internal(g.elts, stackdict), parent(g)) return DirectPowerElement(
Base.deepcopy_internal(g.elts, stackdict),
parent(g),
)
end
Base.inv(g::DirectPowerElement) = DirectPowerElement(inv.(g.elts), parent(g)) Base.inv(g::DirectPowerElement) = DirectPowerElement(inv.(g.elts), parent(g))
@ -98,15 +108,25 @@ function Base.:(*)(g::DirectPowerElement, h::DirectPowerElement)
return DirectPowerElement(g.elts .* h.elts, parent(g)) return DirectPowerElement(g.elts .* h.elts, parent(g))
end end
GroupsCore.order(::Type{I}, g::DirectPowerElement) where {I<:Integer} = function GroupsCore.order(::Type{I}, g::DirectPowerElement) where {I<:Integer}
convert(I, reduce(lcm, (order(I, h) for h in g.elts), init=one(I))) return convert(I, reduce(lcm, (order(I, h) for h in g.elts); init = one(I)))
end
Base.isone(g::DirectPowerElement) = all(isone, g.elts) Base.isone(g::DirectPowerElement) = all(isone, g.elts)
function Base.show(io::IO, G::DirectPower) function Base.show(io::IO, G::DirectPower)
n = _nfold(G) n = _nfold(G)
nn = n == 1 ? "1-st" : n == 2 ? "2-nd" : n == 3 ? "3-rd" : "$n-th" nn = n == 1 ? "1-st" : n == 2 ? "2-nd" : n == 3 ? "3-rd" : "$n-th"
print(io, "Direct $(nn) power of $(G.group)") return print(io, "Direct $(nn) power of $(G.group)")
end
function Base.show(io::IO, g::DirectPowerElement)
return print(io, "( ", join(g.elts, ", "), " )")
end
# convienience:
Base.@propagate_inbounds function Base.getindex(
g::DirectPowerElement,
i::Integer,
)
return g.elts[i]
end end
Base.show(io::IO, g::DirectPowerElement) =
print(io, "( ", join(g.elts, ", "), " )")

View File

@ -14,18 +14,22 @@ end
DirectProductElement(g, h, G::DirectProduct) = DirectProduct((g, h), G) DirectProductElement(g, h, G::DirectProduct) = DirectProduct((g, h), G)
Base.one(G::DirectProduct) = function Base.one(G::DirectProduct)
DirectProductElement((one(G.first), one(G.last)), G) return DirectProductElement((one(G.first), one(G.last)), G)
end
Base.eltype(::Type{<:DirectProduct{Gt,Ht,GEl,HEl}}) where {Gt,Ht,GEl,HEl} = function Base.eltype(
DirectProductElement{GEl,HEl,Gt,Ht} ::Type{<:DirectProduct{Gt,Ht,GEl,HEl}},
) where {Gt,Ht,GEl,HEl}
return DirectProductElement{GEl,HEl,Gt,Ht}
end
function Base.iterate(G::DirectProduct) function Base.iterate(G::DirectProduct)
itr = Iterators.product(G.first, G.last) itr = Iterators.product(G.first, G.last)
res = iterate(itr) res = iterate(itr)
@assert res !== nothing @assert res !== nothing
elt = DirectProductElement(first(res), G) elt = DirectProductElement(first(res), G)
return elt, (iterator=itr, state=last(res)) return elt, (iterator = itr, state = last(res))
end end
function Base.iterate(G::DirectProduct, state) function Base.iterate(G::DirectProduct, state)
@ -33,7 +37,7 @@ function Base.iterate(G::DirectProduct, state)
res = iterate(itr, st) res = iterate(itr, st)
res === nothing && return nothing res === nothing && return nothing
elt = DirectProductElement(first(res), G) elt = DirectProductElement(first(res), G)
return elt, (iterator=itr, state=last(res)) return elt, (iterator = itr, state = last(res))
end end
function Base.IteratorSize(::Type{<:DirectProduct{Gt,Ht}}) where {Gt,Ht} function Base.IteratorSize(::Type{<:DirectProduct{Gt,Ht}}) where {Gt,Ht}
@ -50,15 +54,18 @@ end
Base.size(G::DirectProduct) = (length(G.first), length(G.last)) Base.size(G::DirectProduct) = (length(G.first), length(G.last))
GroupsCore.order(::Type{I}, G::DirectProduct) where {I<:Integer} = function GroupsCore.order(::Type{I}, G::DirectProduct) where {I<:Integer}
convert(I, order(I, G.first) * order(I, G.last)) return convert(I, order(I, G.first) * order(I, G.last))
end
GroupsCore.ngens(G::DirectProduct) = ngens(G.first) + ngens(G.last) GroupsCore.ngens(G::DirectProduct) = ngens(G.first) + ngens(G.last)
function GroupsCore.gens(G::DirectProduct) function GroupsCore.gens(G::DirectProduct)
gens_first = [DirectProductElement((g, one(G.last)), G) for g in gens(G.first)] gens_first =
[DirectProductElement((g, one(G.last)), G) for g in gens(G.first)]
gens_last = [DirectProductElement((one(G.first), g), G) for g in gens(G.last)] gens_last =
[DirectProductElement((one(G.first), g), G) for g in gens(G.last)]
return [gens_first; gens_last] return [gens_first; gens_last]
end end
@ -75,28 +82,45 @@ end
GroupsCore.parent(g::DirectProductElement) = g.parent GroupsCore.parent(g::DirectProductElement) = g.parent
Base.:(==)(g::DirectProductElement, h::DirectProductElement) = function Base.:(==)(g::DirectProductElement, h::DirectProductElement)
(parent(g) === parent(h) && g.elts == h.elts) return (parent(g) === parent(h) && g.elts == h.elts)
end
Base.hash(g::DirectProductElement, h::UInt) = hash(g.elts, hash(parent(g), h)) Base.hash(g::DirectProductElement, h::UInt) = hash(g.elts, hash(parent(g), h))
Base.deepcopy_internal(g::DirectProductElement, stackdict::IdDict) = function Base.deepcopy_internal(g::DirectProductElement, stackdict::IdDict)
DirectProductElement(Base.deepcopy_internal(g.elts, stackdict), parent(g)) return DirectProductElement(
Base.deepcopy_internal(g.elts, stackdict),
parent(g),
)
end
Base.inv(g::DirectProductElement) = function Base.inv(g::DirectProductElement)
DirectProductElement(inv.(g.elts), parent(g)) return DirectProductElement(inv.(g.elts), parent(g))
end
function Base.:(*)(g::DirectProductElement, h::DirectProductElement) function Base.:(*)(g::DirectProductElement, h::DirectProductElement)
@assert parent(g) === parent(h) @assert parent(g) === parent(h)
return DirectProductElement(g.elts .* h.elts, parent(g)) return DirectProductElement(g.elts .* h.elts, parent(g))
end end
GroupsCore.order(::Type{I}, g::DirectProductElement) where {I<:Integer} = function GroupsCore.order(::Type{I}, g::DirectProductElement) where {I<:Integer}
convert(I, lcm(order(I, first(g.elts)), order(I, last(g.elts)))) return convert(I, lcm(order(I, first(g.elts)), order(I, last(g.elts))))
end
Base.isone(g::DirectProductElement) = all(isone, g.elts) Base.isone(g::DirectProductElement) = all(isone, g.elts)
Base.show(io::IO, G::DirectProduct) = function Base.show(io::IO, G::DirectProduct)
print(io, "Direct product of $(G.first) and $(G.last)") return print(io, "Direct product of $(G.first) and $(G.last)")
Base.show(io::IO, g::DirectProductElement) = end
print(io, "( $(join(g.elts, ",")) )") function Base.show(io::IO, g::DirectProductElement)
return print(io, "( $(join(g.elts, ",")) )")
end
# convienience:
Base.@propagate_inbounds function Base.getindex(
g::DirectProductElement,
i::Integer,
)
return g.elts[i]
end

View File

@ -1,4 +1,5 @@
import PermutationGroups: AbstractPermutationGroup, AbstractPerm, degree, SymmetricGroup import PermutationGroups:
AbstractPermutationGroup, AbstractPerm, degree, SymmetricGroup
""" """
WreathProduct(G::Group, P::AbstractPermutationGroup) <: Group WreathProduct(G::Group, P::AbstractPermutationGroup) <: Group
@ -38,21 +39,22 @@ struct WreathProductElement{
p::AbstractPerm, p::AbstractPerm,
W::WreathProduct, W::WreathProduct,
) )
new{typeof(n),typeof(p),typeof(W)}(n, p, W) return new{typeof(n),typeof(p),typeof(W)}(n, p, W)
end end
end end
Base.one(W::WreathProduct) = WreathProductElement(one(W.N), one(W.P), W) Base.one(W::WreathProduct) = WreathProductElement(one(W.N), one(W.P), W)
Base.eltype(::Type{<:WreathProduct{DP,PGr}}) where {DP,PGr} = function Base.eltype(::Type{<:WreathProduct{DP,PGr}}) where {DP,PGr}
WreathProductElement{eltype(DP),eltype(PGr),WreathProduct{DP,PGr}} return WreathProductElement{eltype(DP),eltype(PGr),WreathProduct{DP,PGr}}
end
function Base.iterate(G::WreathProduct) function Base.iterate(G::WreathProduct)
itr = Iterators.product(G.N, G.P) itr = Iterators.product(G.N, G.P)
res = iterate(itr) res = iterate(itr)
@assert res !== nothing @assert res !== nothing
elt = WreathProductElement(first(res)..., G) elt = WreathProductElement(first(res)..., G)
return elt, (iterator=itr, state=last(res)) return elt, (iterator = itr, state = last(res))
end end
function Base.iterate(G::WreathProduct, state) function Base.iterate(G::WreathProduct, state)
@ -60,7 +62,7 @@ function Base.iterate(G::WreathProduct, state)
res = iterate(itr, st) res = iterate(itr, st)
res === nothing && return nothing res === nothing && return nothing
elt = WreathProductElement(first(res)..., G) elt = WreathProductElement(first(res)..., G)
return elt, (iterator=itr, state=last(res)) return elt, (iterator = itr, state = last(res))
end end
function Base.IteratorSize(::Type{<:WreathProduct{DP,PGr}}) where {DP,PGr} function Base.IteratorSize(::Type{<:WreathProduct{DP,PGr}}) where {DP,PGr}
@ -78,8 +80,9 @@ end
Base.size(G::WreathProduct) = (length(G.N), length(G.P)) Base.size(G::WreathProduct) = (length(G.N), length(G.P))
GroupsCore.order(::Type{I}, G::WreathProduct) where {I<:Integer} = function GroupsCore.order(::Type{I}, G::WreathProduct) where {I<:Integer}
convert(I, order(I, G.N) * order(I, G.P)) return convert(I, order(I, G.N) * order(I, G.P))
end
function GroupsCore.gens(G::WreathProduct) function GroupsCore.gens(G::WreathProduct)
N_gens = [WreathProductElement(n, one(G.P), G) for n in gens(G.N)] N_gens = [WreathProductElement(n, one(G.P), G) for n in gens(G.N)]
@ -93,18 +96,19 @@ function Base.rand(
rng::Random.AbstractRNG, rng::Random.AbstractRNG,
rs::Random.SamplerTrivial{<:WreathProduct}, rs::Random.SamplerTrivial{<:WreathProduct},
) )
G = rs[] G = rs[]
return WreathProductElement(rand(rng, G.N), rand(rng, G.P), G) return WreathProductElement(rand(rng, G.N), rand(rng, G.P), G)
end end
GroupsCore.parent(g::WreathProductElement) = g.parent GroupsCore.parent(g::WreathProductElement) = g.parent
Base.:(==)(g::WreathProductElement, h::WreathProductElement) = function Base.:(==)(g::WreathProductElement, h::WreathProductElement)
parent(g) === parent(h) && g.n == h.n && g.p == h.p return parent(g) === parent(h) && g.n == h.n && g.p == h.p
end
Base.hash(g::WreathProductElement, h::UInt) = function Base.hash(g::WreathProductElement, h::UInt)
hash(g.n, hash(g.p, hash(g.parent, h))) return hash(g.n, hash(g.p, hash(g.parent, h)))
end
function Base.deepcopy_internal(g::WreathProductElement, stackdict::IdDict) function Base.deepcopy_internal(g::WreathProductElement, stackdict::IdDict)
return WreathProductElement( return WreathProductElement(
@ -114,8 +118,9 @@ function Base.deepcopy_internal(g::WreathProductElement, stackdict::IdDict)
) )
end end
_act(p::AbstractPerm, n::DirectPowerElement) = function _act(p::AbstractPerm, n::DirectPowerElement)
DirectPowerElement(n.elts^p, parent(n)) return DirectPowerElement(n.elts^p, parent(n))
end
function Base.inv(g::WreathProductElement) function Base.inv(g::WreathProductElement)
pinv = inv(g.p) pinv = inv(g.p)
@ -129,8 +134,9 @@ end
Base.isone(g::WreathProductElement) = isone(g.n) && isone(g.p) Base.isone(g::WreathProductElement) = isone(g.n) && isone(g.p)
Base.show(io::IO, G::WreathProduct) = function Base.show(io::IO, G::WreathProduct)
print(io, "Wreath product of $(G.N.group) by $(G.P)") return print(io, "Wreath product of $(G.N.group) by $(G.P)")
end
Base.show(io::IO, g::WreathProductElement) = print(io, "( $(g.n)$(g.p) )") Base.show(io::IO, g::WreathProductElement) = print(io, "( $(g.n)$(g.p) )")
Base.copy(g::WreathProductElement) = WreathProductElement(g.n, g.p, parent(g)) Base.copy(g::WreathProductElement) = WreathProductElement(g.n, g.p, parent(g))

View File

@ -20,8 +20,12 @@ _isvalidhash(g::AbstractFPGroupElement) = bitget(g.savedhash, 1)
_setnormalform(h::UInt, v::Bool) = bitset(h, v, 0) _setnormalform(h::UInt, v::Bool) = bitset(h, v, 0)
_setvalidhash(h::UInt, v::Bool) = bitset(h, v, 1) _setvalidhash(h::UInt, v::Bool) = bitset(h, v, 1)
_setnormalform!(g::AbstractFPGroupElement, v::Bool) = g.savedhash = _setnormalform(g.savedhash, v) function _setnormalform!(g::AbstractFPGroupElement, v::Bool)
_setvalidhash!(g::AbstractFPGroupElement, v::Bool) = g.savedhash = _setvalidhash(g.savedhash, v) return g.savedhash = _setnormalform(g.savedhash, v)
end
function _setvalidhash!(g::AbstractFPGroupElement, v::Bool)
return g.savedhash = _setvalidhash(g.savedhash, v)
end
# To update hash use this internal method, possibly only after computing the # To update hash use this internal method, possibly only after computing the
# normal form of `g`: # normal form of `g`:

View File

@ -67,11 +67,13 @@ struct Homomorphism{Gr1,Gr2,I,W}
f, f,
source::AbstractFPGroup, source::AbstractFPGroup,
target::AbstractFPGroup; target::AbstractFPGroup;
check=true check = true,
) )
A = alphabet(source) A = alphabet(source)
dct = Dict(i => convert(word_type(target), f(i, source, target)) dct = Dict(
for i in 1:length(A)) i => convert(word_type(target), f(i, source, target)) for
i in 1:length(A)
)
I = eltype(word_type(source)) I = eltype(word_type(source))
W = word_type(target) W = word_type(target)
hom = new{typeof(source),typeof(target),I,W}(dct, source, target) hom = new{typeof(source),typeof(target),I,W}(dct, source, target)
@ -79,7 +81,6 @@ struct Homomorphism{Gr1,Gr2,I,W}
if check if check
@assert hom(one(source)) == one(target) @assert hom(one(source)) == one(target)
for x in gens(source) for x in gens(source)
@assert hom(x^-1) == hom(x)^-1 @assert hom(x^-1) == hom(x)^-1
for y in gens(source) for y in gens(source)
@ -111,4 +112,6 @@ function (h::Homomorphism)(g::AbstractFPGroupElement)
return h.target(w) return h.target(w)
end end
Base.show(io::IO, h::Homomorphism) = print(io, "Homomorphism\n from : $(h.source)\n to : $(h.target)") function Base.show(io::IO, h::Homomorphism)
return print(io, "Homomorphism\n from : $(h.source)\n to : $(h.target)")
end

View File

@ -9,10 +9,11 @@ import GroupsCore.Random # GroupsCore rand
using ..Groups using ..Groups
using Groups.KnuthBendix using Groups.KnuthBendix
export SpecialLinearGroup, SymplecticGroup export MatrixGroup, SpecialLinearGroup, SymplecticGroup
include("abstract.jl") include("abstract.jl")
include("matrix_group.jl")
include("SLn.jl") include("SLn.jl")
include("Spn.jl") include("Spn.jl")

View File

@ -1,42 +1,35 @@
include("eltary_matrices.jl") include("eltary_matrices.jl")
struct SpecialLinearGroup{N,T,R,A,S} <: MatrixGroup{N,T} struct SpecialLinearGroup{N,T,R,S} <: AbstractMatrixGroup{N,T}
base_ring::R base_ring::R
alphabet::A alphabet::Alphabet{S}
gens::S gens::Vector{S}
function SpecialLinearGroup{N}(base_ring) where {N} function SpecialLinearGroup{N}(base_ring) where {N}
S = [ElementaryMatrix{N}(i, j, one(base_ring)) for i in 1:N for j in 1:N if i j] S = [
ElementaryMatrix{N}(i, j, one(base_ring)) for i in 1:N for
j in 1:N if i j
]
alphabet = Alphabet(S) alphabet = Alphabet(S)
return new{ T = eltype(base_ring)
N, R = typeof(base_ring)
eltype(base_ring), St = eltype(S)
typeof(base_ring),
typeof(alphabet), return new{N,T,R,St}(base_ring, alphabet, S)
typeof(S)
}(base_ring, alphabet, S)
end end
end end
GroupsCore.ngens(SL::SpecialLinearGroup{N}) where {N} = N^2 - N GroupsCore.ngens(SL::SpecialLinearGroup) = length(SL.gens)
Base.show(io::IO, SL::SpecialLinearGroup{N,T}) where {N,T} = function Base.show(io::IO, ::SpecialLinearGroup{N,T}) where {N,T}
print(io, "special linear group of $N×$N matrices over $T") return print(io, "SL{$N,$T}")
end
function Base.show( function Base.show(
io::IO, io::IO,
::MIME"text/plain", ::MIME"text/plain",
sl::Groups.AbstractFPGroupElement{<:SpecialLinearGroup{N}} SL::SpecialLinearGroup{N,T},
) where {N} ) where {N,T}
return print(io, "special linear group of $N×$N matrices over $T")
Groups.normalform!(sl)
print(io, "SL{$N,$(eltype(sl))} matrix: ")
KnuthBendix.print_repr(io, word(sl), alphabet(sl))
println(io)
Base.print_array(io, matrix_repr(sl))
end end
Base.show(io::IO, sl::Groups.AbstractFPGroupElement{<:SpecialLinearGroup}) =
KnuthBendix.print_repr(io, word(sl), alphabet(sl))

View File

@ -1,49 +1,44 @@
include("eltary_symplectic.jl") include("eltary_symplectic.jl")
struct SymplecticGroup{N,T,R,A,S} <: MatrixGroup{N,T} struct SymplecticGroup{N,T,R,S} <: AbstractMatrixGroup{N,T}
base_ring::R base_ring::R
alphabet::A alphabet::Alphabet{S}
gens::S gens::Vector{S}
function SymplecticGroup{N}(base_ring) where {N} function SymplecticGroup{N}(base_ring) where {N}
S = symplectic_gens(N, eltype(base_ring)) S = symplectic_gens(N, eltype(base_ring))
alphabet = Alphabet(S) alphabet = Alphabet(S)
return new{ T = eltype(base_ring)
N, R = typeof(base_ring)
eltype(base_ring), St = eltype(S)
typeof(base_ring),
typeof(alphabet), return new{N,T,R,St}(base_ring, alphabet, S)
typeof(S)
}(base_ring, alphabet, S)
end end
end end
GroupsCore.ngens(Sp::SymplecticGroup) = length(Sp.gens) GroupsCore.ngens(Sp::SymplecticGroup) = length(Sp.gens)
Base.show(io::IO, ::SymplecticGroup{N}) where {N} = print(io, "group of $N×$N symplectic matrices") Base.show(io::IO, ::SymplecticGroup{N,T}) where {N,T} = print(io, "Sp{$N,$T}")
function Base.show( function Base.show(io::IO, ::MIME"text/plain", ::SymplecticGroup{N}) where {N}
io::IO, return print(io, "group of $N×$N symplectic matrices")
::MIME"text/plain",
sp::Groups.AbstractFPGroupElement{<:SymplecticGroup{N}}
) where {N}
Groups.normalform!(sp)
print(io, "$N×$N symplectic matrix: ")
KnuthBendix.print_repr(io, word(sp), alphabet(sp))
println(io)
Base.print_array(io, matrix_repr(sp))
end end
_offdiag_idcs(n) = ((i, j) for i in 1:n for j in 1:n if i j) function symplectic_gens(N, T = Int8)
function symplectic_gens(N, T=Int8)
iseven(N) || throw(ArgumentError("N needs to be even!")) iseven(N) || throw(ArgumentError("N needs to be even!"))
n = N ÷ 2 n = N ÷ 2
a_ijs = [ElementarySymplectic{N}(:A, i, j, one(T)) for (i, j) in _offdiag_idcs(n)] _offdiag_idcs(n) = ((i, j) for i in 1:n for j in 1:n if i j)
a_ijs = [
ElementarySymplectic{N}(:A, i, j, one(T)) for (i, j) in _offdiag_idcs(n)
]
b_is = [ElementarySymplectic{N}(:B, n + i, i, one(T)) for i in 1:n] b_is = [ElementarySymplectic{N}(:B, n + i, i, one(T)) for i in 1:n]
c_ijs = [ElementarySymplectic{N}(:B, n + i, j, one(T)) for (i, j) in _offdiag_idcs(n)] c_ijs = [
ElementarySymplectic{N}(:B, n + i, j, one(T)) for
(i, j) in _offdiag_idcs(n)
]
S = [a_ijs; b_is; c_ijs] S = [a_ijs; b_is; c_ijs]
@ -60,11 +55,16 @@ function _std_symplectic_form(m::AbstractMatrix)
n = r ÷ 2 n = r ÷ 2
𝕆 = zeros(eltype(m), n, n) 𝕆 = zeros(eltype(m), n, n)
𝕀 = one(eltype(m)) * LinearAlgebra.I 𝕀 = one(eltype(m)) * LinearAlgebra.I
Ω = [𝕆 -𝕀 Ω = [
𝕀 𝕆] 𝕆 -𝕀
𝕀 𝕆
]
return Ω return Ω
end end
function issymplectic(mat::M, Ω=_std_symplectic_form(mat)) where {M<:AbstractMatrix} function issymplectic(
mat::M,
Ω = _std_symplectic_form(mat),
) where {M<:AbstractMatrix}
return Ω == transpose(mat) * Ω * mat return Ω == transpose(mat) * Ω * mat
end end

View File

@ -1,40 +1,89 @@
abstract type MatrixGroup{N,T} <: Groups.AbstractFPGroup end abstract type AbstractMatrixGroup{N,T} <: Groups.AbstractFPGroup end
const MatrixGroupElement{N,T} = Groups.AbstractFPGroupElement{<:MatrixGroup{N,T}} const MatrixGroupElement{N,T} =
Groups.AbstractFPGroupElement{<:AbstractMatrixGroup{N,T}}
Base.isone(g::MatrixGroupElement{N,T}) where {N,T} = function Base.isone(g::MatrixGroupElement{N,T}) where {N,T}
isone(word(g)) || matrix_repr(g) == LinearAlgebra.I return isone(word(g)) || isone(matrix(g))
function Base.:(==)(m1::M1, m2::M2) where {M1<:MatrixGroupElement,M2<:MatrixGroupElement}
parent(m1) === parent(m2) || return false
word(m1) == word(m2) && return true
return matrix_repr(m1) == matrix_repr(m2)
end end
Base.size(m::MatrixGroupElement{N}) where {N} = (N, N) function Base.:(==)(
Base.eltype(m::MatrixGroupElement{N,T}) where {N,T} = T m1::M1,
m2::M2,
) where {M1<:MatrixGroupElement,M2<:MatrixGroupElement}
parent(m1) === parent(m2) || return false
word(m1) == word(m2) && return true
return matrix(m1) == matrix(m2)
end
Base.size(::MatrixGroupElement{N}) where {N} = (N, N)
Base.size(::MatrixGroupElement{N}, d) where {N} = ifelse(d::Integer <= 2, N, 1)
Base.eltype(::MatrixGroupElement{N,T}) where {N,T} = T
# three structural assumptions about matrix groups # three structural assumptions about matrix groups
Groups.word(sl::MatrixGroupElement) = sl.word Groups.word(m::MatrixGroupElement) = m.word
Base.parent(sl::MatrixGroupElement) = sl.parent Base.parent(m::MatrixGroupElement) = m.parent
Groups.alphabet(M::MatrixGroup) = M.alphabet Groups.alphabet(M::AbstractMatrixGroup) = M.alphabet
Groups.rewriting(M::MatrixGroup) = alphabet(M) Groups.rewriting(M::AbstractMatrixGroup) = alphabet(M)
Base.hash(sl::MatrixGroupElement, h::UInt) = Base.hash(m::MatrixGroupElement, h::UInt) = hash(matrix(m), hash(parent(m), h))
hash(matrix_repr(sl), hash(parent(sl), h))
function matrix_repr(m::MatrixGroupElement{N,T}) where {N,T} function matrix(m::MatrixGroupElement{N,T}) where {N,T}
if isone(word(m)) if isone(word(m))
return StaticArrays.SMatrix{N,N,T}(LinearAlgebra.I) return StaticArrays.SMatrix{N,N,T}(LinearAlgebra.I)
end end
A = alphabet(parent(m)) A = alphabet(parent(m))
return prod(matrix_repr(A[l]) for l in word(m)) return prod(matrix(A[l]) for l in word(m))
end end
function Base.convert(
::Type{M},
m::MatrixGroupElement,
) where {M<:AbstractMatrix}
return convert(M, matrix(m))
end
(M::Type{<:AbstractMatrix})(m::MatrixGroupElement) = convert(M, m)
function Base.rand( function Base.rand(
rng::Random.AbstractRNG, rng::Random.AbstractRNG,
rs::Random.SamplerTrivial{<:MatrixGroup}, rs::Random.SamplerTrivial{<:AbstractMatrixGroup},
) )
Mgroup = rs[] Mgroup = rs[]
S = gens(Mgroup) S = gens(Mgroup)
return prod(g -> rand(Bool) ? g : inv(g), rand(S, rand(1:30))) return prod(
g -> rand(rng, Bool) ? g : inv(g),
rand(rng, S, rand(rng, 1:30)),
)
end
function Base.show(io::IO, M::AbstractMatrixGroup)
g = gens(M, 1)
N = size(g, 1)
return print(io, "H ⩽ GL{$N,$(eltype(g))}")
end
function Base.show(io::IO, ::MIME"text/plain", M::AbstractMatrixGroup)
N = size(gens(M, 1), 1)
ng = GroupsCore.ngens(M)
return print(
io,
"subgroup of $N×$N invertible matrices with $(ng) generators",
)
end
function Base.show(
io::IO,
mat::Groups.AbstractFPGroupElement{<:AbstractMatrixGroup},
)
return KnuthBendix.print_repr(io, word(mat), alphabet(mat))
end
function Base.show(
io::IO,
::MIME"text/plain",
mat::Groups.AbstractFPGroupElement{<:AbstractMatrixGroup{N}},
) where {N}
Groups.normalform!(mat)
KnuthBendix.print_repr(io, word(mat), alphabet(mat))
println(io, "", parent(mat))
return Base.print_array(io, matrix(mat))
end end

View File

@ -2,25 +2,29 @@ struct ElementaryMatrix{N,T} <: Groups.GSymbol
i::Int i::Int
j::Int j::Int
val::T val::T
ElementaryMatrix{N}(i, j, val=1) where {N} = function ElementaryMatrix{N}(i, j, val = 1) where {N}
(@assert i j; new{N,typeof(val)}(i, j, val)) return (@assert i j; new{N,typeof(val)}(i, j, val))
end
end end
function Base.show(io::IO, e::ElementaryMatrix) function Base.show(io::IO, e::ElementaryMatrix)
print(io, 'E', Groups.subscriptify(e.i), Groups.subscriptify(e.j)) print(io, 'E', Groups.subscriptify(e.i), Groups.subscriptify(e.j))
!isone(e.val) && print(io, "^$(e.val)") return !isone(e.val) && print(io, "^$(e.val)")
end end
Base.:(==)(e::ElementaryMatrix{N}, f::ElementaryMatrix{N}) where {N} = function Base.:(==)(e::ElementaryMatrix{N}, f::ElementaryMatrix{N}) where {N}
e.i == f.i && e.j == f.j && e.val == f.val return e.i == f.i && e.j == f.j && e.val == f.val
end
Base.hash(e::ElementaryMatrix, h::UInt) = function Base.hash(e::ElementaryMatrix, h::UInt)
hash(typeof(e), hash((e.i, e.j, e.val), h)) return hash(typeof(e), hash((e.i, e.j, e.val), h))
end
Base.inv(e::ElementaryMatrix{N}) where {N} = function Base.inv(e::ElementaryMatrix{N}) where {N}
ElementaryMatrix{N}(e.i, e.j, -e.val) return ElementaryMatrix{N}(e.i, e.j, -e.val)
end
function matrix_repr(e::ElementaryMatrix{N,T}) where {N,T} function matrix(e::ElementaryMatrix{N,T}) where {N,T}
m = StaticArrays.MMatrix{N,N,T}(LinearAlgebra.I) m = StaticArrays.MMatrix{N,N,T}(LinearAlgebra.I)
m[e.i, e.j] = e.val m[e.i, e.j] = e.val
x = StaticArrays.SMatrix{N,N}(m) x = StaticArrays.SMatrix{N,N}(m)

View File

@ -3,7 +3,12 @@ struct ElementarySymplectic{N,T} <: Groups.GSymbol
i::Int i::Int
j::Int j::Int
val::T val::T
function ElementarySymplectic{N}(s::Symbol, i::Integer, j::Integer, val=1) where {N} function ElementarySymplectic{N}(
s::Symbol,
i::Integer,
j::Integer,
val = 1,
) where {N}
@assert s (:A, :B) @assert s (:A, :B)
@assert iseven(N) @assert iseven(N)
n = N ÷ 2 n = N ÷ 2
@ -19,7 +24,7 @@ end
function Base.show(io::IO, s::ElementarySymplectic) function Base.show(io::IO, s::ElementarySymplectic)
i, j = Groups.subscriptify(s.i), Groups.subscriptify(s.j) i, j = Groups.subscriptify(s.i), Groups.subscriptify(s.j)
print(io, s.symbol, i, j) print(io, s.symbol, i, j)
!isone(s.val) && print(io, "^$(s.val)") return !isone(s.val) && print(io, "^$(s.val)")
end end
_ind(s::ElementarySymplectic{N}) where {N} = (s.i, s.j) _ind(s::ElementarySymplectic{N}) where {N} = (s.i, s.j)
@ -41,23 +46,29 @@ function _dual_ind(N_half, i, j)
return i, j return i, j
end end
function Base.:(==)(s::ElementarySymplectic{N}, t::ElementarySymplectic{M}) where {N,M} function Base.:(==)(
s::ElementarySymplectic{N},
t::ElementarySymplectic{M},
) where {N,M}
N == M || return false N == M || return false
s.symbol == t.symbol || return false s.symbol == t.symbol || return false
s.val == t.val || return false s.val == t.val || return false
return _ind(t) == _ind(s) || _ind(t) == _dual_ind(s) return _ind(t) == _ind(s) || _ind(t) == _dual_ind(s)
end end
Base.hash(s::ElementarySymplectic, h::UInt) = function Base.hash(s::ElementarySymplectic, h::UInt)
hash(Set([_ind(s); _dual_ind(s)]), hash(s.symbol, hash(s.val, h))) return hash(Set([_ind(s); _dual_ind(s)]), hash(s.symbol, hash(s.val, h)))
end
LinearAlgebra.transpose(s::ElementarySymplectic{N}) where {N} = function LinearAlgebra.transpose(s::ElementarySymplectic{N}) where {N}
ElementarySymplectic{N}(s.symbol, s.j, s.i, s.val) return ElementarySymplectic{N}(s.symbol, s.j, s.i, s.val)
end
Base.inv(s::ElementarySymplectic{N}) where {N} = function Base.inv(s::ElementarySymplectic{N}) where {N}
ElementarySymplectic{N}(s.symbol, s.i, s.j, -s.val) return ElementarySymplectic{N}(s.symbol, s.i, s.j, -s.val)
end
function matrix_repr(s::ElementarySymplectic{N,T}) where {N,T} function matrix(s::ElementarySymplectic{N,T}) where {N,T}
@assert iseven(N) @assert iseven(N)
n = div(N, 2) n = div(N, 2)
m = StaticArrays.MMatrix{N,N,T}(LinearAlgebra.I) m = StaticArrays.MMatrix{N,N,T}(LinearAlgebra.I)

View File

@ -0,0 +1,36 @@
struct MatrixElt{N,T,} <: Groups.GSymbol
id::Symbol
inv::Bool
mat::StaticArrays.SMatrix{N,N,T,}
function MatrixElt{N,T}(
id::Symbol,
mat::AbstractMatrix,
inv::Bool = false,
) where {N,T}
n = LinearAlgebra.checksquare(mat)
@assert N == n
@assert !iszero(LinearAlgebra.det(mat))
return new{N,T,N^2}(id, inv, mat)
end
end
function MatrixElt{N}(
id::Symbol,
mat::AbstractMatrix,
inv::Bool = false,
) where {N}
return MatrixElt{N,eltype(mat)}(id, mat, inv)
end
Base.show(io::IO, m::MatrixElt) = print(io, m.id, m.inv ? "⁻¹" : "")
Base.:(==)(m::MatrixElt, n::MatrixElt) = m.mat == n.mat
Base.hash(m::MatrixElt, h::UInt) = hash(m.mat, hash(typeof(m), h))
function Base.inv(m::MatrixElt{N,T}) where {N,T}
return MatrixElt{N,T}(m.id, round.(T, inv(m.mat)), !m.inv)
end
matrix(m::MatrixElt) = m.mat

View File

@ -0,0 +1,25 @@
include("matrix_generators.jl")
struct MatrixGroup{N,T,R,S} <: AbstractMatrixGroup{N,T}
base_ring::R
alphabet::Alphabet{S}
gens::Vector{S}
end
function MatrixGroup{N}(
gens::AbstractVector{<:AbstractMatrix{T}},
base_ring = T,
) where {N,T}
S = map(enumerate(gens)) do (i, mat)
id = Symbol('m', Groups.subscriptify(i))
return MatrixElt{N}(id, mat)
end
alphabet = Alphabet(S)
R = typeof(base_ring)
St = eltype(S)
return MatrixGroup{N,T,R,St}(base_ring, alphabet, S)
end
GroupsCore.ngens(M::MatrixGroup) = length(M.gens)

View File

@ -42,10 +42,7 @@ KnuthBendix.alphabet(G::AbstractFPGroup) = alphabet(ordering(G))
Base.@propagate_inbounds function (G::AbstractFPGroup)( Base.@propagate_inbounds function (G::AbstractFPGroup)(
word::AbstractVector{<:Integer}, word::AbstractVector{<:Integer},
) )
@boundscheck @assert all( @boundscheck @assert all(l -> 1 <= l <= length(alphabet(G)), word)
l -> 1 <= l <= length(alphabet(G)),
word,
)
return FPGroupElement(word_type(G)(word), G) return FPGroupElement(word_type(G)(word), G)
end end
@ -53,8 +50,9 @@ end
Base.one(G::AbstractFPGroup) = FPGroupElement(one(word_type(G)), G) Base.one(G::AbstractFPGroup) = FPGroupElement(one(word_type(G)), G)
Base.eltype(::Type{FPG}) where {FPG<:AbstractFPGroup} = function Base.eltype(::Type{FPG}) where {FPG<:AbstractFPGroup}
FPGroupElement{FPG,word_type(FPG)} return FPGroupElement{FPG,word_type(FPG)}
end
include("iteration.jl") include("iteration.jl")
@ -65,8 +63,9 @@ function GroupsCore.gens(G::AbstractFPGroup, i::Integer)
l = alphabet(G)[G.gens[i]] l = alphabet(G)[G.gens[i]]
return FPGroupElement(word_type(G)([l]), G) return FPGroupElement(word_type(G)([l]), G)
end end
GroupsCore.gens(G::AbstractFPGroup) = function GroupsCore.gens(G::AbstractFPGroup)
[gens(G, i) for i in 1:GroupsCore.ngens(G)] return [gens(G, i) for i in 1:GroupsCore.ngens(G)]
end
# TODO: ProductReplacementAlgorithm # TODO: ProductReplacementAlgorithm
function Base.rand( function Base.rand(
@ -79,9 +78,11 @@ function Base.rand(
return FPGroupElement(word_type(G)(rand(1:nletters, l)), G) return FPGroupElement(word_type(G)(rand(1:nletters, l)), G)
end end
Base.isfinite(::AbstractFPGroup) = ( function Base.isfinite(::AbstractFPGroup)
@warn "using generic isfinite(::AbstractFPGroup): the returned `false` might be wrong"; false return (
) @warn "using generic isfinite(::AbstractFPGroup): the returned `false` might be wrong"; false
)
end
## FPGroupElement ## FPGroupElement
@ -93,18 +94,22 @@ mutable struct FPGroupElement{Gr<:AbstractFPGroup,W<:AbstractWord} <:
savedhash::UInt savedhash::UInt
parent::Gr parent::Gr
FPGroupElement( function FPGroupElement(
word::W, word::W,
G::AbstractFPGroup, G::AbstractFPGroup,
hash::UInt=UInt(0), hash::UInt = UInt(0),
) where {W<:AbstractWord} = new{typeof(G),W}(word, hash, G) ) where {W<:AbstractWord}
return new{typeof(G),W}(word, hash, G)
end
FPGroupElement{Gr,W}(word::AbstractWord, G::Gr) where {Gr,W} = function FPGroupElement{Gr,W}(word::AbstractWord, G::Gr) where {Gr,W}
new{Gr,W}(word, UInt(0), G) return new{Gr,W}(word, UInt(0), G)
end
end end
Base.show(io::IO, ::Type{<:FPGroupElement{Gr}}) where {Gr} = function Base.show(io::IO, ::Type{<:FPGroupElement{Gr}}) where {Gr}
print(io, FPGroupElement, "{$Gr, …}") return print(io, FPGroupElement, "{$Gr, …}")
end
word(f::AbstractFPGroupElement) = f.word word(f::AbstractFPGroupElement) = f.word
@ -142,11 +147,12 @@ function Base.:(*)(g::GEl, h::GEl) where {GEl<:AbstractFPGroupElement}
return GEl(word(g) * word(h), parent(g)) return GEl(word(g) * word(h), parent(g))
end end
GroupsCore.isfiniteorder(g::AbstractFPGroupElement) = function GroupsCore.isfiniteorder(g::AbstractFPGroupElement)
isone(g) ? true : return isone(g) ? true :
( (
@warn "using generic isfiniteorder(::AbstractFPGroupElement): the returned `false` might be wrong"; false @warn "using generic isfiniteorder(::AbstractFPGroupElement): the returned `false` might be wrong"; false
) )
end
# additional methods: # additional methods:
Base.isone(g::AbstractFPGroupElement) = (normalform!(g); isempty(word(g))) Base.isone(g::AbstractFPGroupElement) = (normalform!(g); isempty(word(g)))
@ -167,9 +173,7 @@ end
FreeGroup(gens, A::Alphabet) = FreeGroup(gens, KnuthBendix.LenLex(A)) FreeGroup(gens, A::Alphabet) = FreeGroup(gens, KnuthBendix.LenLex(A))
function FreeGroup(A::Alphabet) function FreeGroup(A::Alphabet)
@boundscheck @assert all( @boundscheck @assert all(KnuthBendix.hasinverse(l, A) for l in A)
KnuthBendix.hasinverse(l, A) for l in A
)
gens = Vector{eltype(A)}() gens = Vector{eltype(A)}()
invs = Vector{eltype(A)}() invs = Vector{eltype(A)}()
for l in A for l in A
@ -193,8 +197,9 @@ function FreeGroup(n::Integer)
return FreeGroup(symbols[1:2:2n], Alphabet(symbols, inverses)) return FreeGroup(symbols[1:2:2n], Alphabet(symbols, inverses))
end end
Base.show(io::IO, F::FreeGroup) = function Base.show(io::IO, F::FreeGroup)
print(io, "free group on $(ngens(F)) generators") return print(io, "free group on $(ngens(F)) generators")
end
# mandatory methods: # mandatory methods:
KnuthBendix.ordering(F::FreeGroup) = F.ordering KnuthBendix.ordering(F::FreeGroup) = F.ordering
@ -205,8 +210,9 @@ relations(F::FreeGroup) = Pair{eltype(F),eltype(F)}[]
# these are mathematically correct # these are mathematically correct
Base.isfinite(::FreeGroup) = false Base.isfinite(::FreeGroup) = false
GroupsCore.isfiniteorder(g::AbstractFPGroupElement{<:FreeGroup}) = function GroupsCore.isfiniteorder(g::AbstractFPGroupElement{<:FreeGroup})
isone(g) ? true : false return isone(g) ? true : false
end
## FP Groups ## FP Groups
@ -222,8 +228,8 @@ rewriting(G::FPGroup) = G.rw
function FPGroup( function FPGroup(
G::AbstractFPGroup, G::AbstractFPGroup,
rels::AbstractVector{<:Pair{GEl,GEl}}; rels::AbstractVector{<:Pair{GEl,GEl}};
ordering=KnuthBendix.ordering(G), ordering = KnuthBendix.ordering(G),
kwargs... kwargs...,
) where {GEl<:FPGroupElement} ) where {GEl<:FPGroupElement}
for (lhs, rhs) in rels for (lhs, rhs) in rels
@assert parent(lhs) === parent(rhs) === G @assert parent(lhs) === parent(rhs) === G
@ -253,8 +259,9 @@ function Base.show(io::IO, G::FPGroup)
return print(io, "") return print(io, "")
end end
Base.show(io::IO, ::Type{<:FPGroup{T}}) where {T} = function Base.show(io::IO, ::Type{<:FPGroup{T}}) where {T}
print(io, FPGroup, "{$T, …}") return print(io, FPGroup, "{$T, …}")
end
## GSymbol aka letter of alphabet ## GSymbol aka letter of alphabet

View File

@ -8,22 +8,40 @@ radius and multiplication operation to be used.
""" """
function wlmetric_ball( function wlmetric_ball(
S::AbstractVector{T}, S::AbstractVector{T},
center::T=one(first(S)); center::T = one(first(S));
radius=2, radius = 2,
op=*, op = *,
threading=true threading = true,
) where {T} ) where {T}
threading && return wlmetric_ball_thr(S, center, radius=radius, op=op) threading && return wlmetric_ball_thr(S, center; radius = radius, op = op)
return wlmetric_ball_serial(S, center, radius=radius, op=op) return wlmetric_ball_serial(S, center; radius = radius, op = op)
end end
function wlmetric_ball_serial(S::AbstractVector{T}, center::T=one(first(S)); radius=2, op=*) where {T} function wlmetric_ball_serial(
S::AbstractVector{T},
center::T = one(first(S));
radius = 2,
op = *,
) where {T}
@assert radius >= 1 @assert radius >= 1
old = union!([center], [center * s for s in S]) old = union!(OrderedSet([center]), [center * s for s in S])
return _wlmetric_ball(S, old, radius, op, collect, unique!) sizes = [1, length(old)]
for _ in 2:radius
new = collect(
op(o, s) for o in @view(old.dict.keys[sizes[end-1]:end]) for s in S
)
union!(old, new)
push!(sizes, length(old))
end
return old.dict.keys, sizes[2:end]
end end
function wlmetric_ball_thr(S::AbstractVector{T}, center::T=one(first(S)); radius=2, op=*) where {T} function wlmetric_ball_thr(
S::AbstractVector{T},
center::T = one(first(S));
radius = 2,
op = *,
) where {T}
@assert radius >= 1 @assert radius >= 1
old = union!([center], [center * s for s in S]) old = union!([center], [center * s for s in S])
return _wlmetric_ball(S, old, radius, op, Folds.collect, Folds.unique) return _wlmetric_ball(S, old, radius, op, Folds.collect, Folds.unique)
@ -31,11 +49,13 @@ end
function _wlmetric_ball(S, old, radius, op, collect, unique) function _wlmetric_ball(S, old, radius, op, collect, unique)
sizes = [1, length(old)] sizes = [1, length(old)]
for r in 2:radius for _ in 2:radius
old = let old = old, S = S, old = let old = old, S = S
new = collect( new = collect(
(g = op(o, s); hash(g); g) (g = op(o, s);
for o in @view(old[sizes[end-1]:end]) for s in S normalform!(g);
hash(g);
g) for o in @view(old[sizes[end-1]:end]) for s in S
) )
append!(old, new) append!(old, new)
@ -45,4 +65,3 @@ function _wlmetric_ball(S, old, radius, op, collect, unique)
end end
return old, sizes[2:end] return old, sizes[2:end]
end end

View File

@ -7,7 +7,7 @@ using Groups.MatrixGroups
S = gens(SL3Z) S = gens(SL3Z)
union!(S, inv.(S)) union!(S, inv.(S))
_, sizes = Groups.wlmetric_ball(S, radius=4) _, sizes = Groups.wlmetric_ball(S; radius = 4)
@test sizes == [13, 121, 883, 5455] @test sizes == [13, 121, 883, 5455]
@ -20,9 +20,9 @@ using Groups.MatrixGroups
S = [w, r, s] S = [w, r, s]
S = unique([S; inv.(S)]) S = unique([S; inv.(S)])
_, sizes = Groups.wlmetric_ball(S, radius=4) _, sizes = Groups.wlmetric_ball(S; radius = 4)
@test sizes == [7, 33, 141, 561] @test sizes == [7, 33, 141, 561]
_, sizes = Groups.wlmetric_ball_serial(S, radius=4) _, sizes = Groups.wlmetric_ball_serial(S; radius = 4)
@test sizes == [7, 33, 141, 561] @test sizes == [7, 33, 141, 561]
Logging.with_logger(Logging.NullLogger()) do Logging.with_logger(Logging.NullLogger()) do
@ -35,15 +35,18 @@ using Groups.MatrixGroups
end end
end end
x = w * inv(w) * r x = w * inv(w) * r
@test length(word(x)) == 5 @test length(word(x)) == 5
@test size(x) == (3, 3) @test size(x) == (3, 3)
@test eltype(x) == Int8 @test eltype(x) == Int8
@test contains(sprint(print, SL3Z), "special linear group of 3×3") @test contains(sprint(show, SL3Z), "SL{3,Int8}")
@test contains(sprint(show, MIME"text/plain"(), x), "SL{3,Int8} matrix:") @test contains(
sprint(show, MIME"text/plain"(), SL3Z),
"special linear group",
)
@test contains(sprint(show, MIME"text/plain"(), x), "∈ SL{3,Int8}")
@test sprint(print, x) isa String @test sprint(print, x) isa String
@test length(word(x)) == 3 @test length(word(x)) == 3
@ -62,8 +65,6 @@ using Groups.MatrixGroups
end end
end end
@test contains(sprint(print, Sp6), "group of 6×6 symplectic matrices")
x = gens(Sp6, 1) x = gens(Sp6, 1)
x *= inv(x) * gens(Sp6, 2) x *= inv(x) * gens(Sp6, 2)
@ -71,13 +72,50 @@ using Groups.MatrixGroups
@test size(x) == (6, 6) @test size(x) == (6, 6)
@test eltype(x) == Int8 @test eltype(x) == Int8
@test contains(sprint(show, MIME"text/plain"(), x), "6×6 symplectic matrix:") @test contains(sprint(show, Sp6), "Sp{6,Int8}")
@test contains(
sprint(show, MIME"text/plain"(), Sp6),
"group of 6×6 symplectic matrices",
)
@test contains(sprint(show, MIME"text/plain"(), x), "∈ Sp{6,Int8}")
@test sprint(print, x) isa String @test sprint(print, x) isa String
@test length(word(x)) == 1 @test length(word(x)) == 1
for g in gens(Sp6) for g in gens(Sp6)
@test MatrixGroups.issymplectic(MatrixGroups.matrix_repr(g)) @test MatrixGroups.issymplectic(MatrixGroups.matrix(g))
end end
end end
@testset "General matrix group" begin
Sp6 = MatrixGroups.SymplecticGroup{6}(Int8)
G = Groups.MatrixGroup{6}(Matrix{Int16}.(gens(Sp6)))
Logging.with_logger(Logging.NullLogger()) do
@testset "GroupsCore conformance" begin
test_Group_interface(G)
g = G(rand(1:length(alphabet(G)), 10))
h = G(rand(1:length(alphabet(G)), 10))
test_GroupElement_interface(g, h)
end
end
x = gens(G, 1)
x *= inv(x) * gens(G, 2)
@test length(word(x)) == 3
@test size(x) == (6, 6)
@test eltype(x) == Int16
@test contains(sprint(show, G), "H ⩽ GL{6,Int16}")
@test contains(
sprint(show, MIME"text/plain"(), G),
"subgroup of 6×6 invertible matrices",
)
@test contains(sprint(show, MIME"text/plain"(), x), "∈ H ⩽ GL{6,Int16}")
@test sprint(print, x) isa String
@test length(word(x)) == 1
end
end end