diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml new file mode 100644 index 0000000..f05a64a --- /dev/null +++ b/.github/workflows/CompatHelper.yml @@ -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 }} diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index f49313b..f389611 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -4,6 +4,11 @@ on: types: - created workflow_dispatch: + inputs: + lookback: + default: 3 +permissions: + contents: write jobs: TagBot: if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' diff --git a/.github/workflows/runtests.yml b/.github/workflows/ci.yml similarity index 60% rename from .github/workflows/runtests.yml rename to .github/workflows/ci.yml index 24406d7..47976f9 100644 --- a/.github/workflows/runtests.yml +++ b/.github/workflows/ci.yml @@ -1,11 +1,7 @@ name: CI on: - push: - branches: - - master - pull_request: - branches: - - master + - pull_request + - push jobs: test: name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} @@ -22,21 +18,31 @@ jobs: - windows-latest arch: - x64 + allow_failures: + - julia: nightly fail-fast: false - steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.version }} 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-runtest@latest - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v1 + - uses: codecov/codecov-action@v2 with: file: ./lcov.info flags: unittests name: codecov-umbrella fail_ci_if_error: false - token: ${{ secrets.CODECOV_TOKEN }} diff --git a/Project.toml b/Project.toml index 93f0e92..d9251fa 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Groups" uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557" authors = ["Marek Kaluba "] -version = "0.7.3" +version = "0.7.4" [deps] Folds = "41a02a25-b8f0-4f67-bc48-60067656b558" @@ -16,7 +16,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [compat] Folds = "0.2.7" GroupsCore = "0.4" -KnuthBendix = "0.3" +KnuthBendix = "0.4" OrderedCollections = "1" PermutationGroups = "0.3" StaticArrays = "1" diff --git a/README.md b/README.md index 79523ed..b2fc191 100644 --- a/README.md +++ b/README.md @@ -10,25 +10,25 @@ The package implements `AbstractFPGroup` with three concrete types: `FreeGroup`, julia> using Groups, GroupsCore julia> A = Alphabet([:a, :A, :b, :B, :c, :C], [2, 1, 4, 3, 6, 5]) -Alphabet of Symbol: - 1. :a = (:A)⁻¹ - 2. :A = (:a)⁻¹ - 3. :b = (:B)⁻¹ - 4. :B = (:b)⁻¹ - 5. :c = (:C)⁻¹ - 6. :C = (:c)⁻¹ +Alphabet of Symbol + 1. a (inverse of: A) + 2. A (inverse of: a) + 3. b (inverse of: B) + 4. B (inverse of: b) + 5. c (inverse of: C) + 6. C (inverse of: c) julia> F = FreeGroup(A) free group on 3 generators 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 b c julia> a*inv(a) -(empty word) +(id) julia> (a*b)^2 a*b*a*b @@ -40,65 +40,75 @@ julia> x = a*b; y = inv(b)*a; julia> x*y a^2 - ``` + +## FPGroup Let's create a quotient of the free group above: ```julia -julia> ε = one(F); - -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> ε = 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) => ε ], 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> G = FPGroup(F, [a^2 => ε, b^3=> ε, (a*b)^7=>ε, (a*b*a*inv(b))^6 => ε, commutator(a, c) => ε, commutator(b, c) => ε ], maxrules=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)⟩ +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) +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> 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> using Random; Random.seed!(1); w = Groups.Word(rand(1:length(A), 16)); -julia> F(w) # freely reduced w -B*C*a^4*c*A*b*C*A*B*A*C +julia> length(w), w # word of itself +(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 -B*a*b*a*B*a*C^2 +julia> f = F(w) # freely reduced w +a*b*c*B*C*A*c^3*A*B^2 -julia> F(w) # freely reduced w -B*C*a^4*c*A*b*C*A*B*A*C +julia> length(word(f)), word(f) # the underlying word in F +(12, 1·3·5·4·6·2·5·5·5·2·4·4) -julia> word(ans) # the underlying word in A -KnuthBendix.Word{UInt8}: 4·6·1·1·1·1·5·2·3·6·2·4·2·6 - -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> g = G(w) # w as an element of G +a*b*c^3 +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. -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> saut = SpecialAutomorphismGroup(F, maxrules=100) -┌ 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 +julia> saut = SpecialAutomorphismGroup(F, max_rules=1000) automorphism group of free group on 3 generators 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> f = x*y*inv(z) -ϱ₁.₂*λ₃.₂*ϱ₃.₂^-1 +julia> f = x*y*inv(z); -julia> g = inv(z)*y*x -ϱ₃.₂^-1*ϱ₁.₂*λ₃.₂ +julia> g = inv(z)*y*x; 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: ```julia julia> evaluate(f) # or to be more verbose... @@ -147,7 +155,7 @@ This is what is happening behind the scenes: 2. if resulting words are equal `true` is returned 3. if they are not equal `Groups.equality_data` is computed for each argument (here: the images of generators) and the result of comparison is returned. -Moreover we try to amortize the cost of computing those images. That is a hash of `equality_daata` is lazily stored in each group element and used as needed. Essentially only if `true` is returned, but comparison of words returns `false` recomputation of images is needed (to guard against hash collisions). +Moreover we try to amortize the cost of computing those images. That is a hash of `equality_daata` is lazily stored in each group element and used as needed. Essentially only if `true` is returned, but comparison of words returns `false` recomputation of images is needed (to guard against hash collisions). ---- This package was developed for computations in [1712.07167](https://arxiv.org/abs/1712.07167) and in [1812.03456](https://arxiv.org/abs/1812.03456). If you happen to use this package please cite either of them. diff --git a/src/Groups.jl b/src/Groups.jl index 7c97e77..f34a7b7 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -10,7 +10,7 @@ import OrderedCollections: OrderedSet import KnuthBendix import KnuthBendix: AbstractWord, Alphabet, Word -import KnuthBendix: alphabet +import KnuthBendix: alphabet, ordering export MatrixGroups diff --git a/src/abelianize.jl b/src/abelianize.jl index e059feb..4f47f0f 100644 --- a/src/abelianize.jl +++ b/src/abelianize.jl @@ -1,7 +1,7 @@ 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] @@ -10,10 +10,10 @@ function _abelianize( # Automorphisms act on the right which corresponds to action on # the columns in the matrix case eij = MatrixGroups.ElementaryMatrix{N}( - aut.j, - aut.i, - ifelse(aut.inv, -one(T), one(T)) - ) + aut.j, + aut.i, + ifelse(aut.inv, -one(T), one(T)) + ) k = alphabet(target)[eij] return word_type(target)([k]) else @@ -24,7 +24,7 @@ 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,8 +39,8 @@ end function Groups._abelianize( i::Integer, source::AutomorphismGroup{<:Groups.SurfaceGroup}, - target::MatrixGroups.SymplecticGroup{N, T} -) where {N, T} + target::MatrixGroups.SymplecticGroup{N,T} +) where {N,T} @assert iseven(N) As = alphabet(source) At = alphabet(target) @@ -53,7 +53,7 @@ function Groups._abelianize( 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_repr(g) => word(g) for g in union(S, inv.(S))) end # renumeration: @@ -63,7 +63,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_repr(ab(g))[p, p] return matrix_spn_map[Mg] end diff --git a/src/aut_groups/gersten_relations.jl b/src/aut_groups/gersten_relations.jl index 79014b8..d91a8ef 100644 --- a/src/aut_groups/gersten_relations.jl +++ b/src/aut_groups/gersten_relations.jl @@ -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] @@ -6,7 +6,7 @@ function gersten_alphabet(n::Integer; commutative::Bool = true) append!(S, [λ(i, j) for (i, j) in indexing]) end - return Alphabet(S) + return Alphabet(mapreduce(x -> [x, inv(x)], union, S)) end function _commutation_rule( @@ -41,12 +41,12 @@ function _hexagonal_rule( end gersten_relations(n::Integer; commutative) = - gersten_relations(Word{UInt8}, n, commutative = commutative) + gersten_relations(Word{UInt8}, n, commutative=commutative) 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) - @assert length(A) <= KnuthBendix._max_alphabet_length(W) "Type $W can not represent words over alphabet with $(length(A)) letters." + 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}[] @@ -96,7 +96,7 @@ function gersten_relations(::Type{W}, n::Integer; commutative) where {W<:Abstrac if i ≠ j 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(A, w)^2) + push!(rels, w^2 => inv(w, A)^2) end end end diff --git a/src/aut_groups/mcg.jl b/src/aut_groups/mcg.jl index 5d9041f..1fc6286 100644 --- a/src/aut_groups/mcg.jl +++ b/src/aut_groups/mcg.jl @@ -1,9 +1,9 @@ -struct SurfaceGroup{T, S, R} <: AbstractFPGroup +struct SurfaceGroup{T,S,RW} <: AbstractFPGroup genus::Int boundaries::Int gens::Vector{T} relations::Vector{<:Pair{S,S}} - rws::R + rw::RW end include("symplectic_twists.jl") @@ -17,7 +17,7 @@ function Base.show(io::IO, S::SurfaceGroup) end end -function SurfaceGroup(genus::Integer, boundaries::Integer) +function SurfaceGroup(genus::Integer, boundaries::Integer, W=Word{Int16}) @assert genus > 1 # The (confluent) rewriting systems comes from @@ -30,15 +30,15 @@ function SurfaceGroup(genus::Integer, boundaries::Integer) ltrs = String[] for i in 1:genus - subscript = join('₀'+d for d in reverse(digits(i))) + subscript = join('₀' + d for d in reverse(digits(i))) append!(ltrs, ["A" * subscript, "a" * subscript, "B" * subscript, "b" * subscript]) end Al = Alphabet(reverse!(ltrs)) for i in 1:genus - subscript = join('₀'+d for d in reverse(digits(i))) - KnuthBendix.set_inversion!(Al, "a" * subscript, "A" * subscript) - KnuthBendix.set_inversion!(Al, "b" * subscript, "B" * subscript) + subscript = join('₀' + d for d in reverse(digits(i))) + KnuthBendix.setinverse!(Al, "a" * subscript, "A" * subscript) + KnuthBendix.setinverse!(Al, "b" * subscript, "B" * subscript) end if boundaries == 0 @@ -46,43 +46,44 @@ function SurfaceGroup(genus::Integer, boundaries::Integer) for i in reverse(1:genus) x = 4 * i - append!(word, [x, x-2, x-1, x-3]) + append!(word, [x, x - 2, x - 1, x - 3]) end - comms = Word(word) - word_rels = [ comms => one(comms) ] + comms = W(word) + word_rels = [comms => one(comms)] - rws = KnuthBendix.RewritingSystem(word_rels, KnuthBendix.RecursivePathOrder(Al)) - KnuthBendix.knuthbendix!(rws) + rws = let R = KnuthBendix.RewritingSystem(word_rels, KnuthBendix.Recursive(Al)) + KnuthBendix.IndexAutomaton(KnuthBendix.knuthbendix(R)) + end elseif boundaries == 1 - S = typeof(one(Word(Int[]))) - word_rels = Pair{S, S}[] - rws = RewritingSystem(word_rels, KnuthBendix.LenLex(Al)) + word_rels = Pair{W,W}[] + rws = let R = RewritingSystem(word_rels, KnuthBendix.LenLex(Al)) + KnuthBendix.IndexAutomaton(KnuthBendix.knuthbendix(R)) + end else - throw("Not Implemented") + throw("Not Implemented for MCG with $boundaryies boundary components") end - F = FreeGroup(alphabet(rws)) - rels = [F(lhs)=>F(rhs) for (lhs,rhs) in word_rels] + F = FreeGroup(Al) + 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 -rewriting(S::SurfaceGroup) = S.rws -KnuthBendix.alphabet(S::SurfaceGroup) = alphabet(rewriting(S)) +rewriting(S::SurfaceGroup) = S.rw relations(S::SurfaceGroup) = S.relations function symplectic_twists(π₁Σ::SurfaceGroup) 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] - Bij = [SymplecticMappingClass(saut, :B, i, j) for i in 1:g for j in 1:g if i≠j] + Bij = [SymplecticMappingClass(saut, :B, i, j) for i in 1:g for j in 1:g if i ≠ j] - mBij = [SymplecticMappingClass(saut, :B, i, j, minus=true) for i in 1:g for j in 1:g if i≠j] + mBij = [SymplecticMappingClass(saut, :B, i, j, minus=true) for i in 1:g for j in 1:g if i ≠ j] - Bii = [SymplecticMappingClass(saut, :B, i, i) for i in 1:g] + Bii = [SymplecticMappingClass(saut, :B, i, i) for i in 1:g] mBii = [SymplecticMappingClass(saut, :B, i, i, minus=true) for i in 1:g] @@ -98,6 +99,6 @@ function AutomorphismGroup(π₁Σ::SurfaceGroup; kwargs...) # this is to fix the definitions of symplectic twists: # with i->gens(π₁Σ, i) the corresponding automorphisms return # reversed words - domain = ntuple(i->inv(gens(π₁Σ, i)), 2genus(π₁Σ)) + domain = ntuple(i -> inv(gens(π₁Σ, i)), 2genus(π₁Σ)) return AutomorphismGroup(π₁Σ, S, A, domain) end diff --git a/src/aut_groups/sautFn.jl b/src/aut_groups/sautFn.jl index 4e86a37..b0805fa 100644 --- a/src/aut_groups/sautFn.jl +++ b/src/aut_groups/sautFn.jl @@ -1,25 +1,25 @@ 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) - S = KnuthBendix.letters(A)[1:2(n^2-n)] + A, rels = gersten_relations(n, commutative=false) + S = [A[i] for i in 1:2:length(A)] - maxrules = 1000*n + max_rules = 1000 * n - rws = KnuthBendix.RewritingSystem(rels, ordering(A)) - Logging.with_logger(Logging.NullLogger()) do + 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; 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 -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}) n = length(alphabet(object(G))) ÷ 2 - return last(gersten_relations(n, commutative = false)) + return last(gersten_relations(n, commutative=false)) end diff --git a/src/aut_groups/symplectic_twists.jl b/src/aut_groups/symplectic_twists.jl index f9f36dd..7a48233 100644 --- a/src/aut_groups/symplectic_twists.jl +++ b/src/aut_groups/symplectic_twists.jl @@ -25,7 +25,7 @@ function Te_diagonal(λ::Groups.ΡΛ, ϱ::Groups.ΡΛ, i::Integer) if i == n τ = rotation_element(λ, ϱ) - return inv(A, τ) * Te_diagonal(λ, ϱ, 1) * τ + return inv(τ, A) * Te_diagonal(λ, ϱ, 1) * τ end @assert 1 <= i < n @@ -37,32 +37,32 @@ function Te_diagonal(λ::Groups.ΡΛ, ϱ::Groups.ΡΛ, i::Integer) g = one(Word(Int[])) g *= λ[NJ, NI] # β ↦ α*β - g *= λ[NI, I] * inv(A, ϱ[NI, J]) # α ↦ a*α*b^-1 - g *= inv(A, λ[NJ, NI]) # β ↦ b*α^-1*a^-1*α*β - g *= λ[J, NI] * inv(A, λ[J, I]) # b ↦ α - g *= inv(A, λ[J, NI]) # b ↦ b*α^-1*a^-1*α - g *= inv(A, ϱ[J, NI]) * ϱ[J, I] # b ↦ b*α^-1*a^-1*α*b*α^-1 + g *= λ[NI, I] * inv(ϱ[NI, J], A) # α ↦ a*α*b^-1 + g *= inv(λ[NJ, NI], A) # β ↦ b*α^-1*a^-1*α*β + g *= λ[J, NI] * inv(λ[J, I], A) # b ↦ α + g *= inv(λ[J, NI], A) # b ↦ b*α^-1*a^-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 return g end function Te_lantern(A::Alphabet, b₀::T, a₁::T, a₂::T, a₃::T, a₄::T, a₅::T) where {T} - a₀ = (a₁ * a₂ * a₃)^4 * inv(A, b₀) + a₀ = (a₁ * a₂ * a₃)^4 * inv(b₀, A) 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₂ - return inv(A, Y) * b₁ * Y # b₂ from Primer + return inv(Y, A) * b₁ * Y # b₂ from Primer end function Ta(λ::Groups.ΡΛ, i::Integer) - @assert λ.id == :λ; - return λ[mod1(λ.N-2i+1, λ.N), mod1(λ.N-2i+2, λ.N)] + @assert λ.id == :λ + return λ[mod1(λ.N - 2i + 1, λ.N), mod1(λ.N - 2i + 2, λ.N)] end function Tα(λ::Groups.ΡΛ, i::Integer) - @assert λ.id == :λ; - return inv(λ.A, λ[mod1(λ.N-2i+2, λ.N), mod1(λ.N-2i+1, λ.N)]) + @assert λ.id == :λ + return inv(λ[mod1(λ.N - 2i + 2, λ.N), mod1(λ.N - 2i + 1, λ.N)], λ.A) end function Te(λ::ΡΛ, ϱ::ΡΛ, i, j) @@ -73,7 +73,7 @@ function Te(λ::ΡΛ, ϱ::ΡΛ, i, j) @assert λ.id == :λ && ϱ.id == :ϱ @assert iseven(λ.N) - genus = λ.N÷2 + genus = λ.N ÷ 2 i = mod1(i, genus) j = mod1(j, genus) @@ -85,16 +85,16 @@ function Te(λ::ΡΛ, ϱ::ΡΛ, i, j) if mod(j - (i + 1), genus) == 0 return Te_diagonal(λ, ϱ, i) else - return inv(A, Te_lantern( - A, - # Our notation: # Primer notation: - inv(A, Ta(λ, i + 1)), # b₀ - inv(A, Ta(λ, i)), # a₁ - inv(A, Tα(λ, i)), # a₂ - inv(A, Te_diagonal(λ, ϱ, i)), # a₃ - inv(A, Tα(λ, i + 1)), # a₄ - inv(A, Te(λ, ϱ, i + 1, j)), # a₅ - )) + return inv(Te_lantern( + A, + # Our notation: # Primer notation: + inv(Ta(λ, i + 1), A), # b₀ + inv(Ta(λ, i), A), # a₁ + inv(Tα(λ, i), A), # a₂ + inv(Te_diagonal(λ, ϱ, i), A), # a₃ + inv(Tα(λ, i + 1), A), # a₄ + inv(Te(λ, ϱ, i + 1, j), A), # a₅ + ), A) end end @@ -118,29 +118,29 @@ end function rotation_element(λ::ΡΛ, ϱ::ΡΛ) @assert iseven(λ.N) - genus = λ.N÷2 + genus = λ.N ÷ 2 A = λ.A halftwists = map(1:genus-1) do i j = i + 1 - x = Ta(λ, j) * inv(A, Ta(λ, i)) * Tα(λ, j) * Te_diagonal(λ, ϱ, i) - δ = x * Tα(λ, i) * inv(A, x) + x = Ta(λ, j) * inv(Ta(λ, i), A) * Tα(λ, j) * Te_diagonal(λ, ϱ, i) + δ = x * Tα(λ, i) * inv(x, A) c = - inv(A, Ta(λ, j)) * + inv(Ta(λ, j), A) * Te(λ, ϱ, i, j) * Tα(λ, i)^2 * - inv(A, δ) * - inv(A, Ta(λ, j)) * + inv(δ, A) * + inv(Ta(λ, j), A) * Ta(λ, i) * δ z = Te_diagonal(λ, ϱ, i) * - inv(A, Ta(λ, i)) * + inv(Ta(λ, i), A) * Tα(λ, 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 τ = (Ta(λ, 1) * Tα(λ, 1))^6 * prod(halftwists) @@ -168,7 +168,7 @@ function mcg_twists(G::AutomorphismGroup{<:FreeGroup}) return Tas, Tαs, Tes end -struct SymplecticMappingClass{T, F} <: GSymbol +struct SymplecticMappingClass{T,F} <: GSymbol id::Symbol # :A, :B i::UInt j::UInt @@ -187,13 +187,13 @@ 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 @assert iseven(ngens(object(sautFn))) - genus = ngens(object(sautFn))÷2 + genus = ngens(object(sautFn)) ÷ 2 A = alphabet(sautFn) λ = ΡΛ(:λ, A, 2genus) @@ -201,24 +201,24 @@ function SymplecticMappingClass( w = if id === :A Te(λ, ϱ, i, j) * - inv(A, Ta(λ, i)) * + inv(Ta(λ, i), A) * Tα(λ, i) * Ta(λ, i) * - inv(A, Te(λ, ϱ, i, j)) * - inv(A, Tα(λ, i)) * - inv(A, Ta(λ, j)) + inv(Te(λ, ϱ, i, j), A) * + inv(Tα(λ, i), A) * + inv(Ta(λ, j), A) elseif id === :B if !minus if i ≠ j - x = Ta(λ, j) * inv(A, Ta(λ, i)) * Tα(λ, j) * Te(λ, ϱ, i, j) - δ = x * Tα(λ, i) * inv(A, x) - Tα(λ, i) * Tα(λ, j) * inv(A, δ) + x = Ta(λ, j) * inv(Ta(λ, i), A) * Tα(λ, j) * Te(λ, ϱ, i, j) + δ = x * Tα(λ, i) * inv(x, A) + Tα(λ, i) * Tα(λ, j) * inv(δ, A) else - inv(A, Tα(λ, i)) + inv(Tα(λ, i), A) end else if i ≠ j - Ta(λ, i) * Ta(λ, j) * inv(A, Te(λ, ϱ, i, j)) + Ta(λ, i) * Ta(λ, j) * inv(Te(λ, ϱ, i, j), A) else Ta(λ, i) end @@ -241,7 +241,7 @@ end function Base.show(io::IO, smc::SymplecticMappingClass) smc.minus && print(io, 'm') - if smc.i < 10 && smc.j < 10 + if smc.i < 10 && smc.j < 10 print(io, smc.id, subscriptify(smc.i), subscriptify(smc.j)) else print(io, smc.id, subscriptify(smc.i), ".", subscriptify(smc.j)) diff --git a/src/aut_groups/transvections.jl b/src/aut_groups/transvections.jl index ce6238b..63c9717 100644 --- a/src/aut_groups/transvections.jl +++ b/src/aut_groups/transvections.jl @@ -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 @@ -33,7 +33,7 @@ Base.hash(t::Transvection, h::UInt) = hash(hash(t.id, hash(t.i)), hash(t.j, hash 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) @@ -45,9 +45,9 @@ Base.@propagate_inbounds @inline function evaluate!( if !t.inv append!(word(v[i]), word(v[j])) 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])) - push!(word(v[i]), inv(A, l)) + push!(word(v[i]), inv(l, A)) end end else # if t.id === :λ @@ -57,9 +57,9 @@ Base.@propagate_inbounds @inline function evaluate!( pushfirst!(word(v[i]), l) end 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]) - pushfirst!(word(v[i]), inv(A, l)) + pushfirst!(word(v[i]), inv(l, A)) end end end @@ -92,4 +92,4 @@ 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] +evaluate!(v::NTuple{T,N}, p::PermRightAut, tmp=nothing) where {T,N} = v[p.perm] diff --git a/src/autgroups.jl b/src/autgroups.jl index 696270a..c255b47 100644 --- a/src/autgroups.jl +++ b/src/autgroups.jl @@ -4,15 +4,15 @@ function KnuthBendix.Alphabet(S::AbstractVector{<:GSymbol}) return Alphabet(S, inversions) end -struct AutomorphismGroup{G<:Group,T,R,S} <: AbstractFPGroup +struct AutomorphismGroup{G<:Group,T,RW,S} <: AbstractFPGroup group::G gens::Vector{T} - rws::R + rw::RW domain::S end object(G::AutomorphismGroup) = G.group -rewriting(G::AutomorphismGroup) = G.rws +rewriting(G::AutomorphismGroup) = G.rw function equality_data(f::AbstractFPGroupElement{<:AutomorphismGroup}) imf = evaluate(f) @@ -104,8 +104,8 @@ evaluate(f::AbstractFPGroupElement{<:AutomorphismGroup}) = evaluate!(domain(f), function evaluate!( t::NTuple{N,T}, f::AbstractFPGroupElement{<:AutomorphismGroup{<:Group}}, - tmp = one(first(t)), -) where {N, T<:FPGroupElement} + tmp=one(first(t)), +) where {N,T<:FPGroupElement} A = alphabet(f) for idx in word(f) t = @inbounds evaluate!(t, A[idx], tmp)::NTuple{N,T} @@ -113,12 +113,12 @@ 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)))`") +evaluate!(t::NTuple{N,T}, s::GSymbol, tmp=nothing) where {N,T} = throw("you need to implement `evaluate!(::$(typeof(t)), ::$(typeof(s)), ::Alphabet, tmp=one(first(t)))`") # forward evaluate by substitution -struct LettersMap{T, A} - indices_map::Dict{Int, T} +struct LettersMap{T,A} + indices_map::Dict{Int,T} A::A end @@ -144,11 +144,11 @@ end function Base.getindex(lm::LettersMap, i::Integer) # 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) - img = if haskey(lm.indices_map, inv(lm.A, i)) - inv(lm.A, lm.indices_map[inv(lm.A, i)]) + img = if haskey(lm.indices_map, inv(i, lm.A)) + inv(lm.indices_map[inv(i, lm.A)], lm.A) else @warn "LetterMap: neither $i nor its inverse has assigned value" one(valtype(lm.indices_map)) @@ -193,7 +193,7 @@ function generated_evaluate(a::FPGroupElement{<:AutomorphismGroup}) push!(args[idx].args, :(d[$k])) continue end - k = findfirst(==(inv(A, l)), first_ltrs) + k = findfirst(==(inv(l, A)), first_ltrs) if k !== nothing push!(args[idx].args, :(inv(d[$k]))) continue @@ -201,13 +201,14 @@ function generated_evaluate(a::FPGroupElement{<:AutomorphismGroup}) throw("Letter $l doesn't seem to be mapped anywhere!") end end - locals = Dict{Expr, Symbol}() + locals = Dict{Expr,Symbol}() locals_counter = 0 - for (i,v) in enumerate(args) + for (i, v) in enumerate(args) @assert length(v.args) >= 2 if length(v.args) > 2 for (j, a) in pairs(v.args) - if a isa Expr && a.head == :call "$a" + if a isa Expr && a.head == :call + "$a" @assert a.args[1] == :inv if !(a in keys(locals)) locals[a] = Symbol("var_#$locals_counter") @@ -222,7 +223,7 @@ function generated_evaluate(a::FPGroupElement{<:AutomorphismGroup}) end q = quote - $([:(local $v = $k) for (k,v) in locals]...) + $([:(local $v = $k) for (k, v) in locals]...) end # return args, locals diff --git a/src/constructions/direct_power.jl b/src/constructions/direct_power.jl index 7ecfd2b..25ffb4f 100644 --- a/src/constructions/direct_power.jl +++ b/src/constructions/direct_power.jl @@ -30,7 +30,7 @@ function Base.iterate(G::DirectPower) 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 +38,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} @@ -52,7 +52,7 @@ 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)) -GroupsCore.ngens(G::DirectPower) = _nfold(G)*ngens(G.group) +GroupsCore.ngens(G::DirectPower) = _nfold(G) * ngens(G.group) function GroupsCore.gens(G::DirectPower, i::Integer) k = ngens(G.group) @@ -66,7 +66,7 @@ end function GroupsCore.gens(G::DirectPower) N = _nfold(G) S = gens(G.group) - tups = [ntuple(j->(i == j ? s : one(s)), N) for i in 1:N for s in S] + tups = [ntuple(j -> (i == j ? s : one(s)), N) for i in 1:N for s in S] return [DirectPowerElement(elts, G) for elts in tups] end @@ -99,7 +99,7 @@ function Base.:(*)(g::DirectPowerElement, h::DirectPowerElement) 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))) + convert(I, reduce(lcm, (order(I, h) for h in g.elts), init=one(I))) Base.isone(g::DirectPowerElement) = all(isone, g.elts) diff --git a/src/constructions/direct_product.jl b/src/constructions/direct_product.jl index 2f6db67..0ea6dfe 100644 --- a/src/constructions/direct_product.jl +++ b/src/constructions/direct_product.jl @@ -25,7 +25,7 @@ function Base.iterate(G::DirectProduct) 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 +33,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} diff --git a/src/constructions/wreath_product.jl b/src/constructions/wreath_product.jl index 41ad021..aa61198 100644 --- a/src/constructions/wreath_product.jl +++ b/src/constructions/wreath_product.jl @@ -52,7 +52,7 @@ function Base.iterate(G::WreathProduct) res = iterate(itr) @assert res !== nothing elt = WreathProductElement(first(res)..., G) - return elt, (iterator = itr, state = last(res)) + return elt, (iterator=itr, state=last(res)) end function Base.iterate(G::WreathProduct, state) @@ -60,7 +60,7 @@ function Base.iterate(G::WreathProduct, state) res = iterate(itr, st) res === nothing && return nothing elt = WreathProductElement(first(res)..., G) - return elt, (iterator = itr, state = last(res)) + return elt, (iterator=itr, state=last(res)) end function Base.IteratorSize(::Type{<:WreathProduct{DP,PGr}}) where {DP,PGr} diff --git a/src/homomorphisms.jl b/src/homomorphisms.jl index f241ba0..d045d47 100644 --- a/src/homomorphisms.jl +++ b/src/homomorphisms.jl @@ -58,8 +58,8 @@ true ``` """ -struct Homomorphism{Gr1, Gr2, I, W} - gens_images::Dict{I, W} +struct Homomorphism{Gr1,Gr2,I,W} + gens_images::Dict{I,W} source::Gr1 target::Gr2 @@ -70,11 +70,11 @@ struct Homomorphism{Gr1, Gr2, I, W} 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) + hom = new{typeof(source),typeof(target),I,W}(dct, source, target) if check @assert hom(one(source)) == one(target) @@ -83,12 +83,12 @@ struct Homomorphism{Gr1, Gr2, I, W} @assert hom(x^-1) == hom(x)^-1 for y in gens(source) - @assert hom(x*y) == hom(x)*hom(y) - @assert hom(x*y)^-1 == hom(y^-1)*hom(x^-1) + @assert hom(x * y) == hom(x) * hom(y) + @assert hom(x * y)^-1 == hom(y^-1) * hom(x^-1) end end for (lhs, rhs) in relations(source) - relator = lhs*inv(alphabet(source), rhs) + relator = lhs * inv(rhs, alphabet(source)) im_r = hom.target(hom(relator)) @assert isone(im_r) "Map does not define a homomorphism: h($relator) = $(im_r) ≠ $(one(target))." end diff --git a/src/iteration.jl b/src/iteration.jl index 04ce088..210289b 100644 --- a/src/iteration.jl +++ b/src/iteration.jl @@ -1,4 +1,4 @@ -mutable struct FPGroupIter{S, T, GEl} +mutable struct FPGroupIter{S,T,GEl} seen::S seen_iter_state::T current::GEl diff --git a/src/matrix_groups/SLn.jl b/src/matrix_groups/SLn.jl index c4a1f04..5a783fa 100644 --- a/src/matrix_groups/SLn.jl +++ b/src/matrix_groups/SLn.jl @@ -1,12 +1,12 @@ include("eltary_matrices.jl") -struct SpecialLinearGroup{N, T, R, A, S} <: MatrixGroup{N,T} +struct SpecialLinearGroup{N,T,R,A,S} <: MatrixGroup{N,T} base_ring::R alphabet::A gens::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] + 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] alphabet = Alphabet(S) return new{ @@ -19,16 +19,16 @@ struct SpecialLinearGroup{N, T, R, A, S} <: MatrixGroup{N,T} 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") function Base.show( io::IO, ::MIME"text/plain", sl::Groups.AbstractFPGroupElement{<:SpecialLinearGroup{N}} -) where N +) where {N} Groups.normalform!(sl) diff --git a/src/matrix_groups/Spn.jl b/src/matrix_groups/Spn.jl index 4287773..6d98bae 100644 --- a/src/matrix_groups/Spn.jl +++ b/src/matrix_groups/Spn.jl @@ -1,11 +1,11 @@ include("eltary_symplectic.jl") -struct SymplecticGroup{N, T, R, A, S} <: MatrixGroup{N,T} +struct SymplecticGroup{N,T,R,A,S} <: MatrixGroup{N,T} base_ring::R alphabet::A gens::S - function SymplecticGroup{N}(base_ring) where N + function SymplecticGroup{N}(base_ring) where {N} S = symplectic_gens(N, eltype(base_ring)) alphabet = Alphabet(S) @@ -21,7 +21,7 @@ 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}) where {N} = print(io, "group of $N×$N symplectic matrices") function Base.show( io::IO, @@ -35,15 +35,15 @@ function Base.show( Base.print_array(io, matrix_repr(sp)) end -_offdiag_idcs(n) = ((i,j) for i in 1:n for j in 1:n if i ≠ j) +_offdiag_idcs(n) = ((i, j) for i in 1:n for j in 1:n if i ≠ j) function symplectic_gens(N, T=Int8) iseven(N) || throw(ArgumentError("N needs to be even!")) - n = N÷2 + n = N ÷ 2 - a_ijs = [ElementarySymplectic{N}(:A, i,j, one(T)) for (i,j) in _offdiag_idcs(n)] - 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)] + 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)] S = [a_ijs; b_is; c_ijs] @@ -53,18 +53,18 @@ function symplectic_gens(N, T=Int8) end function _std_symplectic_form(m::AbstractMatrix) - r,c = size(m) + r, c = size(m) r == c || return false iseven(r) || return false - n = r÷2 + n = r ÷ 2 𝕆 = zeros(eltype(m), n, n) - 𝕀 = one(eltype(m))*LinearAlgebra.I + 𝕀 = 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 diff --git a/src/matrix_groups/abstract.jl b/src/matrix_groups/abstract.jl index 1a4cf1f..1da2228 100644 --- a/src/matrix_groups/abstract.jl +++ b/src/matrix_groups/abstract.jl @@ -1,17 +1,17 @@ -abstract type MatrixGroup{N, T} <: Groups.AbstractFPGroup end -const MatrixGroupElement{N, T} = Groups.AbstractFPGroupElement{<:MatrixGroup{N, T}} +abstract type MatrixGroup{N,T} <: Groups.AbstractFPGroup end +const MatrixGroupElement{N,T} = Groups.AbstractFPGroupElement{<:MatrixGroup{N,T}} -Base.isone(g::MatrixGroupElement{N, T}) where {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} +function Base.:(==)(m1::M1, m2::M2) where {M1<:MatrixGroupElement,M2<:MatrixGroupElement} parent(m1) === parent(m2) || return false word(m1) == word(m2) && return true return matrix_repr(m1) == matrix_repr(m2) end -Base.size(m::MatrixGroupElement{N}) where N = (N, N) -Base.eltype(m::MatrixGroupElement{N, T}) where {N, T} = T +Base.size(m::MatrixGroupElement{N}) where {N} = (N, N) +Base.eltype(m::MatrixGroupElement{N,T}) where {N,T} = T # three structural assumptions about matrix groups Groups.word(sl::MatrixGroupElement) = sl.word @@ -22,9 +22,9 @@ Groups.rewriting(M::MatrixGroup) = alphabet(M) Base.hash(sl::MatrixGroupElement, h::UInt) = hash(matrix_repr(sl), hash(parent(sl), h)) -function matrix_repr(m::MatrixGroupElement{N, T}) where {N, T} +function matrix_repr(m::MatrixGroupElement{N,T}) where {N,T} if isone(word(m)) - return StaticArrays.SMatrix{N, N, T}(LinearAlgebra.I) + return StaticArrays.SMatrix{N,N,T}(LinearAlgebra.I) end A = alphabet(parent(m)) return prod(matrix_repr(A[l]) for l in word(m)) @@ -33,7 +33,7 @@ end function Base.rand( rng::Random.AbstractRNG, rs::Random.SamplerTrivial{<:MatrixGroup}, - ) +) Mgroup = rs[] S = gens(Mgroup) return prod(g -> rand(Bool) ? g : inv(g), rand(S, rand(1:30))) diff --git a/src/matrix_groups/eltary_matrices.jl b/src/matrix_groups/eltary_matrices.jl index 947faca..6e3ed48 100644 --- a/src/matrix_groups/eltary_matrices.jl +++ b/src/matrix_groups/eltary_matrices.jl @@ -1,9 +1,9 @@ -struct ElementaryMatrix{N, T} <: Groups.GSymbol +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)) + ElementaryMatrix{N}(i, j, val=1) where {N} = + (@assert i ≠ j; new{N,typeof(val)}(i, j, val)) end function Base.show(io::IO, e::ElementaryMatrix) @@ -11,18 +11,18 @@ function Base.show(io::IO, e::ElementaryMatrix) !isone(e.val) && print(io, "^$(e.val)") 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 Base.hash(e::ElementaryMatrix, h::UInt) = 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) -function matrix_repr(e::ElementaryMatrix{N, T}) where {N, T} - m = StaticArrays.MMatrix{N, N, T}(LinearAlgebra.I) +function matrix_repr(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) + x = StaticArrays.SMatrix{N,N}(m) return x end diff --git a/src/matrix_groups/eltary_symplectic.jl b/src/matrix_groups/eltary_symplectic.jl index c1d3d68..0640e6b 100644 --- a/src/matrix_groups/eltary_symplectic.jl +++ b/src/matrix_groups/eltary_symplectic.jl @@ -1,18 +1,18 @@ -struct ElementarySymplectic{N, T} <: Groups.GSymbol +struct ElementarySymplectic{N,T} <: Groups.GSymbol symbol::Symbol 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 + n = N ÷ 2 if s === :A @assert 1 ≤ i ≤ n && 1 ≤ j ≤ n && i ≠ j elseif s === :B @assert xor(1 ≤ i ≤ n, 1 ≤ j ≤ n) && xor(n < i ≤ N, n < j ≤ N) end - return new{N, typeof(val)}(s, i, j, val) + return new{N,typeof(val)}(s, i, j, val) end end @@ -22,12 +22,12 @@ function Base.show(io::IO, s::ElementarySymplectic) !isone(s.val) && print(io, "^$(s.val)") end -_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) -function _dual_ind(s::ElementarySymplectic{N}) where N +_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) +function _dual_ind(s::ElementarySymplectic{N}) where {N} if s.symbol === :A && return _ind(s) else#if s.symbol === :B - return _dual_ind(N÷2, s.i, s.j) + return _dual_ind(N ÷ 2, s.i, s.j) end end @@ -41,7 +41,7 @@ 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 @@ -51,18 +51,18 @@ end Base.hash(s::ElementarySymplectic, h::UInt) = 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) -Base.inv(s::ElementarySymplectic{N}) where N = +Base.inv(s::ElementarySymplectic{N}) where {N} = 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} @assert iseven(N) n = div(N, 2) - m = StaticArrays.MMatrix{N, N, T}(LinearAlgebra.I) - i,j = _ind(s) - m[i,j] = s.val + m = StaticArrays.MMatrix{N,N,T}(LinearAlgebra.I) + i, j = _ind(s) + m[i, j] = s.val if s.symbol === :A m[n+j, n+i] = -s.val else#if s.symbol === :B @@ -72,5 +72,5 @@ function matrix_repr(s::ElementarySymplectic{N, T}) where {N, T} m[j-n, i+n] = s.val end end - return StaticArrays.SMatrix{N, N}(m) + return StaticArrays.SMatrix{N,N}(m) end diff --git a/src/normalform.jl b/src/normalform.jl index 0b799e3..7b7742c 100644 --- a/src/normalform.jl +++ b/src/normalform.jl @@ -42,5 +42,5 @@ Defaults to the rewriting in the free group. """ @inline function normalform!(res::AbstractWord, g::AbstractFPGroupElement) isone(res) && isnormalform(g) && return append!(res, word(g)) - return KnuthBendix.rewrite_from_left!(res, word(g), rewriting(parent(g))) + return KnuthBendix.rewrite!(res, word(g), rewriting(parent(g))) end diff --git a/src/types.jl b/src/types.jl index a96e5dc..bbe332f 100644 --- a/src/types.jl +++ b/src/types.jl @@ -3,15 +3,18 @@ """ 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)` * `rewriting(G::MyFPGroup)` : return the rewriting object which must implement - > `KnuthBendix.rewrite_from_left!(u, v, rewriting(G))`. -By default `alphabet(G)` is returned, which amounts to free rewriting in `G`. + > `KnuthBendix.rewrite!(u, v, rewriting(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. -AbstractFPGroup may also override `word_type(::Type{MyFPGroup}) = Word{UInt16}`, -which controls the word type used for group elements. If a group has more than `255` generators you need to define e.g. +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. > `word_type(::Type{MyFPGroup}) = Word{UInt16}` """ abstract type AbstractFPGroup <: GroupsCore.Group end @@ -22,22 +25,25 @@ word_type(::Type{<:AbstractFPGroup}) = Word{UInt8} """ rewriting(G::AbstractFPGroup) -Return a "rewriting object" for elements of `G`. The rewriting object must must implement - KnuthBendix.rewrite_from_left!( - u::AbstractWord, - v::AbstractWord, - rewriting(G) - ) +Return a "rewriting object" for elements of `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 +KnuthBendix.ordering(G::AbstractFPGroup) = ordering(rewriting(G)) +KnuthBendix.alphabet(G::AbstractFPGroup) = alphabet(ordering(G)) + Base.@propagate_inbounds function (G::AbstractFPGroup)( word::AbstractVector{<:Integer}, ) @boundscheck @assert all( - l -> 1 <= l <= length(KnuthBendix.alphabet(G)), + l -> 1 <= l <= length(alphabet(G)), word, ) return FPGroupElement(word_type(G)(word), G) @@ -90,7 +96,7 @@ mutable struct FPGroupElement{Gr<:AbstractFPGroup,W<:AbstractWord} <: FPGroupElement( word::W, G::AbstractFPGroup, - hash::UInt = UInt(0), + hash::UInt=UInt(0), ) where {W<:AbstractWord} = new{typeof(G),W}(word, hash, G) FPGroupElement{Gr,W}(word::AbstractWord, G::Gr) where {Gr,W} = @@ -128,7 +134,7 @@ end function Base.inv(g::GEl) where {GEl<:AbstractFPGroupElement} G = parent(g) - return GEl(inv(alphabet(G), word(g)), G) + return GEl(inv(word(g), alphabet(G)), G) end function Base.:(*)(g::GEl, h::GEl) where {GEl<:AbstractFPGroupElement} @@ -153,8 +159,7 @@ struct FreeGroup{T,O} <: AbstractFPGroup function FreeGroup(gens, ordering::KnuthBendix.WordOrdering) @assert length(gens) == length(unique(gens)) - L = KnuthBendix.letters(alphabet(ordering)) - @assert all(l -> l in L, gens) + @assert all(l -> l in alphabet(ordering), gens) return new{eltype(gens),typeof(ordering)}(gens, ordering) end end @@ -163,15 +168,14 @@ FreeGroup(gens, A::Alphabet) = FreeGroup(gens, KnuthBendix.LenLex(A)) function FreeGroup(A::Alphabet) @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(ltrs)}() - invs = Vector{eltype(ltrs)}() - for l in ltrs + gens = Vector{eltype(A)}() + invs = Vector{eltype(A)}() + for l in A l ∈ invs && continue push!(gens, l) - push!(invs, inv(A, l)) + push!(invs, inv(l, A)) end return FreeGroup(gens, A) @@ -193,10 +197,9 @@ Base.show(io::IO, F::FreeGroup) = print(io, "free group on $(ngens(F)) generators") # mandatory methods: -relations(F::FreeGroup) = Pair{eltype(F)}[] KnuthBendix.ordering(F::FreeGroup) = F.ordering -KnuthBendix.alphabet(F::FreeGroup) = alphabet(KnuthBendix.ordering(F)) -rewriting(F::FreeGroup) = alphabet(F) +rewriting(F::FreeGroup) = alphabet(F) # alphabet(F) = alphabet(ordering(F)) +relations(F::FreeGroup) = Pair{eltype(F),eltype(F)}[] # GroupsCore interface: # these are mathematically correct @@ -207,22 +210,20 @@ GroupsCore.isfiniteorder(g::AbstractFPGroupElement{<:FreeGroup}) = ## FP Groups -struct FPGroup{T,R,S} <: AbstractFPGroup +struct FPGroup{T,RW,S} <: AbstractFPGroup gens::Vector{T} relations::Vector{Pair{S,S}} - rws::R + rw::RW end relations(G::FPGroup) = G.relations -rewriting(G::FPGroup) = G.rws -KnuthBendix.ordering(G::FPGroup) = KnuthBendix.ordering(rewriting(G)) -KnuthBendix.alphabet(G::FPGroup) = alphabet(KnuthBendix.ordering(G)) +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 @@ -230,9 +231,9 @@ function FPGroup( word_rels = [word(lhs) => word(rhs) for (lhs, rhs) in [relations(G); rels]] 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 function Base.show(io::IO, ::MIME"text/plain", G::FPGroup) diff --git a/src/wl_ball.jl b/src/wl_ball.jl index 69fce79..18645cb 100644 --- a/src/wl_ball.jl +++ b/src/wl_ball.jl @@ -1,20 +1,31 @@ """ 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 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 radius and multiplication operation to be used. """ -function wlmetric_ball_serial(S::AbstractVector{T}, center::T=one(first(S)); radius = 2, op = *) where {T} +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} @assert radius >= 1 - old = union!([center], [center*s for s in S]) + old = union!([center], [center * s for s in S]) return _wlmetric_ball(S, old, radius, op, collect, unique!) end -function wlmetric_ball_thr(S::AbstractVector{T}, center::T=one(first(S)); radius = 2, op = *) where {T} +function wlmetric_ball_thr(S::AbstractVector{T}, center::T=one(first(S)); radius=2, op=*) where {T} @assert radius >= 1 - old = union!([center], [center*s for s in S]) + old = union!([center], [center * s for s in S]) return _wlmetric_ball(S, old, radius, op, Folds.collect, Folds.unique) end @@ -26,6 +37,7 @@ function _wlmetric_ball(S, old, radius, op, collect, unique) (g = op(o, s); hash(g); g) for o in @view(old[sizes[end-1]:end]) for s in S ) + append!(old, new) unique(old) end @@ -34,13 +46,3 @@ function _wlmetric_ball(S, old, radius, op, collect, unique) return old, sizes[2: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 diff --git a/test/AutFn.jl b/test/AutFn.jl index 8c566ea..c3f035b 100644 --- a/test/AutFn.jl +++ b/test/AutFn.jl @@ -29,57 +29,57 @@ end A4 = Alphabet( - [:a,:A,:b,:B,:c,:C,:d,:D], - [ 2, 1, 4, 3, 6, 5, 8, 7] + [:a, :A, :b, :B, :c, :C, :d, :D], + [2, 1, 4, 3, 6, 5, 8, 7] ) A5 = Alphabet( - [:a,:A,:b,:B,:c,:C,:d,:D,:e,:E], - [ 2, 1, 4, 3, 6, 5, 8, 7,10, 9] + [:a, :A, :b, :B, :c, :C, :d, :D, :e, :E], + [2, 1, 4, 3, 6, 5, 8, 7, 10, 9] ) F4 = FreeGroup([:a, :b, :c, :d], A4) - a,b,c,d = gens(F4) - D = ntuple(i->gens(F4, i), 4) + a, b, c, d = gens(F4) + D = ntuple(i -> gens(F4, i), 4) @testset "Transvection action correctness" begin - i,j = 1,2 - r = Groups.Transvection(:ϱ,i,j) - l = Groups.Transvection(:λ,i,j) + i, j = 1, 2 + r = Groups.Transvection(:ϱ, i, j) + l = Groups.Transvection(:λ, i, j) (t::Groups.Transvection)(v::Tuple) = Groups.evaluate!(v, t) - @test r(deepcopy(D)) == (a*b, b, c, d) - @test inv(r)(deepcopy(D)) == (a*b^-1,b, c, d) - @test l(deepcopy(D)) == (b*a, b, c, d) - @test inv(l)(deepcopy(D)) == (b^-1*a,b, c, d) + @test r(deepcopy(D)) == (a * b, b, c, d) + @test inv(r)(deepcopy(D)) == (a * b^-1, b, c, d) + @test l(deepcopy(D)) == (b * a, b, c, d) + @test inv(l)(deepcopy(D)) == (b^-1 * a, b, c, d) - i,j = 3,1 - r = Groups.Transvection(:ϱ,i,j) - l = Groups.Transvection(:λ,i,j) - @test r(deepcopy(D)) == (a, b, c*a, d) - @test inv(r)(deepcopy(D)) == (a, b, c*a^-1,d) - @test l(deepcopy(D)) == (a, b, a*c, d) - @test inv(l)(deepcopy(D)) == (a, b, a^-1*c,d) + i, j = 3, 1 + r = Groups.Transvection(:ϱ, i, j) + l = Groups.Transvection(:λ, i, j) + @test r(deepcopy(D)) == (a, b, c * a, d) + @test inv(r)(deepcopy(D)) == (a, b, c * a^-1, d) + @test l(deepcopy(D)) == (a, b, a * c, d) + @test inv(l)(deepcopy(D)) == (a, b, a^-1 * c, d) - i,j = 4,3 - r = Groups.Transvection(:ϱ,i,j) - l = Groups.Transvection(:λ,i,j) - @test r(deepcopy(D)) == (a, b, c, d*c) - @test inv(r)(deepcopy(D)) == (a, b, c, d*c^-1) - @test l(deepcopy(D)) == (a, b, c, c*d) - @test inv(l)(deepcopy(D)) == (a, b, c, c^-1*d) + i, j = 4, 3 + r = Groups.Transvection(:ϱ, i, j) + l = Groups.Transvection(:λ, i, j) + @test r(deepcopy(D)) == (a, b, c, d * c) + @test inv(r)(deepcopy(D)) == (a, b, c, d * c^-1) + @test l(deepcopy(D)) == (a, b, c, c * d) + @test inv(l)(deepcopy(D)) == (a, b, c, c^-1 * d) - i,j = 2,4 - r = Groups.Transvection(:ϱ,i,j) - l = Groups.Transvection(:λ,i,j) - @test r(deepcopy(D)) == (a, b*d, c, d) - @test inv(r)(deepcopy(D)) == (a, b*d^-1,c, d) - @test l(deepcopy(D)) == (a, d*b, c, d) - @test inv(l)(deepcopy(D)) == (a, d^-1*b,c, d) + i, j = 2, 4 + r = Groups.Transvection(:ϱ, i, j) + l = Groups.Transvection(:λ, i, j) + @test r(deepcopy(D)) == (a, b * d, c, d) + @test inv(r)(deepcopy(D)) == (a, b * d^-1, c, d) + @test l(deepcopy(D)) == (a, d * b, c, d) + @test inv(l)(deepcopy(D)) == (a, d^-1 * b, c, d) end - A = SpecialAutomorphismGroup(F4, maxrules=1000) + A = SpecialAutomorphismGroup(F4, max_rules=1000) @testset "AutomorphismGroup constructors" begin @test A isa Groups.AbstractFPGroup @@ -91,33 +91,33 @@ @testset "Automorphisms: hash and evaluate" begin @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 (g*h).savedhash == zero(UInt) + @test evaluate(g * h) == evaluate(h * g) + @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 - b = h*g + a = g * h + b = h * g @test hash(a) != zero(UInt) @test hash(a) == hash(b) @test a.savedhash == b.savedhash - @test length(unique([a,b])) == 1 - @test length(unique([g*h, h*g])) == 1 + @test length(unique([a, b])) == 1 + @test length(unique([g * h, h * g])) == 1 # Not so simple arithmetic: applying starting on the left: # ϱ₁₂*ϱ₂₁⁻¹*λ₁₂*ε₂ == σ₂₁₃₄ g = gens(A, 1) x1, x2, x3, x4 = Groups.domain(g) - @test evaluate(g) == (x1*x2, x2, x3, x4) + @test evaluate(g) == (x1 * x2, x2, x3, x4) - g = g*inv(gens(A, 4)) # ϱ₂₁ - @test evaluate(g) == (x1*x2, x1^-1, x3, x4) + g = g * inv(gens(A, 4)) # ϱ₂₁ + @test evaluate(g) == (x1 * x2, x1^-1, x3, x4) - g = g*gens(A, 13) + g = g * gens(A, 13) @test evaluate(g) == (x2, x1^-1, x3, x4) end @@ -128,7 +128,7 @@ S = gens(G) @test S isa Vector{<:FPGroupElement{<:AutomorphismGroup{<:FreeGroup}}} - @test length(S) == 2*N*(N-1) + @test length(S) == 2 * N * (N - 1) @test length(unique(S)) == length(S) S_sym = [S; inv.(S)] @@ -136,12 +136,12 @@ pushfirst!(S_sym, one(G)) - B_2 = [i*j for (i,j) in Base.product(S_sym, S_sym)] + B_2 = [i * j for (i, j) in Base.product(S_sym, S_sym)] @test length(B_2) == 2401 @test length(unique(B_2)) == 1777 - @test all(g->isone(inv(g)*g), B_2) - @test all(g->isone(g*inv(g)), B_2) + @test all(g -> isone(inv(g) * g), B_2) + @test all(g -> isone(g * inv(g)), B_2) end @testset "Forward evaluate" begin @@ -153,7 +153,7 @@ f = gens(F) - @test a(f[1]) == f[1]*f[2] + @test a(f[1]) == f[1] * f[2] @test all(a(f[i]) == f[i] for i in 2:length(f)) S = let s = gens(G) diff --git a/test/AutSigma3.jl b/test/AutSigma3.jl index 475fb57..8905342 100644 --- a/test/AutSigma3.jl +++ b/test/AutSigma3.jl @@ -3,22 +3,24 @@ π₁Σ = Groups.SurfaceGroup(genus, 0) + @test contains(sprint(print, π₁Σ), "surface") + Groups.PermRightAut(p::Perm) = Groups.PermRightAut(p.d) # Groups.PermLeftAut(p::Perm) = Groups.PermLeftAut(p.d) autπ₁Σ = let autπ₁Σ = AutomorphismGroup(π₁Σ) pauts = let p = perm"(1,3,5)(2,4,6)" [Groups.PermRightAut(p^i) for i in 0:2] end - T = eltype(KnuthBendix.letters(alphabet(autπ₁Σ))) + T = eltype(alphabet(autπ₁Σ)) S = eltype(pauts) - A = Alphabet(Union{T,S}[KnuthBendix.letters(alphabet(autπ₁Σ)); pauts]) + A = Alphabet(Union{T,S}[alphabet(autπ₁Σ)...; pauts]) autG = AutomorphismGroup( π₁Σ, autπ₁Σ.gens, A, - ntuple(i->inv(gens(π₁Σ, i)), 2Groups.genus(π₁Σ)) + ntuple(i -> inv(gens(π₁Σ, i)), 2Groups.genus(π₁Σ)) ) autG @@ -27,9 +29,7 @@ Al = alphabet(autπ₁Σ) S = [gens(autπ₁Σ); inv.(gens(autπ₁Σ))] - sautFn = let ltrs = KnuthBendix.letters(Al) - parent(first(ltrs).autFn_word) - end + sautFn = parent(Al[1].autFn_word) τ = Groups.rotation_element(sautFn) @@ -38,7 +38,7 @@ λ = Groups.ΡΛ(:λ, A, 2genus) ϱ = Groups.ΡΛ(:ϱ, A, 2genus) @test sautFn(Groups.Te_diagonal(λ, ϱ, 1)) == - conj(sautFn(Groups.Te_diagonal(λ, ϱ, 2)), τ) + conj(sautFn(Groups.Te_diagonal(λ, ϱ, 2)), τ) @test sautFn(Groups.Te_diagonal(λ, ϱ, 3)) == sautFn(Groups.Te(λ, ϱ, 3, 1)) end diff --git a/test/fp_groups.jl b/test/fp_groups.jl index e06f165..5e98dd5 100644 --- a/test/fp_groups.jl +++ b/test/fp_groups.jl @@ -1,5 +1,5 @@ @testset "FPGroups" begin - A = Alphabet([:a, :A, :b, :B, :c, :C], [2,1,4,3,6,5]) + A = Alphabet([:a, :A, :b, :B, :c, :C], [2, 1, 4, 3, 6, 5]) @test FreeGroup(A) isa FreeGroup @test sprint(show, FreeGroup(A)) == "free group on 3 generators" @@ -7,26 +7,26 @@ F = FreeGroup([:a, :b, :c], A) @test sprint(show, F) == "free group on 3 generators" - a,b,c = gens(F) - @test c*b*a isa FPGroupElement + a, b, c = gens(F) + @test c * b * a isa FPGroupElement # quotient of F: - G = FPGroup(F, [a*b=>b*a, a*c=>c*a, b*c=>c*b]) + 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 rand(G) isa FPGroupElement - f = a*c*b + f = a * c * b @test word(f) isa Word{UInt8} - aG,bG,cG = gens(G) + aG, bG, cG = gens(G) @test aG isa FPGroupElement @test_throws AssertionError aG == a @test word(aG) == word(a) - g = aG*cG*bG + g = aG * cG * bG @test_throws AssertionError f == g @test word(f) == word(g) @@ -34,21 +34,21 @@ Groups.normalform!(g) @test word(g) == [1, 3, 5] - let g = aG*cG*bG + let g = aG * cG * bG # test that we normalize g before printing @test sprint(show, g) == "a*b*c" end # 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)) @test h isa FPGroupElement @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′) Groups.normalform!(h) diff --git a/test/matrix_groups.jl b/test/matrix_groups.jl index 316ab8c..ef27c9d 100644 --- a/test/matrix_groups.jl +++ b/test/matrix_groups.jl @@ -4,23 +4,25 @@ using Groups.MatrixGroups @testset "SL(n, ℤ)" begin 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] - E(i,j) = SL3Z([A[MatrixGroups.ElementaryMatrix{3}(i,j, Int8(1))]]) + E(i, j) = SL3Z([A[MatrixGroups.ElementaryMatrix{3}(i, j, Int8(1))]]) A = alphabet(SL3Z) - w = E(1,2) - r = E(2,3)^-3 - s = E(1,3)^2*E(3,2)^-1 + w = E(1, 2) + r = E(2, 3)^-3 + s = E(1, 3)^2 * E(3, 2)^-1 - S = [w,r,s]; S = unique([S; inv.(S)]); - _, sizes = Groups.wlmetric_ball(S, radius=4); + 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_serial(S, radius=4) @test sizes == [7, 33, 141, 561] Logging.with_logger(Logging.NullLogger()) do @@ -34,10 +36,10 @@ using Groups.MatrixGroups end - x = w*inv(w)*r + x = w * inv(w) * r @test length(word(x)) == 5 - @test size(x) == (3,3) + @test size(x) == (3, 3) @test eltype(x) == Int8 @test contains(sprint(print, SL3Z), "special linear group of 3×3") @@ -50,12 +52,14 @@ using Groups.MatrixGroups @testset "Sp(6, ℤ)" begin Sp6 = MatrixGroups.SymplecticGroup{6}(Int8) - @testset "GroupsCore conformance" begin - test_Group_interface(Sp6) - g = Sp6(rand(1:length(alphabet(Sp6)), 10)) - h = Sp6(rand(1:length(alphabet(Sp6)), 10)) + Logging.with_logger(Logging.NullLogger()) do + @testset "GroupsCore conformance" begin + test_Group_interface(Sp6) + g = Sp6(rand(1:length(alphabet(Sp6)), 10)) + h = Sp6(rand(1:length(alphabet(Sp6)), 10)) - test_GroupElement_interface(g, h) + test_GroupElement_interface(g, h) + end end @test contains(sprint(print, Sp6), "group of 6×6 symplectic matrices") @@ -64,7 +68,7 @@ using Groups.MatrixGroups x *= inv(x) * gens(Sp6, 2) @test length(word(x)) == 3 - @test size(x) == (6,6) + @test size(x) == (6, 6) @test eltype(x) == Int8 @test contains(sprint(show, MIME"text/plain"(), x), "6×6 symplectic matrix:") diff --git a/test/runtests.jl b/test/runtests.jl index 061fac8..afe48a4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -24,7 +24,7 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl")) _, t = @timed include("homomorphisms.jl") @info "homomorphisms.jl took $(round(t, digits=2))s" - if haskey(ENV, "CI") + if !haskey(ENV, "CI") _, t = @timed include("AutSigma_41.jl") @info "AutSigma_41 took $(round(t, digits=2))s" _, t = @timed include("AutSigma3.jl") @@ -36,5 +36,5 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl")) end if !haskey(ENV, "CI") - include("benchmarks.jl") + include("benchmarks.jl") end