Compare commits
42 Commits
Author | SHA1 | Date |
---|---|---|
Marek Kaluba | 1fbd7b875b | |
Marek Kaluba | c13226a625 | |
Marek Kaluba | 7776ac4c6e | |
Marek Kaluba | 6ca9497dab | |
Marek Kaluba | f4d018f087 | |
Marek Kaluba | add9a6f287 | |
Marek Kaluba | ba6d58ec77 | |
Marek Kaluba | a33b871754 | |
Marek Kaluba | efbb4eada8 | |
Marek Kaluba | 148a472dd2 | |
Marek Kaluba | 5993cb328f | |
Marek Kaluba | 8137e40998 | |
Marek Kaluba | 866e431c1a | |
Marek Kaluba | 22cf6297a9 | |
Marek Kaluba | 1a51a87771 | |
Marek Kaluba | d385992e92 | |
Marek Kaluba | c8805d6890 | |
Marek Kaluba | 3260e66d37 | |
Marek Kaluba | eacb32af68 | |
Marek Kaluba | 126c8bbc22 | |
Marek Kaluba | 93a841359b | |
Marek Kaluba | 7230106bfc | |
Marek Kaluba | 751850568c | |
Marek Kaluba | 038fc29b81 | |
Marek Kaluba | c69eff1540 | |
Marek Kaluba | a1bc334fb2 | |
Marek Kaluba | 1f1e51917a | |
Marek Kaluba | 8bd3f7ede6 | |
Marek Kaluba | 448857ef03 | |
Marek Kaluba | af3913d085 | |
Marek Kaluba | 3cdef72977 | |
Marek Kaluba | 29af9659d9 | |
Marek Kaluba | a0d2186477 | |
Marek Kaluba | b52de81e06 | |
Marek Kaluba | 46c95dbb83 | |
Marek Kaluba | 40cd92e3c4 | |
Marek Kaluba | 116439d074 | |
Marek Kaluba | a380959614 | |
Marek Kaluba | f096a869b8 | |
Marek Kaluba | b7a6f4e952 | |
Marek Kaluba | 9006f8a4a1 | |
Marek Kaluba | 22c21706d8 |
|
@ -0,0 +1 @@
|
|||
../.JuliaFormatter.toml
|
|
@ -7,7 +7,11 @@ jobs:
|
|||
CompatHelper:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: JuliaRegistries/compathelper-action@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
ssh: ${{ secrets.DOCUMENTER_KEY }}
|
||||
- name: Pkg.add("CompatHelper")
|
||||
run: julia -e 'using Pkg; Pkg.add("CompatHelper")'
|
||||
- name: CompatHelper.main()
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }}
|
||||
run: julia -e 'using CompatHelper; CompatHelper.main()'
|
||||
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
name = "Groups"
|
||||
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
|
||||
authors = ["Marek Kaluba <kalmar@amu.edu.pl>"]
|
||||
version = "0.7.4"
|
||||
version = "0.8"
|
||||
|
||||
[deps]
|
||||
Folds = "41a02a25-b8f0-4f67-bc48-60067656b558"
|
||||
GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120"
|
||||
KnuthBendix = "c2604015-7b3d-4a30-8a26-9074551ec60a"
|
||||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
|
||||
PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420"
|
||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
|
||||
|
||||
[compat]
|
||||
Folds = "0.2.7"
|
||||
GroupsCore = "0.4"
|
||||
GroupsCore = "0.5"
|
||||
KnuthBendix = "0.4"
|
||||
OrderedCollections = "1"
|
||||
PermutationGroups = "0.3"
|
||||
PermutationGroups = "0.6"
|
||||
StaticArrays = "1"
|
||||
julia = "1.6"
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
module Groups
|
||||
|
||||
import Folds
|
||||
import Logging
|
||||
|
||||
using GroupsCore
|
||||
import GroupsCore.Random
|
||||
|
||||
import OrderedCollections: OrderedSet
|
||||
import Random
|
||||
|
||||
import KnuthBendix
|
||||
import KnuthBendix: AbstractWord, Alphabet, Word
|
||||
|
@ -14,7 +11,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
|
||||
|
||||
|
@ -23,6 +27,7 @@ include(joinpath("constructions", "constructions.jl"))
|
|||
import .Constructions
|
||||
|
||||
include("types.jl")
|
||||
include("rand.jl")
|
||||
include("hashing.jl")
|
||||
include("normalform.jl")
|
||||
include("autgroups.jl")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -14,26 +14,24 @@ end
|
|||
λ(i, j) = Transvection(:λ, i, j)
|
||||
|
||||
function Base.show(io::IO, t::Transvection)
|
||||
id = if t.id === :ϱ
|
||||
'ϱ'
|
||||
else # if t.id === :λ
|
||||
'λ'
|
||||
end
|
||||
print(io, id, subscriptify(t.i), '.', subscriptify(t.j))
|
||||
t.inv && print(io, "^-1")
|
||||
print(io, t.id, subscriptify(t.i), '.', subscriptify(t.j))
|
||||
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 +82,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 +90,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
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
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
|
||||
|
||||
struct AutomorphismGroup{G<:Group,T,RW,S} <: AbstractFPGroup
|
||||
mutable struct AutomorphismGroup{G<:Group,T,RW,S} <: AbstractFPGroup
|
||||
group::G
|
||||
gens::Vector{T}
|
||||
rw::RW
|
||||
|
@ -26,15 +26,18 @@ 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)
|
||||
hash(g) != hash(h) && return false
|
||||
end
|
||||
|
||||
length(word(g)) > 8 && normalform!(g)
|
||||
length(word(h)) > 8 && normalform!(h)
|
||||
normalform!(g)
|
||||
normalform!(h)
|
||||
|
||||
word(g) == word(h) && return true
|
||||
|
||||
|
@ -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,47 +130,55 @@ 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
|
||||
|
||||
struct LettersMap{T,A}
|
||||
indices_map::Dict{Int,T}
|
||||
struct LettersMap{W<:AbstractWord,A}
|
||||
indices_map::Dict{Int,W}
|
||||
A::A
|
||||
end
|
||||
|
||||
function LettersMap(a::FPGroupElement{<:AutomorphismGroup})
|
||||
dom = domain(a)
|
||||
@assert all(isone ∘ length ∘ word, dom)
|
||||
A = alphabet(first(dom))
|
||||
first_letters = first.(word.(dom))
|
||||
img = evaluate!(dom, a)
|
||||
if all(isone ∘ length ∘ word, dom)
|
||||
A = alphabet(first(dom))
|
||||
first_letters = first.(word.(dom))
|
||||
img = evaluate!(dom, a)
|
||||
|
||||
# (dom[i] → img[i] is a map from domain to images)
|
||||
# we need a map from alphabet indices → (gens, gens⁻¹) → images
|
||||
# here we do it for elements of the domain
|
||||
# (trusting it's a set of generators that define a)
|
||||
@assert length(dom) == length(img)
|
||||
# (dom[i] → img[i] is a map from domain to images)
|
||||
# we need a map from alphabet indices → (gens, gens⁻¹) → images
|
||||
# here we do it for elements of the domain
|
||||
# (trusting it's a set of generators that define a)
|
||||
@assert length(dom) == length(img)
|
||||
|
||||
indices_map = Dict(A[A[fl]] => word(im) for (fl, im) in zip(first_letters, img))
|
||||
# inverses of generators are dealt lazily in getindex
|
||||
indices_map =
|
||||
Dict(Int(fl) => word(im) for (fl, im) in zip(first_letters, img))
|
||||
# inverses of generators are dealt lazily in getindex
|
||||
else
|
||||
throw("LettersMap is not implemented for non-generators in domain")
|
||||
end
|
||||
|
||||
return LettersMap(indices_map, A)
|
||||
end
|
||||
|
||||
|
||||
function Base.getindex(lm::LettersMap, i::Integer)
|
||||
function Base.getindex(lm::LettersMap{W}, i::Integer) where {W}
|
||||
# here i is an index of an alphabet
|
||||
@boundscheck 1 ≤ i ≤ length(lm.A)
|
||||
|
||||
if !haskey(lm.indices_map, i)
|
||||
img = if haskey(lm.indices_map, inv(i, lm.A))
|
||||
inv(lm.indices_map[inv(i, lm.A)], lm.A)
|
||||
I = inv(i, lm.A)
|
||||
if haskey(lm.indices_map, I)
|
||||
img = inv(lm.indices_map[I], lm.A)
|
||||
lm.indices_map[i] = img
|
||||
else
|
||||
@warn "LetterMap: neither $i nor its inverse has assigned value"
|
||||
one(valtype(lm.indices_map))
|
||||
lm.indices_map[i] = W([i])
|
||||
lm.indices_map[I] = W([I])
|
||||
end
|
||||
lm.indices_map[i] = img
|
||||
end
|
||||
return lm.indices_map[i]
|
||||
end
|
||||
|
@ -164,9 +189,10 @@ function (a::FPGroupElement{<:AutomorphismGroup})(g::FPGroupElement)
|
|||
return parent(g)(img_w)
|
||||
end
|
||||
|
||||
evaluate(w::AbstractWord, lm::LettersMap) = evaluate!(one(w), w, lm)
|
||||
evaluate(w::AbstractWord, lm::LettersMap) = evaluate!(similar(w), w, lm)
|
||||
|
||||
function evaluate!(res::AbstractWord, w::AbstractWord, lm::LettersMap)
|
||||
resize!(res, 0)
|
||||
for i in w
|
||||
append!(res, lm[i])
|
||||
end
|
||||
|
|
|
@ -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,20 +53,12 @@ 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)
|
||||
|
||||
function GroupsCore.gens(G::DirectPower, i::Integer)
|
||||
k = ngens(G.group)
|
||||
ci = CartesianIndices((k, _nfold(G)))
|
||||
@boundscheck checkbounds(ci, i)
|
||||
r, c = Tuple(ci[i])
|
||||
tup = ntuple(j -> j == c ? gens(G.group, r) : one(G.group), _nfold(G))
|
||||
return DirectPowerElement(tup, G)
|
||||
end
|
||||
|
||||
function GroupsCore.gens(G::DirectPower)
|
||||
N = _nfold(G)
|
||||
S = gens(G.group)
|
||||
|
@ -73,24 +69,14 @@ end
|
|||
|
||||
Base.isfinite(G::DirectPower) = isfinite(G.group)
|
||||
|
||||
function Base.rand(
|
||||
rng::Random.AbstractRNG,
|
||||
rs::Random.SamplerTrivial{<:DirectPower},
|
||||
)
|
||||
G = rs[]
|
||||
return DirectPowerElement(rand(rng, G.group, _nfold(G)), G)
|
||||
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))
|
||||
|
||||
Base.inv(g::DirectPowerElement) = DirectPowerElement(inv.(g.elts), parent(g))
|
||||
|
||||
function Base.:(*)(g::DirectPowerElement, h::DirectPowerElement)
|
||||
|
@ -98,15 +84,61 @@ 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)))
|
||||
# to make sure that parents are never copied i.e.
|
||||
# g and deepcopy(g) share their parent
|
||||
Base.deepcopy_internal(G::DirectPower, ::IdDict) = G
|
||||
|
||||
################## Implementing Group Interface Done!
|
||||
|
||||
function GroupsCore.gens(G::DirectPower, i::Integer)
|
||||
k = ngens(G.group)
|
||||
ci = CartesianIndices((k, _nfold(G)))
|
||||
@boundscheck checkbounds(ci, i)
|
||||
r, c = Tuple(ci[i])
|
||||
tup = ntuple(j -> j == c ? gens(G.group, r) : one(G.group), _nfold(G))
|
||||
return DirectPowerElement(tup, G)
|
||||
end
|
||||
|
||||
# Overloading rand: the PRA of GroupsCore is known for not performing
|
||||
# well on direct sums
|
||||
function Random.Sampler(
|
||||
RNG::Type{<:Random.AbstractRNG},
|
||||
G::DirectPower,
|
||||
repetition::Random.Repetition = Val(Inf),
|
||||
)
|
||||
return Random.SamplerTrivial(G)
|
||||
end
|
||||
|
||||
function Base.rand(
|
||||
rng::Random.AbstractRNG,
|
||||
rs::Random.SamplerTrivial{<:DirectPower},
|
||||
)
|
||||
G = rs[]
|
||||
return DirectPowerElement(rand(rng, G.group, _nfold(G)), G)
|
||||
end
|
||||
|
||||
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)
|
||||
print(io, "( ")
|
||||
join(io, g.elts, ", ")
|
||||
return print(" )")
|
||||
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, ", "), " )")
|
||||
|
|
|
@ -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,21 +54,57 @@ 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
|
||||
|
||||
Base.isfinite(G::DirectProduct) = isfinite(G.first) && isfinite(G.last)
|
||||
|
||||
GroupsCore.parent(g::DirectProductElement) = g.parent
|
||||
|
||||
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))
|
||||
|
||||
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
|
||||
|
||||
# to make sure that parents are never copied i.e.
|
||||
# g and deepcopy(g) share their parent
|
||||
Base.deepcopy_internal(G::DirectProduct, ::IdDict) = G
|
||||
|
||||
################## Implementing Group Interface Done!
|
||||
|
||||
# Overloading rand: the PRA of GroupsCore is known for not performing
|
||||
# well on direct sums
|
||||
function Random.Sampler(
|
||||
RNG::Type{<:Random.AbstractRNG},
|
||||
G::DirectProduct,
|
||||
repetition::Random.Repetition = Val(Inf),
|
||||
)
|
||||
return Random.SamplerTrivial(G)
|
||||
end
|
||||
|
||||
function Base.rand(
|
||||
rng::Random.AbstractRNG,
|
||||
rs::Random.SamplerTrivial{<:DirectProduct},
|
||||
|
@ -73,30 +113,26 @@ function Base.rand(
|
|||
return DirectProductElement((rand(rng, G.first), rand(rng, G.last)), G)
|
||||
end
|
||||
|
||||
GroupsCore.parent(g::DirectProductElement) = g.parent
|
||||
|
||||
Base.:(==)(g::DirectProductElement, h::DirectProductElement) =
|
||||
(parent(g) === parent(h) && g.elts == h.elts)
|
||||
|
||||
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))
|
||||
|
||||
Base.inv(g::DirectProductElement) =
|
||||
DirectProductElement(inv.(g.elts), parent(g))
|
||||
|
||||
function Base.:(*)(g::DirectProductElement, h::DirectProductElement)
|
||||
@assert parent(g) === parent(h)
|
||||
return DirectProductElement(g.elts .* h.elts, parent(g))
|
||||
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
|
||||
|
||||
GroupsCore.order(::Type{I}, g::DirectProductElement) where {I<:Integer} =
|
||||
convert(I, lcm(order(I, first(g.elts)), order(I, last(g.elts))))
|
||||
|
||||
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)
|
||||
print(io, "( ")
|
||||
join(io, g.elts, ", ")
|
||||
return print(io, " )")
|
||||
end
|
||||
|
||||
# convienience:
|
||||
Base.@propagate_inbounds function Base.getindex(
|
||||
g::DirectProductElement,
|
||||
i::Integer,
|
||||
)
|
||||
return g.elts[i]
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import PermutationGroups: AbstractPermutationGroup, AbstractPerm, degree, SymmetricGroup
|
||||
import PermutationGroups as PG
|
||||
|
||||
"""
|
||||
WreathProduct(G::Group, P::AbstractPermutationGroup) <: Group
|
||||
|
@ -13,20 +13,20 @@ product is defined as
|
|||
where `m^σ` denotes the action (from the right) of the permutation `σ` on
|
||||
`d`-tuples of elements from `G`.
|
||||
"""
|
||||
struct WreathProduct{DP<:DirectPower,PGr<:AbstractPermutationGroup} <:
|
||||
struct WreathProduct{DP<:DirectPower,PGr<:PG.AbstractPermutationGroup} <:
|
||||
GroupsCore.Group
|
||||
N::DP
|
||||
P::PGr
|
||||
|
||||
function WreathProduct(G::Group, P::AbstractPermutationGroup)
|
||||
N = DirectPower{degree(P)}(G)
|
||||
function WreathProduct(G::Group, P::PG.AbstractPermutationGroup)
|
||||
N = DirectPower{PG.AP.degree(P)}(G)
|
||||
return new{typeof(N),typeof(P)}(N, P)
|
||||
end
|
||||
end
|
||||
|
||||
struct WreathProductElement{
|
||||
DPEl<:DirectPowerElement,
|
||||
PEl<:AbstractPerm,
|
||||
PEl<:PG.AP.AbstractPermutation,
|
||||
Wr<:WreathProduct,
|
||||
} <: GroupsCore.GroupElement
|
||||
n::DPEl
|
||||
|
@ -35,32 +35,36 @@ struct WreathProductElement{
|
|||
|
||||
function WreathProductElement(
|
||||
n::DirectPowerElement,
|
||||
p::AbstractPerm,
|
||||
p::PG.AP.AbstractPermutation,
|
||||
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))
|
||||
ab, st = res
|
||||
(a, b) = ab
|
||||
elt = WreathProductElement(a, b, G)
|
||||
return elt, (itr, st)
|
||||
end
|
||||
|
||||
function Base.iterate(G::WreathProduct, state)
|
||||
itr, st = state.iterator, state.state
|
||||
itr, st = state
|
||||
res = iterate(itr, st)
|
||||
res === nothing && return nothing
|
||||
elt = WreathProductElement(first(res)..., G)
|
||||
return elt, (iterator=itr, state=last(res))
|
||||
(a::eltype(G.N), b::eltype(G.P)), st = res
|
||||
elt = WreathProductElement(a, b, G)
|
||||
return elt, (itr, st)
|
||||
end
|
||||
|
||||
function Base.IteratorSize(::Type{<:WreathProduct{DP,PGr}}) where {DP,PGr}
|
||||
|
@ -78,8 +82,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)]
|
||||
|
@ -89,33 +94,22 @@ end
|
|||
|
||||
Base.isfinite(G::WreathProduct) = isfinite(G.N) && isfinite(G.P)
|
||||
|
||||
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
|
||||
|
||||
Base.hash(g::WreathProductElement, h::UInt) =
|
||||
hash(g.n, hash(g.p, hash(g.parent, h)))
|
||||
|
||||
function Base.deepcopy_internal(g::WreathProductElement, stackdict::IdDict)
|
||||
return WreathProductElement(
|
||||
Base.deepcopy_internal(g.n, stackdict),
|
||||
Base.deepcopy_internal(g.p, stackdict),
|
||||
parent(g),
|
||||
)
|
||||
function Base.:(==)(g::WreathProductElement, h::WreathProductElement)
|
||||
return parent(g) === parent(h) && g.n == h.n && g.p == h.p
|
||||
end
|
||||
|
||||
_act(p::AbstractPerm, n::DirectPowerElement) =
|
||||
DirectPowerElement(n.elts^p, parent(n))
|
||||
function Base.hash(g::WreathProductElement, h::UInt)
|
||||
return hash(g.n, hash(g.p, hash(g.parent, h)))
|
||||
end
|
||||
|
||||
function _act(p::PG.AP.AbstractPermutation, n::DirectPowerElement)
|
||||
return DirectPowerElement(
|
||||
ntuple(i -> n.elts[i^p], length(n.elts)),
|
||||
parent(n),
|
||||
)
|
||||
end
|
||||
|
||||
function Base.inv(g::WreathProductElement)
|
||||
pinv = inv(g.p)
|
||||
|
@ -127,10 +121,36 @@ function Base.:(*)(g::WreathProductElement, h::WreathProductElement)
|
|||
return WreathProductElement(g.n * _act(g.p, h.n), g.p * h.p, parent(g))
|
||||
end
|
||||
|
||||
# to make sure that parents are never copied i.e.
|
||||
# g and deepcopy(g) share their parent
|
||||
Base.deepcopy_internal(G::WreathProduct, ::IdDict) = G
|
||||
|
||||
################## Implementing Group Interface Done!
|
||||
|
||||
# Overloading rand: the PRA of GroupsCore is known for not performing
|
||||
# well on direct sums
|
||||
function Random.Sampler(
|
||||
RNG::Type{<:Random.AbstractRNG},
|
||||
G::WreathProduct,
|
||||
repetition::Random.Repetition = Val(Inf),
|
||||
)
|
||||
return Random.SamplerTrivial(G)
|
||||
end
|
||||
|
||||
function Base.rand(
|
||||
rng::Random.AbstractRNG,
|
||||
rs::Random.SamplerTrivial{<:WreathProduct},
|
||||
)
|
||||
G = rs[]
|
||||
return WreathProductElement(rand(rng, G.N), rand(rng, G.P), G)
|
||||
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)")
|
||||
Base.show(io::IO, g::WreathProductElement) = print(io, "( $(g.n)≀$(g.p) )")
|
||||
function Base.show(io::IO, G::WreathProduct)
|
||||
return print(io, "Wreath product of ", G.N.group, " by ", G.P)
|
||||
end
|
||||
|
||||
Base.copy(g::WreathProductElement) = WreathProductElement(g.n, g.p, parent(g))
|
||||
function Base.show(io::IO, g::WreathProductElement)
|
||||
return print(io, "( ", g.n, "≀", g.p, " )")
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
## Hashing
|
||||
|
||||
equality_data(g::AbstractFPGroupElement) = (normalform!(g); word(g))
|
||||
equality_data(g::AbstractFPGroupElement) = word(g)
|
||||
|
||||
bitget(h::UInt, n::Int) = Bool((h & (1 << n)) >> n)
|
||||
bitclear(h::UInt, n::Int) = h & ~(1 << n)
|
||||
|
@ -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`:
|
||||
|
@ -33,6 +37,7 @@ function _update_savedhash!(g::AbstractFPGroupElement, data)
|
|||
end
|
||||
|
||||
function Base.hash(g::AbstractFPGroupElement, h::UInt)
|
||||
g = normalform!(g)
|
||||
_isvalidhash(g) || _update_savedhash!(g, equality_data(g))
|
||||
return hash(g.savedhash >> count_ones(__BITFLAGS_MASK), h)
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import OrderedCollections: OrderedSet
|
||||
|
||||
mutable struct FPGroupIter{S,T,GEl}
|
||||
seen::S
|
||||
seen_iter_state::T
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
@ -18,8 +23,8 @@ 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)")
|
||||
print(io, s.symbol, i, '.', j)
|
||||
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)
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
struct MatrixElt{N,T,N²} <: Groups.GSymbol
|
||||
id::Symbol
|
||||
inv::Bool
|
||||
mat::StaticArrays.SMatrix{N,N,T,N²}
|
||||
|
||||
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
|
|
@ -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)
|
|
@ -2,8 +2,10 @@
|
|||
normalform!(g::FPGroupElement)
|
||||
Compute the normal form of `g`, possibly modifying `g` in-place.
|
||||
"""
|
||||
@inline function normalform!(g::AbstractFPGroupElement)
|
||||
isnormalform(g) && return g
|
||||
@inline function normalform!(g::AbstractFPGroupElement; force = false)
|
||||
if !force
|
||||
isnormalform(g) && return g
|
||||
end
|
||||
|
||||
let w = one(word(g))
|
||||
w = normalform!(w, g)
|
||||
|
@ -36,9 +38,9 @@ end
|
|||
|
||||
"""
|
||||
normalform!(res::AbstractWord, g::FPGroupElement)
|
||||
Append the normal form of `g` to word `res`, modifying `res` in place.
|
||||
Write the normal form of `g` to word `res`, modifying `res` in place.
|
||||
|
||||
Defaults to the rewriting in the free group.
|
||||
The particular implementation of the normal form depends on `parent(g)`.
|
||||
"""
|
||||
@inline function normalform!(res::AbstractWord, g::AbstractFPGroupElement)
|
||||
isone(res) && isnormalform(g) && return append!(res, word(g))
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
"""
|
||||
PoissonSampler
|
||||
For a finitely presented group PoissonSampler returns group elements represented
|
||||
by words of length at most `R ~ Poisson(λ)` chosen uniformly at random.
|
||||
|
||||
For finitely presented groups the Product Replacement Algorithm
|
||||
(see `PRASampler` from `GroupsCore.jl`) doesn't make much sense due to
|
||||
overly long words it produces. We therefore resort to a pseudo-random method,
|
||||
where a word `w` of length `R` is chosen uniformly at random among all
|
||||
words of length `R` where `R` follows the Poisson distribution.
|
||||
|
||||
!!! note
|
||||
Due to the choice of the parameters (`λ=8`) and the floating point
|
||||
arithmetic the sampler will always return group elements represented by
|
||||
words of length at most `42`.
|
||||
"""
|
||||
struct PoissonSampler{G,T} <: Random.Sampler{T}
|
||||
group::G
|
||||
λ::Int
|
||||
end
|
||||
|
||||
function PoissonSampler(G::AbstractFPGroup; λ)
|
||||
return PoissonSampler{typeof(G),eltype(G)}(G, λ)
|
||||
end
|
||||
|
||||
function __poisson_invcdf(val; λ)
|
||||
# __poisson_pdf(k, λ) = λ^k * ℯ^-λ / factorial(k)
|
||||
# pdf = ntuple(k -> __poisson_pdf(k - 1, λ), 21)
|
||||
# cdf = accumulate(+, pdf)
|
||||
# radius = something(findfirst(>(val), cdf) - 1, 0)
|
||||
# this is the iterative version:
|
||||
pdf = ℯ^-λ
|
||||
cdf = pdf
|
||||
k = 0
|
||||
while cdf < val
|
||||
k += 1
|
||||
pdf = pdf * λ / k
|
||||
cdf += pdf
|
||||
end
|
||||
return k
|
||||
end
|
||||
|
||||
function Random.rand(rng::Random.AbstractRNG, sampler::PoissonSampler)
|
||||
R = __poisson_invcdf(rand(rng); λ = sampler.λ)
|
||||
|
||||
G = sampler.group
|
||||
n = length(alphabet(G))
|
||||
W = word_type(G)
|
||||
T = eltype(W)
|
||||
|
||||
letters = rand(rng, T(1):T(n), R)
|
||||
word = W(letters, false)
|
||||
return G(word)
|
||||
end
|
||||
|
||||
function Random.Sampler(
|
||||
RNG::Type{<:Random.AbstractRNG},
|
||||
G::AbstractFPGroup,
|
||||
repetition::Random.Repetition = Val(Inf),
|
||||
)
|
||||
return PoissonSampler(G; λ = 8)
|
||||
end
|
166
src/types.jl
166
src/types.jl
|
@ -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,48 +63,45 @@ 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)]
|
||||
|
||||
# TODO: ProductReplacementAlgorithm
|
||||
function Base.rand(
|
||||
rng::Random.AbstractRNG,
|
||||
rs::Random.SamplerTrivial{<:AbstractFPGroup},
|
||||
)
|
||||
l = rand(10:100)
|
||||
G = rs[]
|
||||
nletters = length(alphabet(G))
|
||||
return FPGroupElement(word_type(G)(rand(1:nletters, l)), G)
|
||||
function GroupsCore.gens(G::AbstractFPGroup)
|
||||
return [gens(G, i) for i in 1:GroupsCore.ngens(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
|
||||
|
||||
abstract type AbstractFPGroupElement{Gr} <: GroupElement end
|
||||
|
||||
Base.copy(g::AbstractFPGroupElement) = one(g) * g
|
||||
word(f::AbstractFPGroupElement) = f.word
|
||||
|
||||
mutable struct FPGroupElement{Gr<:AbstractFPGroup,W<:AbstractWord} <:
|
||||
AbstractFPGroupElement{Gr}
|
||||
word::W
|
||||
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, …}")
|
||||
|
||||
word(f::AbstractFPGroupElement) = f.word
|
||||
function Base.copy(f::FPGroupElement)
|
||||
return FPGroupElement(copy(word(f)), parent(f), f.savedhash)
|
||||
end
|
||||
|
||||
#convenience
|
||||
KnuthBendix.alphabet(g::AbstractFPGroupElement) = alphabet(parent(g))
|
||||
|
@ -124,12 +119,33 @@ function Base.:(==)(g::AbstractFPGroupElement, h::AbstractFPGroupElement)
|
|||
@boundscheck @assert parent(g) === parent(h)
|
||||
normalform!(g)
|
||||
normalform!(h)
|
||||
# I. compare hashes of the normalform
|
||||
# II. compare some data associated to FPGroupElement,
|
||||
# e.g. word, image of the domain etc.
|
||||
hash(g) != hash(h) && return false
|
||||
return equality_data(g) == equality_data(h)
|
||||
equality_data(g) == equality_data(h) && return true # compares
|
||||
|
||||
# if this failed it is still possible that the words together can be
|
||||
# rewritten even further, so we
|
||||
# 1. rewrite word(g⁻¹·h) w.r.t. rewriting(parent(g))
|
||||
# 2. check if the result is empty
|
||||
G = parent(g)
|
||||
|
||||
g⁻¹h = append!(inv(word(g), alphabet(G)), word(h))
|
||||
# similar + empty preserve the storage size
|
||||
# saves some re-allocations if res does not represent id
|
||||
res = similar(word(g))
|
||||
resize!(res, 0)
|
||||
res = KnuthBendix.rewrite!(res, g⁻¹h, rewriting(G))
|
||||
return isone(res)
|
||||
end
|
||||
|
||||
function Base.deepcopy_internal(g::FPGroupElement, stackdict::IdDict)
|
||||
return FPGroupElement(copy(word(g)), parent(g), g.savedhash)
|
||||
haskey(stackdict, g) && return stackdict[g]
|
||||
cw = Base.deepcopy_internal(word(g), stackdict)
|
||||
h = FPGroupElement(cw, parent(g), g.savedhash)
|
||||
stackdict[g] = h
|
||||
return h
|
||||
end
|
||||
|
||||
function Base.inv(g::GEl) where {GEl<:AbstractFPGroupElement}
|
||||
|
@ -139,14 +155,26 @@ end
|
|||
|
||||
function Base.:(*)(g::GEl, h::GEl) where {GEl<:AbstractFPGroupElement}
|
||||
@boundscheck @assert parent(g) === parent(h)
|
||||
return GEl(word(g) * word(h), parent(g))
|
||||
A = alphabet(parent(g))
|
||||
k = 0
|
||||
while k + 1 ≤ min(length(word(g)), length(word(h)))
|
||||
if inv(word(g)[end-k], A) == word(h)[k+1]
|
||||
k += 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
w = @view(word(g)[1:end-k]) * @view(word(h)[k+1:end])
|
||||
res = GEl(w, parent(g))
|
||||
return res
|
||||
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)))
|
||||
|
@ -164,12 +192,17 @@ struct FreeGroup{T,O} <: AbstractFPGroup
|
|||
end
|
||||
end
|
||||
|
||||
FreeGroup(gens, A::Alphabet) = FreeGroup(gens, KnuthBendix.LenLex(A))
|
||||
function FreeGroup(n::Integer)
|
||||
symbols =
|
||||
collect(Iterators.flatten((Symbol(:f, i), Symbol(:F, i)) for i in 1:n))
|
||||
inverses = collect(Iterators.flatten((2i, 2i - 1) for i in 1:n))
|
||||
return FreeGroup(Alphabet(symbols, inverses))
|
||||
end
|
||||
|
||||
function FreeGroup(A::Alphabet)
|
||||
@boundscheck @assert all(
|
||||
KnuthBendix.hasinverse(l, A) for l in A
|
||||
)
|
||||
FreeGroup(A::Alphabet) = FreeGroup(KnuthBendix.LenLex(A))
|
||||
|
||||
function __group_gens(A::Alphabet)
|
||||
@boundscheck @assert all(KnuthBendix.hasinverse(l, A) for l in A)
|
||||
gens = Vector{eltype(A)}()
|
||||
invs = Vector{eltype(A)}()
|
||||
for l in A
|
||||
|
@ -177,24 +210,17 @@ function FreeGroup(A::Alphabet)
|
|||
push!(gens, l)
|
||||
push!(invs, inv(l, A))
|
||||
end
|
||||
|
||||
return FreeGroup(gens, A)
|
||||
return gens
|
||||
end
|
||||
|
||||
function FreeGroup(n::Integer)
|
||||
symbols = Symbol[]
|
||||
inverses = Int[]
|
||||
sizehint!(symbols, 2n)
|
||||
sizehint!(inverses, 2n)
|
||||
for i in 1:n
|
||||
push!(symbols, Symbol(:f, i), Symbol(:F, i))
|
||||
push!(inverses, 2i, 2i - 1)
|
||||
end
|
||||
return FreeGroup(symbols[1:2:2n], Alphabet(symbols, inverses))
|
||||
function FreeGroup(O::KnuthBendix.WordOrdering)
|
||||
grp_gens = __group_gens(alphabet(O))
|
||||
return FreeGroup(grp_gens, O)
|
||||
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 +231,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 +249,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
|
||||
|
@ -237,10 +264,18 @@ function FPGroup(
|
|||
end
|
||||
|
||||
function Base.show(io::IO, ::MIME"text/plain", G::FPGroup)
|
||||
print(io, "Finitely presented group generated by:\n\t{")
|
||||
Base.print_array(io, permutedims(gens(G)))
|
||||
println(io, " },")
|
||||
println(io, "subject to relations:")
|
||||
println(
|
||||
io,
|
||||
"Finitely presented group generated by $(ngens(G)) element",
|
||||
ngens(G) > 1 ? 's' : "",
|
||||
": ",
|
||||
)
|
||||
join(io, gens(G), ", ", ", and ")
|
||||
println(
|
||||
io,
|
||||
"\n subject to relation",
|
||||
length(relations(G)) > 1 ? 's' : "",
|
||||
)
|
||||
return Base.print_array(io, relations(G))
|
||||
end
|
||||
|
||||
|
@ -253,8 +288,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
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""
|
||||
wlmetric_ball(S::AbstractVector{<:GroupElem}
|
||||
[, center=one(first(S)); radius=2, op=*, threading=true])
|
||||
[, center=one(first(S)); radius=2, op=*])
|
||||
Compute metric ball as a list of elements of non-decreasing length, given the
|
||||
word-length metric on the group generated by `S`. The ball is centered at `center`
|
||||
(by default: the identity element). `radius` and `op` keywords specify the
|
||||
|
@ -8,41 +8,29 @@ 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 = *,
|
||||
) where {T}
|
||||
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}
|
||||
@assert radius >= 1
|
||||
old = union!([center], [center * s for s in S])
|
||||
return _wlmetric_ball(S, old, radius, op, collect, unique!)
|
||||
end
|
||||
|
||||
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)
|
||||
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,
|
||||
new = collect(
|
||||
(g = op(o, s); hash(g); g)
|
||||
for o in @view(old[sizes[end-1]:end]) for s in S
|
||||
)
|
||||
|
||||
append!(old, new)
|
||||
unique(old)
|
||||
ball = [center]
|
||||
sizes = [1]
|
||||
if radius ≤ 0
|
||||
return ball, sizes[2:end]
|
||||
else
|
||||
ball = union!(ball, [center * s for s in S])
|
||||
push!(sizes, length(ball))
|
||||
if radius == 1
|
||||
return ball, sizes[2:end]
|
||||
else
|
||||
for _ in 2:radius
|
||||
new = collect(
|
||||
op(o, s) for o in @view(ball[sizes[end-1]:end]) for s in S
|
||||
)
|
||||
ball = union!(ball, new)
|
||||
push!(sizes, length(ball))
|
||||
end
|
||||
end
|
||||
push!(sizes, length(old))
|
||||
return ball, sizes[2:end]
|
||||
end
|
||||
return old, sizes[2:end]
|
||||
# return wlmetric_ball_serial(S, center; radius = radius, op = op)
|
||||
end
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
|
||||
)
|
||||
|
||||
F4 = FreeGroup([:a, :b, :c, :d], A4)
|
||||
F4 = FreeGroup(A4)
|
||||
a, b, c, d = gens(F4)
|
||||
D = ntuple(i -> gens(F4, i), 4)
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
@test contains(sprint(print, π₁Σ), "surface")
|
||||
|
||||
Groups.PermRightAut(p::Perm) = Groups.PermRightAut(p.d)
|
||||
Groups.PermRightAut(p::Perm) = Groups.PermRightAut([i^p for i in 1:2genus])
|
||||
# Groups.PermLeftAut(p::Perm) = Groups.PermLeftAut(p.d)
|
||||
autπ₁Σ = let autπ₁Σ = AutomorphismGroup(π₁Σ)
|
||||
pauts = let p = perm"(1,3,5)(2,4,6)"
|
||||
|
@ -50,8 +50,9 @@
|
|||
@test π₁Σ.(word.(z)) == Groups.domain(first(S))
|
||||
d = Groups.domain(first(S))
|
||||
p = perm"(1,3,5)(2,4,6)"
|
||||
@test Groups.evaluate!(deepcopy(d), τ) == d^inv(p)
|
||||
@test Groups.evaluate!(deepcopy(d), τ^2) == d^p
|
||||
@test Groups.evaluate!(deepcopy(d), τ) ==
|
||||
ntuple(i -> d[i^inv(p)], length(d))
|
||||
@test Groups.evaluate!(deepcopy(d), τ^2) == ntuple(i -> d[i^p], length(d))
|
||||
|
||||
E, sizes = Groups.wlmetric_ball(S, radius=3)
|
||||
@test sizes == [49, 1813, 62971]
|
||||
|
|
|
@ -5,14 +5,18 @@ using Groups
|
|||
|
||||
function wl_ball(F; radius::Integer)
|
||||
g, state = iterate(F)
|
||||
while length(word(g)) <= radius
|
||||
sizes = Int[]
|
||||
while length(sizes) ≤ radius
|
||||
res = iterate(F, state)
|
||||
isnothing(res) && break
|
||||
g, state = res
|
||||
if length(word(g)) > length(sizes)
|
||||
push!(sizes, length(state.seen) - 1)
|
||||
end
|
||||
end
|
||||
elts = collect(state.seen)
|
||||
elts = resize!(elts, length(elts)-1)
|
||||
return elts
|
||||
resize!(elts, sizes[end] - 1)
|
||||
return elts, sizes[2:end]
|
||||
end
|
||||
|
||||
@testset "Benchmarks" begin
|
||||
|
@ -25,21 +29,16 @@ end
|
|||
let G = FN
|
||||
S = unique([gens(G); inv.(gens(G))])
|
||||
|
||||
sizes1 = last(Groups.wlmetric_ball(S, radius=R, threading=false))
|
||||
sizes2 = last(Groups.wlmetric_ball(S, radius=R, threading=true))
|
||||
|
||||
l = length(wl_ball(G, radius=R))
|
||||
sizes1 = last(Groups.wlmetric_ball(S; radius = R))
|
||||
sizes2 = last(wl_ball(G; radius = R))
|
||||
|
||||
@test sizes1 == sizes2
|
||||
@test last(sizes1) == l
|
||||
|
||||
@info "Ball of radius $R in $(parent(first(S)))" sizes=sizes1
|
||||
@info "Ball of radius $R in $(parent(first(S)))" sizes = sizes1
|
||||
@info "serial"
|
||||
@time Groups.wlmetric_ball(S, radius=R, threading=false)
|
||||
@info "threaded"
|
||||
@time Groups.wlmetric_ball(S, radius=R, threading=true)
|
||||
@time Groups.wlmetric_ball(S, radius = R)
|
||||
@info "iteration"
|
||||
@time wl_ball(G, radius=R)
|
||||
@time wl_ball(G, radius = R)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -51,21 +50,16 @@ end
|
|||
let G = SAutFN
|
||||
S = unique([gens(G); inv.(gens(G))])
|
||||
|
||||
sizes1 = last(Groups.wlmetric_ball(S, radius=R, threading=false))
|
||||
sizes2 = last(Groups.wlmetric_ball(S, radius=R, threading=true))
|
||||
|
||||
l = length(wl_ball(G, radius=R))
|
||||
sizes1 = last(Groups.wlmetric_ball(S; radius = R))
|
||||
sizes2 = last(wl_ball(G; radius = R))
|
||||
|
||||
@test sizes1 == sizes2
|
||||
@test last(sizes1) == l
|
||||
|
||||
@info "Ball of radius $R in $(parent(first(S)))" sizes=sizes1
|
||||
@info "Ball of radius $R in $(parent(first(S)))" sizes = sizes1
|
||||
@info "serial"
|
||||
@time Groups.wlmetric_ball(S, radius=R, threading=false)
|
||||
@info "threaded"
|
||||
@time Groups.wlmetric_ball(S, radius=R, threading=true)
|
||||
@time Groups.wlmetric_ball(S, radius = R)
|
||||
@info "iteration"
|
||||
@time wl_ball(G, radius=R)
|
||||
@time wl_ball(G, radius = R)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
@test FreeGroup(A) isa FreeGroup
|
||||
@test sprint(show, FreeGroup(A)) == "free group on 3 generators"
|
||||
|
||||
F = FreeGroup([:a, :b, :c], A)
|
||||
F = FreeGroup([:a, :b, :c], Groups.KnuthBendix.LenLex(A))
|
||||
@test sprint(show, F) == "free group on 3 generators"
|
||||
|
||||
a, b, c = gens(F)
|
||||
|
@ -14,7 +14,8 @@
|
|||
G = FPGroup(F, [a * b => b * a, a * c => c * a, b * c => c * b])
|
||||
|
||||
@test G isa FPGroup
|
||||
@test sprint(show, G) == "⟨ a b c | \n\t a*b => b*a a*c => c*a b*c => c*b ⟩"
|
||||
@test sprint(show, G) ==
|
||||
"⟨ a b c | \n\t a*b => b*a a*c => c*a b*c => c*b ⟩"
|
||||
@test rand(G) isa FPGroupElement
|
||||
|
||||
f = a * c * b
|
||||
|
@ -40,7 +41,7 @@
|
|||
end
|
||||
|
||||
# quotient of G
|
||||
H = FPGroup(G, [aG^2 => cG, bG * cG => aG], max_rules=200)
|
||||
H = FPGroup(G, [aG^2 => cG, bG * cG => aG]; max_rules = 200)
|
||||
|
||||
h = H(word(g))
|
||||
|
||||
|
@ -48,15 +49,21 @@
|
|||
@test_throws AssertionError h == g
|
||||
@test_throws MethodError h * g
|
||||
|
||||
H′ = FPGroup(G, [aG^2 => cG, bG * cG => aG], max_rules=200)
|
||||
H′ = FPGroup(G, [aG^2 => cG, bG * cG => aG]; max_rules = 200)
|
||||
@test_throws AssertionError one(H) == one(H′)
|
||||
|
||||
Groups.normalform!(h)
|
||||
@test h == H([5])
|
||||
|
||||
@test_logs (:warn, "using generic isfiniteorder(::AbstractFPGroupElement): the returned `false` might be wrong") isfiniteorder(h)
|
||||
@test_logs (
|
||||
:warn,
|
||||
"using generic isfiniteorder(::AbstractFPGroupElement): the returned `false` might be wrong",
|
||||
) isfiniteorder(h)
|
||||
|
||||
@test_logs (:warn, "using generic isfinite(::AbstractFPGroup): the returned `false` might be wrong") isfinite(H)
|
||||
@test_logs (
|
||||
:warn,
|
||||
"using generic isfinite(::AbstractFPGroup): the returned `false` might be wrong",
|
||||
) isfinite(H)
|
||||
|
||||
Logging.with_logger(Logging.NullLogger()) do
|
||||
@testset "GroupsCore conformance: H" begin
|
||||
|
@ -64,4 +71,24 @@
|
|||
test_GroupElement_interface(rand(H, 2)...)
|
||||
end
|
||||
end
|
||||
|
||||
@testset "hash/normalform #28" begin
|
||||
function cyclic_group(n::Integer)
|
||||
A = Alphabet([:a, :A], [2, 1])
|
||||
F = FreeGroup(A)
|
||||
a, = Groups.gens(F)
|
||||
e = one(F)
|
||||
Cₙ = FPGroup(F, [a^n => e])
|
||||
|
||||
return Cₙ
|
||||
end
|
||||
|
||||
n = 15
|
||||
G = cyclic_group(n)
|
||||
ball, sizes = Groups.wlmetric_ball(gens(G); radius = n)
|
||||
@test first(sizes) == 2
|
||||
@test last(sizes) == n
|
||||
|
||||
@test Set(ball) == Set([first(gens(G))^i for i in 0:n-1])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@testset "FreeGroup" begin
|
||||
|
||||
A3 = Alphabet([:a, :b, :c, :A, :B, :C], [4,5,6,1,2,3])
|
||||
F3 = FreeGroup([:a, :b, :c], A3)
|
||||
F3 = FreeGroup(A3)
|
||||
@test F3 isa FreeGroup
|
||||
|
||||
@test gens(F3) isa Vector
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
@testset "GroupConstructions" begin
|
||||
|
||||
symmetric_group(n) = PermGroup(perm"(1,2)", Perm([2:n; 1]))
|
||||
|
||||
@testset "DirectProduct" begin
|
||||
GH =
|
||||
let G = PermutationGroups.SymmetricGroup(3),
|
||||
H = PermutationGroups.SymmetricGroup(4)
|
||||
let G = symmetric_group(3), H = symmetric_group(4)
|
||||
|
||||
Groups.Constructions.DirectProduct(G, H)
|
||||
end
|
||||
|
@ -17,7 +18,7 @@
|
|||
|
||||
@testset "DirectPower" begin
|
||||
GGG = Groups.Constructions.DirectPower{3}(
|
||||
PermutationGroups.SymmetricGroup(3),
|
||||
symmetric_group(3),
|
||||
)
|
||||
test_Group_interface(GGG)
|
||||
test_GroupElement_interface(rand(GGG, 2)...)
|
||||
|
@ -28,8 +29,7 @@
|
|||
end
|
||||
@testset "WreathProduct" begin
|
||||
W =
|
||||
let G = PermutationGroups.SymmetricGroup(2),
|
||||
P = PermutationGroups.SymmetricGroup(4)
|
||||
let G = symmetric_group(2), P = symmetric_group(4)
|
||||
|
||||
Groups.Constructions.WreathProduct(G, P)
|
||||
end
|
||||
|
|
|
@ -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,7 @@ using Groups.MatrixGroups
|
|||
|
||||
S = [w, r, s]
|
||||
S = unique([S; inv.(S)])
|
||||
_, 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(S; radius = 4)
|
||||
@test sizes == [7, 33, 141, 561]
|
||||
|
||||
Logging.with_logger(Logging.NullLogger()) do
|
||||
|
@ -35,15 +33,18 @@ using Groups.MatrixGroups
|
|||
end
|
||||
end
|
||||
|
||||
x = w * inv(SL3Z(word(w)[end:end])) * r
|
||||
|
||||
x = w * inv(w) * r
|
||||
|
||||
@test length(word(x)) == 5
|
||||
@test length(word(x)) == length(word(r))
|
||||
@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,22 +63,57 @@ using Groups.MatrixGroups
|
|||
end
|
||||
end
|
||||
|
||||
@test contains(sprint(print, Sp6), "group of 6×6 symplectic matrices")
|
||||
x = gens(Sp6, 1) * gens(Sp6, 2)^2
|
||||
x *= inv(gens(Sp6, 2)^2) * gens(Sp6, 3)
|
||||
|
||||
x = gens(Sp6, 1)
|
||||
x *= inv(x) * gens(Sp6, 2)
|
||||
|
||||
@test length(word(x)) == 3
|
||||
@test length(word(x)) == 2
|
||||
@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)) == 2
|
||||
|
||||
for g in gens(Sp6)
|
||||
@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) * gens(G, 2)^3
|
||||
x *= gens(G, 2)^-3
|
||||
|
||||
@test length(word(x)) == 1
|
||||
@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
|
||||
|
||||
for g in gens(Sp6)
|
||||
@test MatrixGroups.issymplectic(MatrixGroups.matrix_repr(g))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue