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

View File

@ -1,5 +1,5 @@
# 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)
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 Alphabet, AutomorphismGroup, FreeGroup, FreeGroup, FPGroup, FPGroupElement, SpecialAutomorphismGroup, Homomorphism
export Alphabet,
AutomorphismGroup,
FreeGroup,
FreeGroup,
FPGroup,
FPGroupElement,
SpecialAutomorphismGroup,
Homomorphism
export alphabet, evaluate, word, gens

View File

@ -1,7 +1,8 @@
function _abelianize(
i::Integer,
source::AutomorphismGroup{<:FreeGroup},
target::MatrixGroups.SpecialLinearGroup{N,T}) where {N,T}
target::MatrixGroups.SpecialLinearGroup{N,T},
) where {N,T}
n = ngens(object(source))
@assert n == N
aut = alphabet(source)[i]
@ -12,7 +13,7 @@ function _abelianize(
eij = MatrixGroups.ElementaryMatrix{N}(
aut.j,
aut.i,
ifelse(aut.inv, -one(T), one(T))
ifelse(aut.inv, -one(T), one(T)),
)
k = alphabet(target)[eij]
return word_type(target)([k])
@ -24,7 +25,8 @@ end
function _abelianize(
i::Integer,
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))
@assert n == N
g = alphabet(source)[i].autFn_word
@ -39,7 +41,7 @@ end
function Groups._abelianize(
i::Integer,
source::AutomorphismGroup{<:Groups.SurfaceGroup},
target::MatrixGroups.SymplecticGroup{N,T}
target::MatrixGroups.SymplecticGroup{N,T},
) where {N,T}
@assert iseven(N)
As = alphabet(source)
@ -50,10 +52,10 @@ function Groups._abelianize(
MatrixGroups.SpecialLinearGroup{2genus}(T)
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)
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
# renumeration:
@ -63,7 +65,7 @@ function Groups._abelianize(
p = [reverse(2:2:N); reverse(1:2:N)]
g = source([i])
Mg = MatrixGroups.matrix_repr(ab(g))[p, p]
Mg = MatrixGroups.matrix(ab(g))[p, p]
return matrix_spn_map[Mg]
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]
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]])
end
gersten_relations(n::Integer; commutative) =
gersten_relations(Word{UInt8}, n, commutative=commutative)
function gersten_relations(n::Integer; 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"
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."
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)
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)))
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)))
# 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), ϱ(j, k), λ(i, k)^-1))
@ -94,7 +105,10 @@ function gersten_relations(::Type{W}, n::Integer; commutative) where {W<:Abstrac
if !commutative
for (i, j) in Iterators.product(1:n, 1:n)
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)]])
push!(rels, w^2 => inv(w, A)^2)
end

View File

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

View File

