1
0
mirror of https://github.com/kalmarek/Groups.jl.git synced 2024-12-01 01:25:27 +01:00

Merge pull request #24 from kalmarek/mk/update_to_KB_0.4

update to KnuthBendix-0.4
This commit is contained in:
Marek Kaluba 2022-10-14 19:36:51 +02:00 committed by GitHub
commit 161c146642
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 440 additions and 399 deletions

13
.github/workflows/CompatHelper.yml vendored Normal file
View File

@ -0,0 +1,13 @@
name: CompatHelper
on:
schedule:
- cron: 0 0 * * *
workflow_dispatch:
jobs:
CompatHelper:
runs-on: ubuntu-latest
steps:
- uses: JuliaRegistries/compathelper-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
ssh: ${{ secrets.DOCUMENTER_KEY }}

View File

@ -4,6 +4,11 @@ on:
types: types:
- created - created
workflow_dispatch: workflow_dispatch:
inputs:
lookback:
default: 3
permissions:
contents: write
jobs: jobs:
TagBot: TagBot:
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'

View File

@ -1,11 +1,7 @@
name: CI name: CI
on: on:
push: - pull_request
branches: - push
- master
pull_request:
branches:
- master
jobs: jobs:
test: test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
@ -22,21 +18,31 @@ jobs:
- windows-latest - windows-latest
arch: arch:
- x64 - x64
allow_failures:
- julia: nightly
fail-fast: false fail-fast: false
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: julia-actions/setup-julia@v1 - uses: julia-actions/setup-julia@v1
with: with:
version: ${{ matrix.version }} version: ${{ matrix.version }}
arch: ${{ matrix.arch }} arch: ${{ matrix.arch }}
- uses: actions/cache@v3
env:
cache-name: cache-artifacts
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
- uses: julia-actions/julia-buildpkg@latest - uses: julia-actions/julia-buildpkg@latest
- uses: julia-actions/julia-runtest@latest - uses: julia-actions/julia-runtest@latest
- uses: julia-actions/julia-processcoverage@v1 - uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v1 - uses: codecov/codecov-action@v2
with: with:
file: ./lcov.info file: ./lcov.info
flags: unittests flags: unittests
name: codecov-umbrella name: codecov-umbrella
fail_ci_if_error: false fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -1,7 +1,7 @@
name = "Groups" name = "Groups"
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557" uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
authors = ["Marek Kaluba <kalmar@amu.edu.pl>"] authors = ["Marek Kaluba <kalmar@amu.edu.pl>"]
version = "0.7.3" version = "0.7.4"
[deps] [deps]
Folds = "41a02a25-b8f0-4f67-bc48-60067656b558" Folds = "41a02a25-b8f0-4f67-bc48-60067656b558"
@ -16,7 +16,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
[compat] [compat]
Folds = "0.2.7" Folds = "0.2.7"
GroupsCore = "0.4" GroupsCore = "0.4"
KnuthBendix = "0.3" KnuthBendix = "0.4"
OrderedCollections = "1" OrderedCollections = "1"
PermutationGroups = "0.3" PermutationGroups = "0.3"
StaticArrays = "1" StaticArrays = "1"

110
README.md
View File