@ -1,10 +1,13 @@
include("transvections.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
A, rels = gersten_relations(n, commutative=false)
A, rels = gersten_relations(n; commutative = false)
S = [A[i] for i in 1:2:length(A)]
max_rules = 1000 * n
@ -12,7 +15,10 @@ function SpecialAutomorphismGroup(F::FreeGroup; ordering=KnuthBendix.LenLex, kwa
rws = Logging.with_logger(Logging.NullLogger()) do
rws = KnuthBendix.RewritingSystem(rels, ordering(A))
# 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
idxA = KnuthBendix.IndexAutomaton(rws)
@ -21,5 +27,5 @@ end
function relations(G::AutomorphismGroup{<:FreeGroup})
n = length(alphabet(object(G))) ÷ 2
return last(gersten_relations(n, commutative=false))
return last(gersten_relations(n; commutative = false))
end

View File

@ -47,7 +47,15 @@ function Te_diagonal(λ::Groups.ΡΛ, ϱ::Groups.ΡΛ, i::Integer)
return g
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)
X = a₄ * a₅ * a₃ * a₄ # 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
return Te_diagonal(λ, ϱ, i)
else
return inv(Te_lantern(
return inv(
Te_lantern(
A,
# Our notation: # Primer notation:
inv(Ta(λ, i + 1), A), # b₀
@ -94,7 +103,9 @@ function Te(λ::ΡΛ, ϱ::ΡΛ, i, j)
inv(Te_diagonal(λ, ϱ, i), A), # a₃
inv(Tα(λ, i + 1), A), # a₄
inv(Te(λ, ϱ, i + 1, j), A), # a₅
), A)
),
A,
)
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.
"""
function rotation_element(G::AutomorphismGroup{<:FreeGroup})
A = alphabet(G)
@assert iseven(ngens(object(G)))
genus = ngens(object(G)) ÷ 2
@ -140,7 +150,10 @@ function rotation_element(λ::ΡΛ, ϱ::ΡΛ)
Ta(λ, i) *
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
τ = (Ta(λ, 1) * Tα(λ, 1))^6 * prod(halftwists)
@ -148,7 +161,6 @@ function rotation_element(λ::ΡΛ, ϱ::ΡΛ)
end
function mcg_twists(G::AutomorphismGroup{<:FreeGroup})
@assert iseven(ngens(object(G)))
genus = ngens(object(G)) ÷ 2
@ -178,7 +190,9 @@ struct SymplecticMappingClass{T,F} <: GSymbol
f::F
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)
@ -187,8 +201,8 @@ function SymplecticMappingClass(
id::Symbol,
i::Integer,
j::Integer;
minus=false,
inverse=false
minus = false,
inverse = false,
)
@assert i > 0 && j > 0
id === :A && @assert i j
@ -246,7 +260,7 @@ function Base.show(io::IO, smc::SymplecticMappingClass)
else
print(io, smc.id, subscriptify(smc.i), ".", subscriptify(smc.j))
end
smc.inv && print(io, "^-1")
return smc.inv && print(io, "^-1")
end
function Base.inv(m::SymplecticMappingClass)
@ -259,7 +273,7 @@ end
function evaluate!(
t::NTuple{N,T},
smc::SymplecticMappingClass,
tmp=nothing,
tmp = nothing,
) where {N,T}
t = smc.f(t)
for i in 1:N

View File

@ -4,7 +4,7 @@ struct Transvection <: GSymbol
j::UInt16
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 (:ϱ, )
return new(id, i, j, inv)
end
@ -20,20 +20,23 @@ function Base.show(io::IO, t::Transvection)
'λ'
end
print(io, id, subscriptify(t.i), '.', subscriptify(t.j))
t.inv && print(io, "^-1")
return t.inv && print(io, "^-1")
end
Base.inv(t::Transvection) = Transvection(t.id, t.i, t.j, !t.inv)
Base.:(==)(t::Transvection, s::Transvection) =
t.id === s.id && t.i == s.i && t.j == s.j && t.inv == s.inv
function Base.:(==)(t::Transvection, s::Transvection)
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!(
v::NTuple{T,N},
t::Transvection,
tmp=one(first(v)),
tmp = one(first(v)),
) where {T,N}
i, j = t.i, t.j
@assert 1 i length(v) && 1 j length(v)
@ -84,7 +87,7 @@ end
function Base.show(io::IO, p::PermRightAut)
print(io, 'σ')
join(io, (subscriptify(Int(i)) for i in p.perm))
return join(io, (subscriptify(Int(i)) for i in p.perm))
end
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.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})
S = unique!([S; inv.(S)])
S = union(S, inv.(S))
inversions = [findfirst(==(inv(s)), S) for s in S]
return Alphabet(S, inversions)
end
@ -26,7 +26,10 @@ function equality_data(f::AbstractFPGroupElement{<:AutomorphismGroup})
return imf
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)
if _isvalidhash(g) && _isvalidhash(h)
@ -79,32 +82,46 @@ end
# eye-candy
Base.show(io::IO, ::Type{<:FPGroupElement{<:AutomorphismGroup{T}}}) where {T} =
print(io, "Automorphism{$T, …}")
function Base.show(
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):")
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))")
return println(io, "$(last(d))$(last(im))")
end
## 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)))...)
evaluate(f::AbstractFPGroupElement{<:AutomorphismGroup}) = evaluate!(domain(f), f)
function evaluate(f::AbstractFPGroupElement{<:AutomorphismGroup})
return evaluate!(domain(f), f)
end
function evaluate!(
t::NTuple{N,T},
f::AbstractFPGroupElement{<:AutomorphismGroup{<:Group}},
tmp=one(first(t)),
tmp = one(first(t)),
) where {N,T<:FPGroupElement}
A = alphabet(f)
for idx in word(f)
@ -113,7 +130,11 @@ function evaluate!(
return t
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
@ -135,13 +156,13 @@ function LettersMap(a::FPGroupElement{<:AutomorphismGroup})
# (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))
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(lm.A)

View File

@ -12,25 +12,29 @@ struct DirectPowerElement{GEl,N,Gr<:GroupsCore.Group} <: GroupsCore.GroupElement
parent::DirectPower{Gr,N,GEl}
end
DirectPowerElement(
function DirectPowerElement(
elts::AbstractVector{<:GroupsCore.GroupElement},
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
Base.one(G::DirectPower) =
DirectPowerElement(ntuple(_ -> one(G.group), _nfold(G)), G)
function Base.one(G::DirectPower)
return DirectPowerElement(ntuple(_ -> one(G.group), _nfold(G)), G)
end
Base.eltype(::Type{<:DirectPower{Gr,N,GEl}}) where {Gr,N,GEl} =
DirectPowerElement{GEl,N,Gr}
function Base.eltype(::Type{<:DirectPower{Gr,N,GEl}}) where {Gr,N,GEl}
return DirectPowerElement{GEl,N,Gr}
end
function Base.iterate(G::DirectPower)
itr = Iterators.ProductIterator(ntuple(i -> G.group, _nfold(G)))
res = iterate(itr)
@assert res !== nothing
elt = DirectPowerElement(first(res), G)
return elt, (iterator=itr, state=last(res))
return elt, (iterator = itr, state = last(res))
end
function Base.iterate(G::DirectPower, state)
@ -38,7 +42,7 @@ function Base.iterate(G::DirectPower, state)
res = iterate(itr, st)
res === nothing && return nothing
elt = DirectPowerElement(first(res), G)
return elt, (iterator=itr, state=last(res))
return elt, (iterator = itr, state = last(res))
end
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))
GroupsCore.order(::Type{I}, G::DirectPower) where {I<:Integer} =
convert(I, order(I, G.group)^_nfold(G))
function GroupsCore.order(::Type{I}, G::DirectPower) where {I<:Integer}
return convert(I, order(I, G.group)^_nfold(G))
end
GroupsCore.ngens(G::DirectPower) = _nfold(G) * ngens(G.group)
@ -83,13 +88,18 @@ end
GroupsCore.parent(g::DirectPowerElement) = g.parent
Base.:(==)(g::DirectPowerElement, h::DirectPowerElement) =
(parent(g) === parent(h) && g.elts == h.elts)
function Base.:(==)(g::DirectPowerElement, h::DirectPowerElement)
return (parent(g) === parent(h) && g.elts == h.elts)
end
Base.hash(g::DirectPowerElement, h::UInt) = hash(g.elts, hash(parent(g), h))
Base.deepcopy_internal(g::DirectPowerElement, stackdict::IdDict) =
DirectPowerElement(Base.deepcopy_internal(g.elts, stackdict), parent(g))
function Base.deepcopy_internal(g::DirectPowerElement, stackdict::IdDict)
return DirectPowerElement(
Base.deepcopy_internal(g.elts, stackdict),
parent(g),
)
end
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))
end
GroupsCore.order(::Type{I}, g::DirectPowerElement) where {I<:Integer} =
convert(I, reduce(lcm, (order(I, h) for h in g.elts), init=one(I)))
function GroupsCore.order(::Type{I}, g::DirectPowerElement) where {I<:Integer}
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)
function Base.show(io::IO, G::DirectPower)
n = _nfold(G)
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
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)
Base.one(G::DirectProduct) =
DirectProductElement((one(G.first), one(G.last)), G)
function Base.one(G::DirectProduct)
return DirectProductElement((one(G.first), one(G.last)), G)
end
Base.eltype(::Type{<:DirectProduct{Gt,Ht,GEl,HEl}}) where {Gt,Ht,GEl,HEl} =
DirectProductElement{GEl,HEl,Gt,Ht}
function Base.eltype(
::Type{<:DirectProduct{Gt,Ht,GEl,HEl}},
) where {Gt,Ht,GEl,HEl}
return DirectProductElement{GEl,HEl,Gt,Ht}
end
function Base.iterate(G::DirectProduct)
itr = Iterators.product(G.first, G.last)
res = iterate(itr)
@assert res !== nothing
elt = DirectProductElement(first(res), G)
return elt, (iterator=itr, state=last(res))
return elt, (iterator = itr, state = last(res))
end
function Base.iterate(G::DirectProduct, state)
@ -33,7 +37,7 @@ function Base.iterate(G::DirectProduct, state)
res = iterate(itr, st)
res === nothing && return nothing
elt = DirectProductElement(first(res), G)
return elt, (iterator=itr, state=last(res))
return elt, (iterator = itr, state = last(res))
end
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))
GroupsCore.order(::Type{I}, G::DirectProduct) where {I<:Integer} =
convert(I, order(I, G.first) * order(I, G.last))
function GroupsCore.order(::Type{I}, G::DirectProduct) where {I<:Integer}
return convert(I, order(I, G.first) * order(I, G.last))
end
GroupsCore.ngens(G::DirectProduct) = ngens(G.first) + ngens(G.last)
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]
end
@ -75,28 +82,45 @@ end
GroupsCore.parent(g::DirectProductElement) = g.parent
Base.:(==)(g::DirectProductElement, h::DirectProductElement) =
(parent(g) === parent(h) && g.elts == h.elts)
function Base.:(==)(g::DirectProductElement, h::DirectProductElement)
return (parent(g) === parent(h) && g.elts == h.elts)
end
Base.hash(g::DirectProductElement, h::UInt) = hash(g.elts, hash(parent(g), h))
Base.deepcopy_internal(g::DirectProductElement, stackdict::IdDict) =
DirectProductElement(Base.deepcopy_internal(g.elts, stackdict), parent(g))
function Base.deepcopy_internal(g::DirectProductElement, stackdict::IdDict)
return DirectProductElement(
Base.deepcopy_internal(g.elts, stackdict),
parent(g),
)
end
Base.inv(g::DirectProductElement) =
DirectProductElement(inv.(g.elts), parent(g))
function Base.inv(g::DirectProductElement)
return DirectProductElement(inv.(g.elts), parent(g))
end
function Base.:(*)(g::DirectProductElement, h::DirectProductElement)
@assert parent(g) === parent(h)
return DirectProductElement(g.elts .* h.elts, parent(g))
end
GroupsCore.order(::Type{I}, g::DirectProductElement) where {I<:Integer} =
convert(I, lcm(order(I, first(g.elts)), order(I, last(g.elts))))
function GroupsCore.order(::Type{I}, g::DirectProductElement) where {I<:Integer}
return convert(I, lcm(order(I, first(g.elts)), order(I, last(g.elts))))
end
Base.isone(g::DirectProductElement) = all(isone, g.elts)
Base.show(io::IO, G::DirectProduct) =
print(io, "Direct product of $(G.first) and $(G.last)")
Base.show(io::IO, g::DirectProductElement) =
print(io, "( $(join(g.elts, ",")) )")
function Base.show(io::IO, G::DirectProduct)
return print(io, "Direct product of $(G.first) and $(G.last)")
end
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
@ -38,21 +39,22 @@ struct WreathProductElement{
p::AbstractPerm,
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
Base.one(W::WreathProduct) = WreathProductElement(one(W.N), one(W.P), W)
Base.eltype(::Type{<:WreathProduct{DP,PGr}}) where {DP,PGr} =
WreathProductElement{eltype(DP),eltype(PGr),WreathProduct{DP,PGr}}
function Base.eltype(::Type{<:WreathProduct{DP,PGr}}) where {DP,PGr}
return WreathProductElement{eltype(DP),eltype(PGr),WreathProduct{DP,PGr}}
end
function Base.iterate(G::WreathProduct)
itr = Iterators.product(G.N, G.P)
res = iterate(itr)
@assert res !== nothing
elt = WreathProductElement(first(res)..., G)
return elt, (iterator=itr, state=last(res))
return elt, (iterator = itr, state = last(res))
end
function Base.iterate(G::WreathProduct, state)
@ -60,7 +62,7 @@ function Base.iterate(G::WreathProduct, state)
res = iterate(itr, st)
res === nothing && return nothing
elt = WreathProductElement(first(res)..., G)
return elt, (iterator=itr, state=last(res))
return elt, (iterator = itr, state = last(res))
end
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))
GroupsCore.order(::Type{I}, G::WreathProduct) where {I<:Integer} =
convert(I, order(I, G.N) * order(I, G.P))
function GroupsCore.order(::Type{I}, G::WreathProduct) where {I<:Integer}
return convert(I, order(I, G.N) * order(I, G.P))
end
function GroupsCore.gens(G::WreathProduct)
N_gens = [WreathProductElement(n, one(G.P), G) for n in gens(G.N)]
@ -93,18 +96,19 @@ function Base.rand(
rng::Random.AbstractRNG,
rs::Random.SamplerTrivial{<:WreathProduct},
)
G = rs[]
return WreathProductElement(rand(rng, G.N), rand(rng, G.P), G)
end
GroupsCore.parent(g::WreathProductElement) = g.parent
Base.:(==)(g::WreathProductElement, h::WreathProductElement) =
parent(g) === parent(h) && g.n == h.n && g.p == h.p
function Base.:(==)(g::WreathProductElement, h::WreathProductElement)
return parent(g) === parent(h) && g.n == h.n && g.p == h.p
end
Base.hash(g::WreathProductElement, h::UInt) =
hash(g.n, hash(g.p, hash(g.parent, h)))
function Base.hash(g::WreathProductElement, h::UInt)
return hash(g.n, hash(g.p, hash(g.parent, h)))
end
function Base.deepcopy_internal(g::WreathProductElement, stackdict::IdDict)
return WreathProductElement(
@ -114,8 +118,9 @@ function Base.deepcopy_internal(g::WreathProductElement, stackdict::IdDict)
)
end
_act(p::AbstractPerm, n::DirectPowerElement) =
DirectPowerElement(n.elts^p, parent(n))
function _act(p::AbstractPerm, n::DirectPowerElement)
return DirectPowerElement(n.elts^p, parent(n))
end
function Base.inv(g::WreathProductElement)
pinv = inv(g.p)
@ -129,8 +134,9 @@ end
Base.isone(g::WreathProductElement) = isone(g.n) && isone(g.p)
Base.show(io::IO, G::WreathProduct) =
print(io, "Wreath product of $(G.N.group) by $(G.P)")
function Base.show(io::IO, G::WreathProduct)
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.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)
_setvalidhash(h::UInt, v::Bool) = bitset(h, v, 1)
_setnormalform!(g::AbstractFPGroupElement, v::Bool) = g.savedhash = _setnormalform(g.savedhash, v)
_setvalidhash!(g::AbstractFPGroupElement, v::Bool) = g.savedhash = _setvalidhash(g.savedhash, v)
function _setnormalform!(g::AbstractFPGroupElement, v::Bool)
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
# normal form of `g`:

View File

@ -67,11 +67,13 @@ struct Homomorphism{Gr1,Gr2,I,W}
f,
source::AbstractFPGroup,
target::AbstractFPGroup;
check=true
check = true,
)
A = alphabet(source)
dct = Dict(i => convert(word_type(target), f(i, source, target))
for i in 1:length(A))
dct = Dict(
i => convert(word_type(target), f(i, source, target)) for
i in 1:length(A)
)
I = eltype(word_type(source))
W = word_type(target)
hom = new{typeof(source),typeof(target),I,W}(dct, source, target)
@ -79,7 +81,6 @@ struct Homomorphism{Gr1,Gr2,I,W}
if check
@assert hom(one(source)) == one(target)
for x in gens(source)
@assert hom(x^-1) == hom(x)^-1
for y in gens(source)
@ -111,4 +112,6 @@ function (h::Homomorphism)(g::AbstractFPGroupElement)
return h.target(w)
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.KnuthBendix
export SpecialLinearGroup, SymplecticGroup
export MatrixGroup, SpecialLinearGroup, SymplecticGroup
include("abstract.jl")
include("matrix_group.jl")
include("SLn.jl")
include("Spn.jl")

View File

@ -1,42 +1,35 @@
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
alphabet::A
gens::S
alphabet::Alphabet{S}
gens::Vector{S}
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)
return new{
N,
eltype(base_ring),
typeof(base_ring),
typeof(alphabet),
typeof(S)
}(base_ring, alphabet, S)
T = eltype(base_ring)
R = typeof(base_ring)
St = eltype(S)
return new{N,T,R,St}(base_ring, alphabet, S)
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} =
print(io, "special linear group of $N×$N matrices over $T")
function Base.show(io::IO, ::SpecialLinearGroup{N,T}) where {N,T}
return print(io, "SL{$N,$T}")
end
function Base.show(
io::IO,
::MIME"text/plain",
sl::Groups.AbstractFPGroupElement{<:SpecialLinearGroup{N}}
) where {N}
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))
SL::SpecialLinearGroup{N,T},
) where {N,T}
return print(io, "special linear group of $N×$N matrices over $T")
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")
struct SymplecticGroup{N,T,R,A,S} <: MatrixGroup{N,T}
struct SymplecticGroup{N,T,R,S} <: AbstractMatrixGroup{N,T}
base_ring::R
alphabet::A
gens::S
alphabet::Alphabet{S}
gens::Vector{S}
function SymplecticGroup{N}(base_ring) where {N}
S = symplectic_gens(N, eltype(base_ring))
alphabet = Alphabet(S)
return new{
N,
eltype(base_ring),
typeof(base_ring),
typeof(alphabet),
typeof(S)
}(base_ring, alphabet, S)
T = eltype(base_ring)
R = typeof(base_ring)
St = eltype(S)
return new{N,T,R,St}(base_ring, alphabet, S)
end
end
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(
io::IO,
::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))
function Base.show(io::IO, ::MIME"text/plain", ::SymplecticGroup{N}) where {N}
return print(io, "group of $N×$N symplectic matrices")
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!"))
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]
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]
@ -60,11 +55,16 @@ function _std_symplectic_form(m::AbstractMatrix)
n = r ÷ 2
𝕆 = zeros(eltype(m), n, n)
𝕀 = one(eltype(m)) * LinearAlgebra.I
Ω = [𝕆 -𝕀
𝕀 𝕆]
Ω = [
𝕆 -𝕀
𝕀 𝕆
]
return Ω
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
end

View File

@ -1,40 +1,89 @@
abstract type MatrixGroup{N,T} <: Groups.AbstractFPGroup end
const MatrixGroupElement{N,T} = Groups.AbstractFPGroupElement{<:MatrixGroup{N,T}}
abstract type AbstractMatrixGroup{N,T} <: Groups.AbstractFPGroup end
const MatrixGroupElement{N,T} =
Groups.AbstractFPGroupElement{<:AbstractMatrixGroup{N,T}}
Base.isone(g::MatrixGroupElement{N,T}) where {N,T} =
isone(word(g)) || matrix_repr(g) == LinearAlgebra.I
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)
function Base.isone(g::MatrixGroupElement{N,T}) where {N,T}
return isone(word(g)) || isone(matrix(g))
end
Base.size(m::MatrixGroupElement{N}) where {N} = (N, N)
Base.eltype(m::MatrixGroupElement{N,T}) where {N,T} = T
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(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
Groups.word(sl::MatrixGroupElement) = sl.word
Base.parent(sl::MatrixGroupElement) = sl.parent
Groups.alphabet(M::MatrixGroup) = M.alphabet
Groups.rewriting(M::MatrixGroup) = alphabet(M)
Groups.word(m::MatrixGroupElement) = m.word
Base.parent(m::MatrixGroupElement) = m.parent
Groups.alphabet(M::AbstractMatrixGroup) = M.alphabet
Groups.rewriting(M::AbstractMatrixGroup) = alphabet(M)
Base.hash(sl::MatrixGroupElement, h::UInt) =
hash(matrix_repr(sl), hash(parent(sl), h))
Base.hash(m::MatrixGroupElement, h::UInt) = hash(matrix(m), hash(parent(m), h))
function matrix_repr(m::MatrixGroupElement{N,T}) where {N,T}
function matrix(m::MatrixGroupElement{N,T}) where {N,T}
if isone(word(m))
return StaticArrays.SMatrix{N,N,T}(LinearAlgebra.I)
end
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
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(
rng::Random.AbstractRNG,
rs::Random.SamplerTrivial{<:MatrixGroup},
rs::Random.SamplerTrivial{<:AbstractMatrixGroup},
)
Mgroup = rs[]
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

View File

@ -2,25 +2,29 @@ struct ElementaryMatrix{N,T} <: Groups.GSymbol
i::Int
j::Int
val::T
ElementaryMatrix{N}(i, j, val=1) where {N} =
(@assert i j; new{N,typeof(val)}(i, j, val))
function ElementaryMatrix{N}(i, j, val = 1) where {N}
return (@assert i j; new{N,typeof(val)}(i, j, val))
end
end
function Base.show(io::IO, e::ElementaryMatrix)
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
Base.:(==)(e::ElementaryMatrix{N}, f::ElementaryMatrix{N}) where {N} =
e.i == f.i && e.j == f.j && e.val == f.val
function Base.:(==)(e::ElementaryMatrix{N}, f::ElementaryMatrix{N}) where {N}
return e.i == f.i && e.j == f.j && e.val == f.val
end
Base.hash(e::ElementaryMatrix, h::UInt) =
hash(typeof(e), hash((e.i, e.j, e.val), h))
function Base.hash(e::ElementaryMatrix, h::UInt)
return hash(typeof(e), hash((e.i, e.j, e.val), h))
end
Base.inv(e::ElementaryMatrix{N}) where {N} =
ElementaryMatrix{N}(e.i, e.j, -e.val)
function Base.inv(e::ElementaryMatrix{N}) where {N}
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[e.i, e.j] = e.val
x = StaticArrays.SMatrix{N,N}(m)

View File

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

View File

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

View File

@ -7,7 +7,7 @@ using Groups.MatrixGroups
S = gens(SL3Z)
union!(S, inv.(S))
_, sizes = Groups.wlmetric_ball(S, radius=4)
_, sizes = Groups.wlmetric_ball(S; radius = 4)
@test sizes == [13, 121, 883, 5455]
@ -20,9 +20,9 @@ using Groups.MatrixGroups
S = [w, r, 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]
_, sizes = Groups.wlmetric_ball_serial(S, radius=4)
_, sizes = Groups.wlmetric_ball_serial(S; radius = 4)
@test sizes == [7, 33, 141, 561]
Logging.with_logger(Logging.NullLogger()) do
@ -35,15 +35,18 @@ using Groups.MatrixGroups
end
end
x = w * inv(w) * r
@test length(word(x)) == 5
@test size(x) == (3, 3)
@test eltype(x) == Int8
@test contains(sprint(print, SL3Z), "special linear group of 3×3")
@test contains(sprint(show, MIME"text/plain"(), x), "SL{3,Int8} matrix:")
@test contains(sprint(show, SL3Z), "SL{3,Int8}")
@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 length(word(x)) == 3
@ -62,8 +65,6 @@ using Groups.MatrixGroups
end
end
@test contains(sprint(print, Sp6), "group of 6×6 symplectic matrices")
x = gens(Sp6, 1)
x *= inv(x) * gens(Sp6, 2)
@ -71,13 +72,50 @@ using Groups.MatrixGroups
@test size(x) == (6, 6)
@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 length(word(x)) == 1
for g in gens(Sp6)
@test MatrixGroups.issymplectic(MatrixGroups.matrix_repr(g))
@test MatrixGroups.issymplectic(MatrixGroups.matrix(g))
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