@ -10,25 +10,25 @@ The package implements `AbstractFPGroup` with three concrete types: `FreeGroup`,
julia> using Groups, GroupsCore julia> using Groups, GroupsCore
julia> A = Alphabet([:a, :A, :b, :B, :c, :C], [2, 1, 4, 3, 6, 5]) julia> A = Alphabet([:a, :A, :b, :B, :c, :C], [2, 1, 4, 3, 6, 5])
Alphabet of Symbol: Alphabet of Symbol
1. :a = (:A)⁻¹ 1. a (inverse of: A)
2. :A = (:a)⁻¹ 2. A (inverse of: a)
3. :b = (:B)⁻¹ 3. b (inverse of: B)
4. :B = (:b)⁻¹ 4. B (inverse of: b)
5. :c = (:C)⁻¹ 5. c (inverse of: C)
6. :C = (:c)⁻¹ 6. C (inverse of: c)
julia> F = FreeGroup(A) julia> F = FreeGroup(A)
free group on 3 generators free group on 3 generators
julia> a,b,c = gens(F) julia> a,b,c = gens(F)
3-element Vector{FPGroupElement{FreeGroup{Symbol}, KnuthBendix.Word{UInt8}}}: 3-element Vector{FPGroupElement{FreeGroup{Symbol, KnuthBendix.LenLex{Symbol}}, …}}:
a a
b b
c c
julia> a*inv(a) julia> a*inv(a)
(empty word) (id)
julia> (a*b)^2 julia> (a*b)^2
a*b*a*b a*b*a*b
@ -40,65 +40,75 @@ julia> x = a*b; y = inv(b)*a;
julia> x*y julia> x*y
a^2 a^2
``` ```
## FPGroup
Let's create a quotient of the free group above: Let's create a quotient of the free group above:
```julia ```julia
julia> ε = one(F); julia> ε = one(F)
(id)
julia> G = FPGroup(F, [a^2 => ε, b^3=> ε, (a*b)^7=>ε, (a*b*a*inv(b))^6 => ε, commutator(a, c) => ε, commutator(b, c) => ε ])
┌ Warning: Maximum number of rules (100) reached. The rewriting system may not be confluent.
│ You may retry `knuthbendix` with a larger `maxrules` kwarg.
└ @ KnuthBendix ~/.julia/packages/KnuthBendix/i93Np/src/kbs.jl:6
⟨a, b, c | a^2 => (empty word), b^3 => (empty word), a*b*a*b*a*b*a*b*a*b*a*b*a*b => (empty word), a*b*a*B*a*b*a*B*a*b*a*B*a*b*a*B*a*b*a*B*a*b*a*B => (empty word), A*C*a*c => (empty word), B*C*b*c => (empty word)⟩
julia> G = FPGroup(F, [a^2 => ε, b^3=> ε, (a*b)^7=>ε, (a*b*a*inv(b))^6 => ε, commutator(a, c) => ε, commutator(b, c) => ε ], max_rules=100)
┌ Warning: Maximum number of rules (100) reached.
│ The rewriting system may not be confluent.
│ You may retry `knuthbendix` with a larger `max_rules` kwarg.
└ @ KnuthBendix ~/.julia/packages/KnuthBendix/6ME1b/src/knuthbendix_base.jl:8
Finitely presented group generated by:
{ a b c },
subject to relations:
a^2 => (id)
b^3 => (id)
a*b*a*b*a*b*a*b*a*b*a*b*a*b => (id)
a*b*a*B*a*b*a*B*a*b*a*B*a*b*a*B*a*b*a*B*a*b*a*B => (id)
A*C*a*c => (id)
B*C*b*c => (id)
``` ```
As you can see from the warning, the Knuth-Bendix procedure has not completed successfully. This means that we only are able to approximate the word problem in `G`, i.e. if the equality (`==`) of two group elements may return `false` even if group elements are equal. Let us try with a larger maximal number of rules in the underlying rewriting system. As you can see from the warning, the Knuth-Bendix procedure has not completed successfully. This means that we only are able to **approximate the word problem** in `G`, i.e. if the equality (`==`) of two group elements may return `false` even if group elements are equal. Let us try with a larger maximal number of rules in the underlying rewriting system.
```julia ```julia
julia> G = FPGroup(F, [a^2 => ε, b^3=> ε, (a*b)^7=>ε, (a*b*a*inv(b))^6 => ε, commutator(a, c) => ε, commutator(b, c) => ε ], maxrules=500) julia> G = FPGroup(F, [a^2 => ε, b^3=> ε, (a*b)^7=>ε, (a*b*a*inv(b))^6 => ε, commutator(a, c) => ε, commutator(b, c) => ε ], max_rules=500)
⟨a, b, c | a^2 => (empty word), b^3 => (empty word), a*b*a*b*a*b*a*b*a*b*a*b*a*b => (empty word), a*b*a*B*a*b*a*B*a*b*a*B*a*b*a*B*a*b*a*B*a*b*a*B => (empty word), A*C*a*c => (empty word), B*C*b*c => (empty word)⟩ Finitely presented group generated by:
{ a b c },
subject to relations:
a^2 => (id)
b^3 => (id)
a*b*a*b*a*b*a*b*a*b*a*b*a*b => (id)
a*b*a*B*a*b*a*B*a*b*a*B*a*b*a*B*a*b*a*B*a*b*a*B => (id)
A*C*a*c => (id)
B*C*b*c => (id)
``` ```
This time there was no warning, i.e. Knuth-Bendix completion was successful and we may treat the equality (`==`) as true mathematical equality. Note that `G` is the direct product of ` = ⟨ c ⟩` and a quotient of van Dyck `(2,3,7)`-group. Let's create a random word and reduce it as an element of `G`. This time there was no warning, i.e. Knuth-Bendix completion was successful and we may treat the equality (`==`) as the **true mathematical equality**. Note that `G` is the direct product of ` = ⟨ c ⟩` and a quotient of van Dyck `(2,3,7)`-group. Let's create a random word and reduce it as an element of `G`.
```julia ```julia
julia> using Random; Random.seed!(1); w = Groups.Word(rand(1:length(A), 16)) julia> using Random; Random.seed!(1); w = Groups.Word(rand(1:length(A), 16));
KnuthBendix.Word{UInt16}: 4·6·1·1·1·6·5·1·5·2·3·6·2·4·2·6
julia> F(w) # freely reduced w julia> length(w), w # word of itself
B*C*a^4*c*A*b*C*A*B*A*C (16, 1·3·5·4·6·2·5·5·5·2·4·3·2·1·4·4)
julia> G(w) # w as an element of G julia> f = F(w) # freely reduced w
B*a*b*a*B*a*C^2 a*b*c*B*C*A*c^3*A*B^2
julia> F(w) # freely reduced w julia> length(word(f)), word(f) # the underlying word in F
B*C*a^4*c*A*b*C*A*B*A*C (12, 1·3·5·4·6·2·5·5·5·2·4·4)
julia> word(ans) # the underlying word in A julia> g = G(w) # w as an element of G
KnuthBendix.Word{UInt8}: 4·6·1·1·1·1·5·2·3·6·2·4·2·6 a*b*c^3
julia> G(w) # w as an element of G
B*a*b*a*B*a*C^2
julia> word(ans) # the underlying word in A
KnuthBendix.Word{UInt8}: 4·1·3·1·4·1·6·6
julia> length(word(g)), word(g) # the underlying word in G
(5, 1·3·5·5·5)
``` ```
As we can see the underlying words change according to where they are reduced. As we can see the underlying words change according to where they are reduced.
Note that a word `w` (of type `Word <: AbstractWord`) is just a sequence of numbers -- pointers to letters of an `Alphabet`. Without the alphabet `w` has no meaning. Note that a word `w` (of type `Word <: AbstractWord`) is just a sequence of numbers -- indices of letters of an `Alphabet`. Without the alphabet `w` has no intrinsic meaning.
### Automorphism Groups ## Automorphism Groups
Relatively complete is the support for the automorphisms of free groups, as given by Gersten presentation: Relatively complete is the support for the automorphisms of free groups generated by transvections (or Nielsen generators):
```julia ```julia
julia> saut = SpecialAutomorphismGroup(F, maxrules=100) julia> saut = SpecialAutomorphismGroup(F, max_rules=1000)
┌ Warning: Maximum number of rules (100) reached. The rewriting system may not be confluent.
│ You may retry `knuthbendix` with a larger `maxrules` kwarg.
└ @ KnuthBendix ~/.julia/packages/KnuthBendix/i93Np/src/kbs.jl:6
automorphism group of free group on 3 generators automorphism group of free group on 3 generators
julia> S = gens(saut) julia> S = gens(saut)
12-element Vector{Automorphism{FreeGroup{Symbol},…}}: 12-element Vector{Automorphism{FreeGroup{Symbol, KnuthBendix.LenLex{Symbol}}, …}}:
ϱ₁.₂ ϱ₁.₂
ϱ₁.₃ ϱ₁.₃
ϱ₂.₁ ϱ₂.₁
@ -114,17 +124,15 @@ julia> S = gens(saut)
julia> x, y, z = S[1], S[12], S[6]; julia> x, y, z = S[1], S[12], S[6];
julia> f = x*y*inv(z) julia> f = x*y*inv(z);
ϱ₁.₂*λ₃.₂*ϱ₃.₂^-1
julia> g = inv(z)*y*x julia> g = inv(z)*y*x;
ϱ₃.₂^-1*ϱ₁.₂*λ₃.₂
julia> word(f), word(g) julia> word(f), word(g)
(KnuthBendix.Word{UInt8}: 1·12·18, KnuthBendix.Word{UInt8}: 18·1·12) (1·23·12, 12·23·1)
``` ```
Even though Knuth-Bendix did not finish successfully in automorphism groups we have another ace in our sleeve to solve the word problem: evaluation. Even though there is no known finite, confluent rewriting system for automorphism groupsof the free group (so Knuth-Bendix did not finish successfully) we have another ace in our sleeve to solve the word problem: evaluation.
Lets have a look at the images of generators under those automorphisms: Lets have a look at the images of generators under those automorphisms:
```julia ```julia
julia> evaluate(f) # or to be more verbose... julia> evaluate(f) # or to be more verbose...

View File

@ -10,7 +10,7 @@ import OrderedCollections: OrderedSet
import KnuthBendix import KnuthBendix
import KnuthBendix: AbstractWord, Alphabet, Word import KnuthBendix: AbstractWord, Alphabet, Word
import KnuthBendix: alphabet import KnuthBendix: alphabet, ordering
export MatrixGroups export MatrixGroups

View File

@ -6,7 +6,7 @@ function gersten_alphabet(n::Integer; commutative::Bool = true)
append!(S, [λ(i, j) for (i, j) in indexing]) append!(S, [λ(i, j) for (i, j) in indexing])
end end
return Alphabet(S) return Alphabet(mapreduce(x -> [x, inv(x)], union, S))
end end
function _commutation_rule( function _commutation_rule(
@ -46,7 +46,7 @@ gersten_relations(n::Integer; commutative) =
function gersten_relations(::Type{W}, n::Integer; commutative) where {W<:AbstractWord} function gersten_relations(::Type{W}, n::Integer; commutative) where {W<:AbstractWord}
@assert n > 1 "Gersten relations are defined only for n>1, got n=$n" @assert n > 1 "Gersten relations are defined only for n>1, got n=$n"
A = gersten_alphabet(n, commutative=commutative) A = gersten_alphabet(n, commutative=commutative)
@assert length(A) <= KnuthBendix._max_alphabet_length(W) "Type $W can not represent words over alphabet with $(length(A)) letters." @assert length(A) <= typemax(eltype(W)) "Type $W can not represent words over alphabet with $(length(A)) letters."
rels = Pair{W,W}[] rels = Pair{W,W}[]
@ -96,7 +96,7 @@ function gersten_relations(::Type{W}, n::Integer; commutative) where {W<:Abstrac
if i j if i j
push!(rels, _hexagonal_rule(W, A, ϱ(i, j), ϱ(j, i), λ(i, j), λ(j, i))) push!(rels, _hexagonal_rule(W, A, ϱ(i, j), ϱ(j, i), λ(i, j), λ(j, i)))
w = W([A[ϱ(i, j)], A[ϱ(j, i)^-1], A[λ(i, j)]]) w = W([A[ϱ(i, j)], A[ϱ(j, i)^-1], A[λ(i, j)]])
push!(rels, w^2 => inv(A, w)^2) push!(rels, w^2 => inv(w, A)^2)
end end
end end
end end

View File

@ -1,9 +1,9 @@
struct SurfaceGroup{T, S, R} <: AbstractFPGroup struct SurfaceGroup{T,S,RW} <: AbstractFPGroup
genus::Int genus::Int
boundaries::Int boundaries::Int
gens::Vector{T} gens::Vector{T}
relations::Vector{<:Pair{S,S}} relations::Vector{<:Pair{S,S}}
rws::R rw::RW
end end
include("symplectic_twists.jl") include("symplectic_twists.jl")
@ -17,7 +17,7 @@ function Base.show(io::IO, S::SurfaceGroup)
end end
end end
function SurfaceGroup(genus::Integer, boundaries::Integer) function SurfaceGroup(genus::Integer, boundaries::Integer, W=Word{Int16})
@assert genus > 1 @assert genus > 1
# The (confluent) rewriting systems comes from # The (confluent) rewriting systems comes from
@ -37,8 +37,8 @@ function SurfaceGroup(genus::Integer, boundaries::Integer)
for i in 1:genus for i in 1:genus
subscript = join('₀' + d for d in reverse(digits(i))) subscript = join('₀' + d for d in reverse(digits(i)))
KnuthBendix.set_inversion!(Al, "a" * subscript, "A" * subscript) KnuthBendix.setinverse!(Al, "a" * subscript, "A" * subscript)
KnuthBendix.set_inversion!(Al, "b" * subscript, "B" * subscript) KnuthBendix.setinverse!(Al, "b" * subscript, "B" * subscript)
end end
if boundaries == 0 if boundaries == 0
@ -48,33 +48,34 @@ function SurfaceGroup(genus::Integer, boundaries::Integer)
x = 4 * i x = 4 * i
append!(word, [x, x - 2, x - 1, x - 3]) append!(word, [x, x - 2, x - 1, x - 3])
end end
comms = Word(word) comms = W(word)
word_rels = [comms => one(comms)] word_rels = [comms => one(comms)]
rws = KnuthBendix.RewritingSystem(word_rels, KnuthBendix.RecursivePathOrder(Al)) rws = let R = KnuthBendix.RewritingSystem(word_rels, KnuthBendix.Recursive(Al))
KnuthBendix.knuthbendix!(rws) KnuthBendix.IndexAutomaton(KnuthBendix.knuthbendix(R))
end
elseif boundaries == 1 elseif boundaries == 1
S = typeof(one(Word(Int[]))) word_rels = Pair{W,W}[]
word_rels = Pair{S, S}[] rws = let R = RewritingSystem(word_rels, KnuthBendix.LenLex(Al))
rws = RewritingSystem(word_rels, KnuthBendix.LenLex(Al)) KnuthBendix.IndexAutomaton(KnuthBendix.knuthbendix(R))
end
else else
throw("Not Implemented") throw("Not Implemented for MCG with $boundaryies boundary components")
end end
F = FreeGroup(alphabet(rws)) F = FreeGroup(Al)
rels = [F(lhs) => F(rhs) for (lhs, rhs) in word_rels] rels = [F(lhs) => F(rhs) for (lhs, rhs) in word_rels]
return SurfaceGroup(genus, boundaries, KnuthBendix.letters(Al)[2:2:end], rels, rws) return SurfaceGroup(genus, boundaries, [Al[i] for i in 2:2:length(Al)], rels, rws)
end end
rewriting(S::SurfaceGroup) = S.rws rewriting(S::SurfaceGroup) = S.rw
KnuthBendix.alphabet(S::SurfaceGroup) = alphabet(rewriting(S))
relations(S::SurfaceGroup) = S.relations relations(S::SurfaceGroup) = S.relations
function symplectic_twists(π₁Σ::SurfaceGroup) function symplectic_twists(π₁Σ::SurfaceGroup)
g = genus(π₁Σ) g = genus(π₁Σ)
saut = SpecialAutomorphismGroup(FreeGroup(2g), maxrules=100) 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]

View File

@ -5,19 +5,19 @@ function SpecialAutomorphismGroup(F::FreeGroup; ordering = KnuthBendix.LenLex, k
n = length(alphabet(F)) ÷ 2 n = length(alphabet(F)) ÷ 2
A, rels = gersten_relations(n, commutative=false) A, rels = gersten_relations(n, commutative=false)
S = KnuthBendix.letters(A)[1:2(n^2-n)] S = [A[i] for i in 1:2:length(A)]
maxrules = 1000*n max_rules = 1000 * n
rws = Logging.with_logger(Logging.NullLogger()) do
rws = KnuthBendix.RewritingSystem(rels, ordering(A)) rws = KnuthBendix.RewritingSystem(rels, ordering(A))
Logging.with_logger(Logging.NullLogger()) do
# the rws is not confluent, let's suppress warning about it # the rws is not confluent, let's suppress warning about it
KnuthBendix.knuthbendix!(rws; maxrules=maxrules, kwargs...) KnuthBendix.knuthbendix(rws, KnuthBendix.Settings(; max_rules=max_rules, kwargs...))
end
return AutomorphismGroup(F, S, rws, ntuple(i -> gens(F, i), n))
end end
KnuthBendix.alphabet(G::AutomorphismGroup{<:FreeGroup}) = alphabet(rewriting(G)) idxA = KnuthBendix.IndexAutomaton(rws)
return AutomorphismGroup(F, S, idxA, ntuple(i -> gens(F, i), n))
end
function relations(G::AutomorphismGroup{<:FreeGroup}) function relations(G::AutomorphismGroup{<:FreeGroup})
n = length(alphabet(object(G))) ÷ 2 n = length(alphabet(object(G))) ÷ 2

View File

@ -25,7 +25,7 @@ function Te_diagonal(λ::Groups.ΡΛ, ϱ::Groups.ΡΛ, i::Integer)
if i == n if i == n
τ = rotation_element(λ, ϱ) τ = rotation_element(λ, ϱ)
return inv(A, τ) * Te_diagonal(λ, ϱ, 1) * τ return inv(τ, A) * Te_diagonal(λ, ϱ, 1) * τ
end end
@assert 1 <= i < n @assert 1 <= i < n
@ -37,32 +37,32 @@ function Te_diagonal(λ::Groups.ΡΛ, ϱ::Groups.ΡΛ, i::Integer)
g = one(Word(Int[])) g = one(Word(Int[]))
g *= λ[NJ, NI] # β ↦ α g *= λ[NJ, NI] # β ↦ α
g *= λ[NI, I] * inv(A, ϱ[NI, J]) # α ↦ a*α*b^-1 g *= λ[NI, I] * inv(ϱ[NI, J], A) # α ↦ a*α*b^-1
g *= inv(A, λ[NJ, NI]) # β ↦ b*α^-1*a^-1*α g *= inv(λ[NJ, NI], A) # β ↦ b*α^-1*a^-1*α
g *= λ[J, NI] * inv(A, λ[J, I]) # b ↦ α g *= λ[J, NI] * inv(λ[J, I], A) # b ↦ α
g *= inv(A, λ[J, NI]) # b ↦ b*α^-1*a^-1*α g *= inv(λ[J, NI], A) # b ↦ b*α^-1*a^-1*α
g *= inv(A, ϱ[J, NI]) * ϱ[J, I] # b ↦ b*α^-1*a^-1*α*b*α^-1 g *= inv(ϱ[J, NI], A) * ϱ[J, I] # b ↦ b*α^-1*a^-1*α*b*α^-1
g *= ϱ[J, NI] # b ↦ b*α^-1*a^-1*α*b*α^-1*a*α*b^-1 g *= ϱ[J, NI] # b ↦ b*α^-1*a^-1*α*b*α^-1*a*α*b^-1
return g return g
end end
function Te_lantern(A::Alphabet, b₀::T, a₁::T, a₂::T, a₃::T, a₄::T, a₅::T) where {T} function Te_lantern(A::Alphabet, b₀::T, a₁::T, a₂::T, a₃::T, a₄::T, a₅::T) where {T}
a₀ = (a₁ * a₂ * a₃)^4 * inv(A, b₀) a₀ = (a₁ * a₂ * a₃)^4 * inv(b₀, A)
X = a₄ * a₅ * a₃ * a₄ # from Primer X = a₄ * a₅ * a₃ * a₄ # from Primer
b₁ = inv(A, X) * a₀ * X # from Primer b₁ = inv(X, A) * a₀ * X # from Primer
Y = a₂ * a₃ * a₁ * a₂ Y = a₂ * a₃ * a₁ * a₂
return inv(A, Y) * b₁ * Y # b₂ from Primer return inv(Y, A) * b₁ * Y # b₂ from Primer
end end
function Ta(λ::Groups.ΡΛ, i::Integer) function Ta(λ::Groups.ΡΛ, i::Integer)
@assert λ.id == ; @assert λ.id ==
return λ[mod1(λ.N - 2i + 1, λ.N), mod1(λ.N - 2i + 2, λ.N)] return λ[mod1(λ.N - 2i + 1, λ.N), mod1(λ.N - 2i + 2, λ.N)]
end end
function Tα(λ::Groups.ΡΛ, i::Integer) function Tα(λ::Groups.ΡΛ, i::Integer)
@assert λ.id == ; @assert λ.id ==
return inv(λ.A, λ[mod1(λ.N-2i+2, λ.N), mod1(λ.N-2i+1, λ.N)]) return inv(λ[mod1(λ.N - 2i + 2, λ.N), mod1(λ.N - 2i + 1, λ.N)], λ.A)
end end
function Te(λ::ΡΛ, ϱ::ΡΛ, i, j) function Te(λ::ΡΛ, ϱ::ΡΛ, i, j)
@ -85,16 +85,16 @@ function Te(λ::ΡΛ, ϱ::ΡΛ, i, j)
if mod(j - (i + 1), genus) == 0 if mod(j - (i + 1), genus) == 0
return Te_diagonal(λ, ϱ, i) return Te_diagonal(λ, ϱ, i)
else else
return inv(A, Te_lantern( return inv(Te_lantern(
A, A,
# Our notation: # Primer notation: # Our notation: # Primer notation:
inv(A, Ta(λ, i + 1)), # b₀ inv(Ta(λ, i + 1), A), # b₀
inv(A, Ta(λ, i)), # a₁ inv(Ta(λ, i), A), # a₁
inv(A, Tα(λ, i)), # a₂ inv(Tα(λ, i), A), # a₂
inv(A, Te_diagonal(λ, ϱ, i)), # a₃ inv(Te_diagonal(λ, ϱ, i), A), # a₃
inv(A, Tα(λ, i + 1)), # a₄ inv(Tα(λ, i + 1), A), # a₄
inv(A, Te(λ, ϱ, i + 1, j)), # a₅ inv(Te(λ, ϱ, i + 1, j), A), # a₅
)) ), A)
end end
end end
@ -123,24 +123,24 @@ function rotation_element(λ::ΡΛ, ϱ::ΡΛ)
halftwists = map(1:genus-1) do i halftwists = map(1:genus-1) do i
j = i + 1 j = i + 1
x = Ta(λ, j) * inv(A, Ta(λ, i)) * Tα(λ, j) * Te_diagonal(λ, ϱ, i) x = Ta(λ, j) * inv(Ta(λ, i), A) * Tα(λ, j) * Te_diagonal(λ, ϱ, i)
δ = x * Tα(λ, i) * inv(A, x) δ = x * Tα(λ, i) * inv(x, A)
c = c =
inv(A, Ta(λ, j)) * inv(Ta(λ, j), A) *
Te(λ, ϱ, i, j) * Te(λ, ϱ, i, j) *
Tα(λ, i)^2 * Tα(λ, i)^2 *
inv(A, δ) * inv(δ, A) *
inv(A, Ta(λ, j)) * inv(Ta(λ, j), A) *
Ta(λ, i) * Ta(λ, i) *
δ δ
z = z =
Te_diagonal(λ, ϱ, i) * Te_diagonal(λ, ϱ, i) *
inv(A, Ta(λ, i)) * inv(Ta(λ, i), A) *
Tα(λ, i) * Tα(λ, i) *
Ta(λ, i) * Ta(λ, i) *
inv(A, Te_diagonal(λ, ϱ, i)) inv(Te_diagonal(λ, ϱ, i), A)
Ta(λ, i) * inv(A, Ta(λ, j) * Tα(λ, j))^6 * (Ta(λ, j) * Tα(λ, j) * z)^4 * c Ta(λ, i) * inv(Ta(λ, j) * Tα(λ, j), A)^6 * (Ta(λ, j) * Tα(λ, j) * z)^4 * c
end end
τ = (Ta(λ, 1) * Tα(λ, 1))^6 * prod(halftwists) τ = (Ta(λ, 1) * Tα(λ, 1))^6 * prod(halftwists)
@ -188,7 +188,7 @@ function SymplecticMappingClass(
i::Integer, i::Integer,
j::Integer; j::Integer;
minus=false, minus=false,
inverse = false, inverse=false
) )
@assert i > 0 && j > 0 @assert i > 0 && j > 0
id === :A && @assert i j id === :A && @assert i j
@ -201,24 +201,24 @@ function SymplecticMappingClass(
w = if id === :A w = if id === :A
Te(λ, ϱ, i, j) * Te(λ, ϱ, i, j) *
inv(A, Ta(λ, i)) * inv(Ta(λ, i), A) *
Tα(λ, i) * Tα(λ, i) *
Ta(λ, i) * Ta(λ, i) *
inv(A, Te(λ, ϱ, i, j)) * inv(Te(λ, ϱ, i, j), A) *
inv(A, Tα(λ, i)) * inv(Tα(λ, i), A) *
inv(A, Ta(λ, j)) inv(Ta(λ, j), A)
elseif id === :B elseif id === :B
if !minus if !minus
if i j if i j
x = Ta(λ, j) * inv(A, Ta(λ, i)) * Tα(λ, j) * Te(λ, ϱ, i, j) x = Ta(λ, j) * inv(Ta(λ, i), A) * Tα(λ, j) * Te(λ, ϱ, i, j)
δ = x * Tα(λ, i) * inv(A, x) δ = x * Tα(λ, i) * inv(x, A)
Tα(λ, i) * Tα(λ, j) * inv(A, δ) Tα(λ, i) * Tα(λ, j) * inv(δ, A)
else else
inv(A, Tα(λ, i)) inv(Tα(λ, i), A)
end end
else else
if i j if i j
Ta(λ, i) * Ta(λ, j) * inv(A, Te(λ, ϱ, i, j)) Ta(λ, i) * Ta(λ, j) * inv(Te(λ, ϱ, i, j), A)
else else
Ta(λ, i) Ta(λ, i)
end end

View File

@ -45,9 +45,9 @@ Base.@propagate_inbounds @inline function evaluate!(
if !t.inv if !t.inv
append!(word(v[i]), word(v[j])) append!(word(v[i]), word(v[j]))
else else
# append!(word(v[i]), inv(A, word(v[j]))) # append!(word(v[i]), inv(word(v[j]), A))
for l in Iterators.reverse(word(v[j])) for l in Iterators.reverse(word(v[j]))
push!(word(v[i]), inv(A, l)) push!(word(v[i]), inv(l, A))
end end
end end
else # if t.id === :λ else # if t.id === :λ
@ -57,9 +57,9 @@ Base.@propagate_inbounds @inline function evaluate!(
pushfirst!(word(v[i]), l) pushfirst!(word(v[i]), l)
end end
else else
# prepend!(word(v[i]), inv(A, word(v[j]))) # prepend!(word(v[i]), inv(word(v[j]), A))
for l in word(v[j]) for l in word(v[j])
pushfirst!(word(v[i]), inv(A, l)) pushfirst!(word(v[i]), inv(l, A))
end end
end end
end end

View File

@ -4,15 +4,15 @@ function KnuthBendix.Alphabet(S::AbstractVector{<:GSymbol})
return Alphabet(S, inversions) return Alphabet(S, inversions)
end end
struct AutomorphismGroup{G<:Group,T,R,S} <: AbstractFPGroup struct AutomorphismGroup{G<:Group,T,RW,S} <: AbstractFPGroup
group::G group::G
gens::Vector{T} gens::Vector{T}
rws::R rw::RW
domain::S domain::S
end end
object(G::AutomorphismGroup) = G.group object(G::AutomorphismGroup) = G.group
rewriting(G::AutomorphismGroup) = G.rws rewriting(G::AutomorphismGroup) = G.rw
function equality_data(f::AbstractFPGroupElement{<:AutomorphismGroup}) function equality_data(f::AbstractFPGroupElement{<:AutomorphismGroup})
imf = evaluate(f) imf = evaluate(f)
@ -144,11 +144,11 @@ end
function Base.getindex(lm::LettersMap, i::Integer) function Base.getindex(lm::LettersMap, i::Integer)
# here i is an index of an alphabet # here i is an index of an alphabet
@boundscheck 1 i length(KnuthBendix.letters(lm.A)) @boundscheck 1 i length(lm.A)
if !haskey(lm.indices_map, i) if !haskey(lm.indices_map, i)
img = if haskey(lm.indices_map, inv(lm.A, i)) img = if haskey(lm.indices_map, inv(i, lm.A))
inv(lm.A, lm.indices_map[inv(lm.A, i)]) inv(lm.indices_map[inv(i, lm.A)], lm.A)
else else
@warn "LetterMap: neither $i nor its inverse has assigned value" @warn "LetterMap: neither $i nor its inverse has assigned value"
one(valtype(lm.indices_map)) one(valtype(lm.indices_map))
@ -193,7 +193,7 @@ function generated_evaluate(a::FPGroupElement{<:AutomorphismGroup})
push!(args[idx].args, :(d[$k])) push!(args[idx].args, :(d[$k]))
continue continue
end end
k = findfirst(==(inv(A, l)), first_ltrs) k = findfirst(==(inv(l, A)), first_ltrs)
if k !== nothing if k !== nothing
push!(args[idx].args, :(inv(d[$k]))) push!(args[idx].args, :(inv(d[$k])))
continue continue
@ -207,7 +207,8 @@ function generated_evaluate(a::FPGroupElement{<:AutomorphismGroup})
@assert length(v.args) >= 2 @assert length(v.args) >= 2
if length(v.args) > 2 if length(v.args) > 2
for (j, a) in pairs(v.args) for (j, a) in pairs(v.args)
if a isa Expr && a.head == :call "$a" if a isa Expr && a.head == :call
"$a"
@assert a.args[1] == :inv @assert a.args[1] == :inv
if !(a in keys(locals)) if !(a in keys(locals))
locals[a] = Symbol("var_#$locals_counter") locals[a] = Symbol("var_#$locals_counter")

View File

@ -88,7 +88,7 @@ struct Homomorphism{Gr1, Gr2, I, W}
end end
end end
for (lhs, rhs) in relations(source) for (lhs, rhs) in relations(source)
relator = lhs*inv(alphabet(source), rhs) relator = lhs * inv(rhs, alphabet(source))
im_r = hom.target(hom(relator)) im_r = hom.target(hom(relator))
@assert isone(im_r) "Map does not define a homomorphism: h($relator) = $(im_r)$(one(target))." @assert isone(im_r) "Map does not define a homomorphism: h($relator) = $(im_r)$(one(target))."
end end

View File

@ -5,7 +5,7 @@ struct SpecialLinearGroup{N, T, R, A, S} <: MatrixGroup{N,T}
alphabet::A alphabet::A
gens::S gens::S
function SpecialLinearGroup{N}(base_ring) where N function SpecialLinearGroup{N}(base_ring) where {N}
S = [ElementaryMatrix{N}(i, j, one(base_ring)) for i in 1:N for j in 1:N if i j] S = [ElementaryMatrix{N}(i, j, one(base_ring)) for i in 1:N for j in 1:N if i j]
alphabet = Alphabet(S) alphabet = Alphabet(S)
@ -19,7 +19,7 @@ struct SpecialLinearGroup{N, T, R, A, S} <: MatrixGroup{N,T}
end end
end end
GroupsCore.ngens(SL::SpecialLinearGroup{N}) where N = N^2 - N GroupsCore.ngens(SL::SpecialLinearGroup{N}) where {N} = N^2 - N
Base.show(io::IO, SL::SpecialLinearGroup{N,T}) where {N,T} = Base.show(io::IO, SL::SpecialLinearGroup{N,T}) where {N,T} =
print(io, "special linear group of $N×$N matrices over $T") print(io, "special linear group of $N×$N matrices over $T")
@ -28,7 +28,7 @@ function Base.show(
io::IO, io::IO,
::MIME"text/plain", ::MIME"text/plain",
sl::Groups.AbstractFPGroupElement{<:SpecialLinearGroup{N}} sl::Groups.AbstractFPGroupElement{<:SpecialLinearGroup{N}}
) where N ) where {N}
Groups.normalform!(sl) Groups.normalform!(sl)

View File

@ -5,7 +5,7 @@ struct SymplecticGroup{N, T, R, A, S} <: MatrixGroup{N,T}
alphabet::A alphabet::A
gens::S gens::S
function SymplecticGroup{N}(base_ring) where N function SymplecticGroup{N}(base_ring) where {N}
S = symplectic_gens(N, eltype(base_ring)) S = symplectic_gens(N, eltype(base_ring))
alphabet = Alphabet(S) alphabet = Alphabet(S)
@ -21,7 +21,7 @@ end
GroupsCore.ngens(Sp::SymplecticGroup) = length(Sp.gens) GroupsCore.ngens(Sp::SymplecticGroup) = length(Sp.gens)
Base.show(io::IO, ::SymplecticGroup{N}) where N = print(io, "group of $N×$N symplectic matrices") Base.show(io::IO, ::SymplecticGroup{N}) where {N} = print(io, "group of $N×$N symplectic matrices")
function Base.show( function Base.show(
io::IO, io::IO,
@ -65,6 +65,6 @@ function _std_symplectic_form(m::AbstractMatrix)
return Ω return Ω
end end
function issymplectic(mat::M, Ω = _std_symplectic_form(mat)) where M <: AbstractMatrix function issymplectic(mat::M, Ω=_std_symplectic_form(mat)) where {M<:AbstractMatrix}
return Ω == transpose(mat) * Ω * mat return Ω == transpose(mat) * Ω * mat
end end

View File

@ -10,7 +10,7 @@ function Base.:(==)(m1::M1, m2::M2) where {M1<:MatrixGroupElement, M2<:MatrixGro
return matrix_repr(m1) == matrix_repr(m2) return matrix_repr(m1) == matrix_repr(m2)
end end
Base.size(m::MatrixGroupElement{N}) where N = (N, N) Base.size(m::MatrixGroupElement{N}) where {N} = (N, N)
Base.eltype(m::MatrixGroupElement{N,T}) where {N,T} = T Base.eltype(m::MatrixGroupElement{N,T}) where {N,T} = T
# three structural assumptions about matrix groups # three structural assumptions about matrix groups

View File

@ -2,7 +2,7 @@ struct ElementaryMatrix{N, T} <: Groups.GSymbol
i::Int i::Int
j::Int j::Int
val::T val::T
ElementaryMatrix{N}(i, j, val=1) where N = ElementaryMatrix{N}(i, j, val=1) where {N} =
(@assert i j; new{N,typeof(val)}(i, j, val)) (@assert i j; new{N,typeof(val)}(i, j, val))
end end
@ -11,13 +11,13 @@ function Base.show(io::IO, e::ElementaryMatrix)
!isone(e.val) && print(io, "^$(e.val)") !isone(e.val) && print(io, "^$(e.val)")
end end
Base.:(==)(e::ElementaryMatrix{N}, f::ElementaryMatrix{N}) where N = Base.:(==)(e::ElementaryMatrix{N}, f::ElementaryMatrix{N}) where {N} =
e.i == f.i && e.j == f.j && e.val == f.val e.i == f.i && e.j == f.j && e.val == f.val
Base.hash(e::ElementaryMatrix, h::UInt) = Base.hash(e::ElementaryMatrix, h::UInt) =
hash(typeof(e), hash((e.i, e.j, e.val), h)) hash(typeof(e), hash((e.i, e.j, e.val), h))
Base.inv(e::ElementaryMatrix{N}) where N = Base.inv(e::ElementaryMatrix{N}) where {N} =
ElementaryMatrix{N}(e.i, e.j, -e.val) ElementaryMatrix{N}(e.i, e.j, -e.val)
function matrix_repr(e::ElementaryMatrix{N,T}) where {N,T} function matrix_repr(e::ElementaryMatrix{N,T}) where {N,T}

View File

@ -3,7 +3,7 @@ struct ElementarySymplectic{N, T} <: Groups.GSymbol
i::Int i::Int
j::Int j::Int
val::T val::T
function ElementarySymplectic{N}(s::Symbol, i::Integer, j::Integer, val=1) where N function ElementarySymplectic{N}(s::Symbol, i::Integer, j::Integer, val=1) where {N}
@assert s (:A, :B) @assert s (:A, :B)
@assert iseven(N) @assert iseven(N)
n = N ÷ 2 n = N ÷ 2
@ -22,9 +22,9 @@ function Base.show(io::IO, s::ElementarySymplectic)
!isone(s.val) && print(io, "^$(s.val)") !isone(s.val) && print(io, "^$(s.val)")
end end
_ind(s::ElementarySymplectic{N}) where N = (s.i, s.j) _ind(s::ElementarySymplectic{N}) where {N} = (s.i, s.j)
_local_ind(N_half::Integer, i::Integer) = ifelse(i <= N_half, i, i - N_half) _local_ind(N_half::Integer, i::Integer) = ifelse(i <= N_half, i, i - N_half)
function _dual_ind(s::ElementarySymplectic{N}) where N function _dual_ind(s::ElementarySymplectic{N}) where {N}
if s.symbol === :A && return _ind(s) if s.symbol === :A && return _ind(s)
else#if s.symbol === :B else#if s.symbol === :B
return _dual_ind(N ÷ 2, s.i, s.j) return _dual_ind(N ÷ 2, s.i, s.j)
@ -51,10 +51,10 @@ end
Base.hash(s::ElementarySymplectic, h::UInt) = Base.hash(s::ElementarySymplectic, h::UInt) =
hash(Set([_ind(s); _dual_ind(s)]), hash(s.symbol, hash(s.val, h))) hash(Set([_ind(s); _dual_ind(s)]), hash(s.symbol, hash(s.val, h)))
LinearAlgebra.transpose(s::ElementarySymplectic{N}) where N = LinearAlgebra.transpose(s::ElementarySymplectic{N}) where {N} =
ElementarySymplectic{N}(s.symbol, s.j, s.i, s.val) ElementarySymplectic{N}(s.symbol, s.j, s.i, s.val)
Base.inv(s::ElementarySymplectic{N}) where N = Base.inv(s::ElementarySymplectic{N}) where {N} =
ElementarySymplectic{N}(s.symbol, s.i, s.j, -s.val) ElementarySymplectic{N}(s.symbol, s.i, s.j, -s.val)
function matrix_repr(s::ElementarySymplectic{N,T}) where {N,T} function matrix_repr(s::ElementarySymplectic{N,T}) where {N,T}

View File

@ -42,5 +42,5 @@ Defaults to the rewriting in the free group.
""" """
@inline function normalform!(res::AbstractWord, g::AbstractFPGroupElement) @inline function normalform!(res::AbstractWord, g::AbstractFPGroupElement)
isone(res) && isnormalform(g) && return append!(res, word(g)) isone(res) && isnormalform(g) && return append!(res, word(g))
return KnuthBendix.rewrite_from_left!(res, word(g), rewriting(parent(g))) return KnuthBendix.rewrite!(res, word(g), rewriting(parent(g)))
end end

View File

@ -3,15 +3,18 @@
""" """
AbstractFPGroup AbstractFPGroup
An Abstract type representing finitely presented groups. Every instance `` must implement An Abstract type representing finitely presented groups. Every instance must implement
* `KnuthBendix.alphabet(G::MyFPGroup)` * `KnuthBendix.alphabet(G::MyFPGroup)`
* `rewriting(G::MyFPGroup)` : return the rewriting object which must implement * `rewriting(G::MyFPGroup)` : return the rewriting object which must implement
> `KnuthBendix.rewrite_from_left!(u, v, rewriting(G))`. > `KnuthBendix.rewrite!(u, v, rewriting(G))`.
By default `alphabet(G)` is returned, which amounts to free rewriting in `G`. E.g. for `G::FreeGroup` `alphabet(G)` is returned, which amounts to free rewriting.
* `ordering(G::MyFPGroup)[ = KnuthBendix.ordering(rewriting(G))]` : return the
(implicit) ordering for the alphabet of `G`.
* `relations(G::MyFPGroup)` : return a set of defining relations. * `relations(G::MyFPGroup)` : return a set of defining relations.
AbstractFPGroup may also override `word_type(::Type{MyFPGroup}) = Word{UInt16}`, AbstractFPGroup may also override `word_type(::Type{MyFPGroup}) = Word{UInt8}`,
which controls the word type used for group elements. If a group has more than `255` generators you need to define e.g. which controls the word type used for group elements.
If a group has more than `255` generators you need to define e.g.
> `word_type(::Type{MyFPGroup}) = Word{UInt16}` > `word_type(::Type{MyFPGroup}) = Word{UInt16}`
""" """
abstract type AbstractFPGroup <: GroupsCore.Group end abstract type AbstractFPGroup <: GroupsCore.Group end
@ -22,22 +25,25 @@ word_type(::Type{<:AbstractFPGroup}) = Word{UInt8}
""" """
rewriting(G::AbstractFPGroup) rewriting(G::AbstractFPGroup)
Return a "rewriting object" for elements of `G`. The rewriting object must must implement Return a "rewriting object" for elements of `G`.
KnuthBendix.rewrite_from_left!(
u::AbstractWord,
v::AbstractWord,
rewriting(G)
)
For example if `G` is a `FreeGroup` then `alphabet(G)` is returned which results in free rewriting. For `FPGroup` a rewriting system is returned which may (or may not) rewrite word `v` to its normal form. The rewriting object must must implement
KnuthBendix.rewrite!(u::AbstractWord, v::AbstractWord, rewriting(G))
For example if `G` is a `FreeGroup` then `alphabet(G)` is returned which results
in free rewriting. For `FPGroup` a rewriting system is returned which may
(or may not) rewrite word `v` to its normal form (depending on e.g. its confluence).
""" """
function rewriting end function rewriting end
KnuthBendix.ordering(G::AbstractFPGroup) = ordering(rewriting(G))
KnuthBendix.alphabet(G::AbstractFPGroup) = alphabet(ordering(G))
Base.@propagate_inbounds function (G::AbstractFPGroup)( Base.@propagate_inbounds function (G::AbstractFPGroup)(
word::AbstractVector{<:Integer}, word::AbstractVector{<:Integer},
) )
@boundscheck @assert all( @boundscheck @assert all(
l -> 1 <= l <= length(KnuthBendix.alphabet(G)), l -> 1 <= l <= length(alphabet(G)),
word, word,
) )
return FPGroupElement(word_type(G)(word), G) return FPGroupElement(word_type(G)(word), G)
@ -128,7 +134,7 @@ end
function Base.inv(g::GEl) where {GEl<:AbstractFPGroupElement} function Base.inv(g::GEl) where {GEl<:AbstractFPGroupElement}
G = parent(g) G = parent(g)
return GEl(inv(alphabet(G), word(g)), G) return GEl(inv(word(g), alphabet(G)), G)
end end
function Base.:(*)(g::GEl, h::GEl) where {GEl<:AbstractFPGroupElement} function Base.:(*)(g::GEl, h::GEl) where {GEl<:AbstractFPGroupElement}
@ -153,8 +159,7 @@ struct FreeGroup{T,O} <: AbstractFPGroup
function FreeGroup(gens, ordering::KnuthBendix.WordOrdering) function FreeGroup(gens, ordering::KnuthBendix.WordOrdering)
@assert length(gens) == length(unique(gens)) @assert length(gens) == length(unique(gens))
L = KnuthBendix.letters(alphabet(ordering)) @assert all(l -> l in alphabet(ordering), gens)
@assert all(l -> l in L, gens)
return new{eltype(gens),typeof(ordering)}(gens, ordering) return new{eltype(gens),typeof(ordering)}(gens, ordering)
end end
end end
@ -163,15 +168,14 @@ FreeGroup(gens, A::Alphabet) = FreeGroup(gens, KnuthBendix.LenLex(A))
function FreeGroup(A::Alphabet) function FreeGroup(A::Alphabet)
@boundscheck @assert all( @boundscheck @assert all(
KnuthBendix.hasinverse(l, A) for l in KnuthBendix.letters(A) KnuthBendix.hasinverse(l, A) for l in A
) )
ltrs = KnuthBendix.letters(A) gens = Vector{eltype(A)}()
gens = Vector{eltype(ltrs)}() invs = Vector{eltype(A)}()
invs = Vector{eltype(ltrs)}() for l in A
for l in ltrs
l invs && continue l invs && continue
push!(gens, l) push!(gens, l)
push!(invs, inv(A, l)) push!(invs, inv(l, A))
end end
return FreeGroup(gens, A) return FreeGroup(gens, A)
@ -193,10 +197,9 @@ Base.show(io::IO, F::FreeGroup) =
print(io, "free group on $(ngens(F)) generators") print(io, "free group on $(ngens(F)) generators")
# mandatory methods: # mandatory methods:
relations(F::FreeGroup) = Pair{eltype(F)}[]
KnuthBendix.ordering(F::FreeGroup) = F.ordering KnuthBendix.ordering(F::FreeGroup) = F.ordering
KnuthBendix.alphabet(F::FreeGroup) = alphabet(KnuthBendix.ordering(F)) rewriting(F::FreeGroup) = alphabet(F) # alphabet(F) = alphabet(ordering(F))
rewriting(F::FreeGroup) = alphabet(F) relations(F::FreeGroup) = Pair{eltype(F),eltype(F)}[]
# GroupsCore interface: # GroupsCore interface:
# these are mathematically correct # these are mathematically correct
@ -207,22 +210,20 @@ GroupsCore.isfiniteorder(g::AbstractFPGroupElement{<:FreeGroup}) =
## FP Groups ## FP Groups
struct FPGroup{T,R,S} <: AbstractFPGroup struct FPGroup{T,RW,S} <: AbstractFPGroup
gens::Vector{T} gens::Vector{T}
relations::Vector{Pair{S,S}} relations::Vector{Pair{S,S}}
rws::R rw::RW
end end
relations(G::FPGroup) = G.relations relations(G::FPGroup) = G.relations
rewriting(G::FPGroup) = G.rws rewriting(G::FPGroup) = G.rw
KnuthBendix.ordering(G::FPGroup) = KnuthBendix.ordering(rewriting(G))
KnuthBendix.alphabet(G::FPGroup) = alphabet(KnuthBendix.ordering(G))
function FPGroup( function FPGroup(
G::AbstractFPGroup, G::AbstractFPGroup,
rels::AbstractVector{<:Pair{GEl,GEl}}; rels::AbstractVector{<:Pair{GEl,GEl}};
ordering=KnuthBendix.ordering(G), ordering=KnuthBendix.ordering(G),
kwargs..., kwargs...
) where {GEl<:FPGroupElement} ) where {GEl<:FPGroupElement}
for (lhs, rhs) in rels for (lhs, rhs) in rels
@assert parent(lhs) === parent(rhs) === G @assert parent(lhs) === parent(rhs) === G
@ -230,9 +231,9 @@ function FPGroup(
word_rels = [word(lhs) => word(rhs) for (lhs, rhs) in [relations(G); rels]] word_rels = [word(lhs) => word(rhs) for (lhs, rhs) in [relations(G); rels]]
rws = KnuthBendix.RewritingSystem(word_rels, ordering) rws = KnuthBendix.RewritingSystem(word_rels, ordering)
KnuthBendix.knuthbendix!(rws; kwargs...) rws = KnuthBendix.knuthbendix(rws, KnuthBendix.Settings(; kwargs...))
return FPGroup(G.gens, rels, rws) return FPGroup(G.gens, rels, KnuthBendix.IndexAutomaton(rws))
end end
function Base.show(io::IO, ::MIME"text/plain", G::FPGroup) function Base.show(io::IO, ::MIME"text/plain", G::FPGroup)

View File

@ -1,11 +1,22 @@
""" """
wlmetric_ball(S::AbstractVector{<:GroupElem} wlmetric_ball(S::AbstractVector{<:GroupElem}
[, center=one(first(S)); radius=2, op=*]) [, center=one(first(S)); radius=2, op=*, threading=true])
Compute metric ball as a list of elements of non-decreasing length, given the 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` 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 (by default: the identity element). `radius` and `op` keywords specify the
radius and multiplication operation to be used. radius and multiplication operation to be used.
""" """
function wlmetric_ball(
S::AbstractVector{T},
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)
end
function wlmetric_ball_serial(S::AbstractVector{T}, center::T=one(first(S)); radius=2, op=*) where {T} function wlmetric_ball_serial(S::AbstractVector{T}, center::T=one(first(S)); radius=2, op=*) where {T}
@assert radius >= 1 @assert radius >= 1
old = union!([center], [center * s for s in S]) old = union!([center], [center * s for s in S])
@ -26,6 +37,7 @@ function _wlmetric_ball(S, old, radius, op, collect, unique)
(g = op(o, s); hash(g); g) (g = op(o, s); hash(g); g)
for o in @view(old[sizes[end-1]:end]) for s in S for o in @view(old[sizes[end-1]:end]) for s in S
) )
append!(old, new) append!(old, new)
unique(old) unique(old)
end end
@ -34,13 +46,3 @@ function _wlmetric_ball(S, old, radius, op, collect, unique)
return old, sizes[2:end] return old, sizes[2:end]
end end
function wlmetric_ball(
S::AbstractVector{T},
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)
end

View File

@ -79,7 +79,7 @@
@test inv(l)(deepcopy(D)) == (a, d^-1 * b, c, d) @test inv(l)(deepcopy(D)) == (a, d^-1 * b, c, d)
end end
A = SpecialAutomorphismGroup(F4, maxrules=1000) A = SpecialAutomorphismGroup(F4, max_rules=1000)
@testset "AutomorphismGroup constructors" begin @testset "AutomorphismGroup constructors" begin
@test A isa Groups.AbstractFPGroup @test A isa Groups.AbstractFPGroup
@ -91,12 +91,12 @@
@testset "Automorphisms: hash and evaluate" begin @testset "Automorphisms: hash and evaluate" begin
@test Groups.domain(gens(A, 1)) == D @test Groups.domain(gens(A, 1)) == D
g, h = gens(A, 1), gens(A, 8) g, h = gens(A, 1), gens(A, 8) # (ϱ₁.₂, ϱ₃.₂)
@test evaluate(g * h) == evaluate(h * g) @test evaluate(g * h) == evaluate(h * g)
@test (g * h).savedhash == zero(UInt) @test (g * h).savedhash == zero(UInt)
@test sprint(show, typeof(g)) == "Automorphism{FreeGroup{Symbol, KnuthBendix.LenLex{Symbol}}, …}" @test contains(sprint(show, typeof(g)), "Automorphism{FreeGroup{Symbol")
a = g * h a = g * h
b = h * g b = h * g

View File

@ -3,16 +3,18 @@
π₁Σ = Groups.SurfaceGroup(genus, 0) π₁Σ = Groups.SurfaceGroup(genus, 0)
@test contains(sprint(print, π₁Σ), "surface")
Groups.PermRightAut(p::Perm) = Groups.PermRightAut(p.d) Groups.PermRightAut(p::Perm) = Groups.PermRightAut(p.d)
# Groups.PermLeftAut(p::Perm) = Groups.PermLeftAut(p.d) # Groups.PermLeftAut(p::Perm) = Groups.PermLeftAut(p.d)
autπ₁Σ = let autπ₁Σ = AutomorphismGroup(π₁Σ) autπ₁Σ = let autπ₁Σ = AutomorphismGroup(π₁Σ)
pauts = let p = perm"(1,3,5)(2,4,6)" pauts = let p = perm"(1,3,5)(2,4,6)"
[Groups.PermRightAut(p^i) for i in 0:2] [Groups.PermRightAut(p^i) for i in 0:2]
end end
T = eltype(KnuthBendix.letters(alphabet(autπ₁Σ))) T = eltype(alphabet(autπ₁Σ))
S = eltype(pauts) S = eltype(pauts)
A = Alphabet(Union{T,S}[KnuthBendix.letters(alphabet(autπ₁Σ)); pauts]) A = Alphabet(Union{T,S}[alphabet(autπ₁Σ)...; pauts])
autG = AutomorphismGroup( autG = AutomorphismGroup(
π₁Σ, π₁Σ,
@ -27,9 +29,7 @@
Al = alphabet(autπ₁Σ) Al = alphabet(autπ₁Σ)
S = [gens(autπ₁Σ); inv.(gens(autπ₁Σ))] S = [gens(autπ₁Σ); inv.(gens(autπ₁Σ))]
sautFn = let ltrs = KnuthBendix.letters(Al) sautFn = parent(Al[1].autFn_word)
parent(first(ltrs).autFn_word)
end
τ = Groups.rotation_element(sautFn) τ = Groups.rotation_element(sautFn)

View File

@ -40,7 +40,7 @@
end end
# quotient of G # quotient of G
H = FPGroup(G, [aG^2=>cG, bG*cG=>aG], maxrules=200) H = FPGroup(G, [aG^2 => cG, bG * cG => aG], max_rules=200)
h = H(word(g)) h = H(word(g))
@ -48,7 +48,7 @@
@test_throws AssertionError h == g @test_throws AssertionError h == g
@test_throws MethodError h * g @test_throws MethodError h * g
H = FPGroup(G, [aG^2=>cG, bG*cG=>aG], maxrules=200) H = FPGroup(G, [aG^2 => cG, bG * cG => aG], max_rules=200)
@test_throws AssertionError one(H) == one(H) @test_throws AssertionError one(H) == one(H)
Groups.normalform!(h) Groups.normalform!(h)

View File

@ -4,9 +4,10 @@ using Groups.MatrixGroups
@testset "SL(n, )" begin @testset "SL(n, )" begin
SL3Z = SpecialLinearGroup{3}(Int8) SL3Z = SpecialLinearGroup{3}(Int8)
S = gens(SL3Z); union!(S, inv.(S)) S = gens(SL3Z)
union!(S, inv.(S))
E, sizes = Groups.wlmetric_ball(S, radius=4) _, sizes = Groups.wlmetric_ball(S, radius=4)
@test sizes == [13, 121, 883, 5455] @test sizes == [13, 121, 883, 5455]
@ -17,10 +18,11 @@ using Groups.MatrixGroups
r = E(2, 3)^-3 r = E(2, 3)^-3
s = E(1, 3)^2 * E(3, 2)^-1 s = E(1, 3)^2 * E(3, 2)^-1
S = [w,r,s]; S = unique([S; inv.(S)]); S = [w, r, s]
_, sizes = Groups.wlmetric_ball(S, radius=4); S = unique([S; inv.(S)])
_, sizes = Groups.wlmetric_ball(S, radius=4)
@test sizes == [7, 33, 141, 561] @test sizes == [7, 33, 141, 561]
_, sizes = Groups.wlmetric_ball_serial(S, radius=4); _, sizes = Groups.wlmetric_ball_serial(S, radius=4)
@test sizes == [7, 33, 141, 561] @test sizes == [7, 33, 141, 561]
Logging.with_logger(Logging.NullLogger()) do Logging.with_logger(Logging.NullLogger()) do
@ -50,6 +52,7 @@ using Groups.MatrixGroups
@testset "Sp(6, )" begin @testset "Sp(6, )" begin
Sp6 = MatrixGroups.SymplecticGroup{6}(Int8) Sp6 = MatrixGroups.SymplecticGroup{6}(Int8)
Logging.with_logger(Logging.NullLogger()) do
@testset "GroupsCore conformance" begin @testset "GroupsCore conformance" begin
test_Group_interface(Sp6) test_Group_interface(Sp6)
g = Sp6(rand(1:length(alphabet(Sp6)), 10)) g = Sp6(rand(1:length(alphabet(Sp6)), 10))
@ -57,6 +60,7 @@ using Groups.MatrixGroups
test_GroupElement_interface(g, h) test_GroupElement_interface(g, h)
end end
end
@test contains(sprint(print, Sp6), "group of 6×6 symplectic matrices") @test contains(sprint(print, Sp6), "group of 6×6 symplectic matrices")

View File

@ -24,7 +24,7 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl"))
_, t = @timed include("homomorphisms.jl") _, t = @timed include("homomorphisms.jl")
@info "homomorphisms.jl took $(round(t, digits=2))s" @info "homomorphisms.jl took $(round(t, digits=2))s"
if haskey(ENV, "CI") if !haskey(ENV, "CI")
_, t = @timed include("AutSigma_41.jl") _, t = @timed include("AutSigma_41.jl")
@info "AutSigma_41 took $(round(t, digits=2))s" @info "AutSigma_41 took $(round(t, digits=2))s"
_, t = @timed include("AutSigma3.jl") _, t = @timed include("AutSigma3.jl")