From 5810eeb4aed0a1e1e1bc1e114a6916c8ec9e109b Mon Sep 17 00:00:00 2001 From: kalmarek Date: Tue, 24 Mar 2020 23:44:03 +0100 Subject: [PATCH 01/34] create uniform hash interface using hash_internal --- src/AutGroup.jl | 19 +++++++++---------- src/FPGroups.jl | 2 -- src/FreeGroup.jl | 2 -- src/Groups.jl | 10 +++++++++- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index 7695278..09ed682 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -242,19 +242,18 @@ evaluate(f::Automorphism) = f(domain(parent(f))) const HASHINGCONST = 0x7d28276b01874b19 # hash(Automorphism) -hash(s::AutSymbol, h::UInt) = hash(s.id, hash(s.pow, hash(:AutSymbol, h))) +hash(s::AutSymbol, h::UInt) = hash(s.id, hash(s.pow, hash(AutSymbol, h))) -function hash(g::Automorphism{N}, images, h::UInt=HASHINGCONST) where N - return hash(images, hash(parent(g), hash(Automorphism{N}, h))) +function hash_internal(g::Automorphism, images = freereduce!.(evaluate(g)), + h::UInt = HASHINGCONST) + return hash(images, hash(parent(g), h)) end -function hash(g::Automorphism, h::UInt) - if g.modified - g_im = reduce!.(evaluate(g)) - g.savedhash = hash(g, g_im) - g.modified = false - end - return xor(g.savedhash, h) +function compute_images(g::Automorphism) + images = reduce!.(evaluate(g)) + g.savedhash = hash_internal(g, images) + unsetmodified!(g) + return images end function (==)(g::Automorphism{N}, h::Automorphism{N}) where N diff --git a/src/FPGroups.jl b/src/FPGroups.jl index e7aeb84..ef5b036 100644 --- a/src/FPGroups.jl +++ b/src/FPGroups.jl @@ -95,8 +95,6 @@ end # ############################################################################### -hash(s::FPSymbol, h::UInt) = hash(s.id, hash(s.pow, hash(FPSymbol, h))) - change_pow(s::FPSymbol, n::Int) = FPSymbol(s.id, n) length(s::FPSymbol) = abs(s.pow) diff --git a/src/FreeGroup.jl b/src/FreeGroup.jl index 9e3709e..5f77479 100644 --- a/src/FreeGroup.jl +++ b/src/FreeGroup.jl @@ -80,8 +80,6 @@ end # ############################################################################### -hash(s::FreeSymbol, h::UInt) = hash(s.id, hash(s.pow, hash(FreeSymbol, h))) - change_pow(s::FreeSymbol, n::Int) = FreeSymbol(s.id, n) length(s::FreeSymbol) = abs(s.pow) diff --git a/src/Groups.jl b/src/Groups.jl index ee021e8..64d4e25 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -101,8 +101,16 @@ convert(::Type{GroupWord{T}}, s::T) where {T<:GSymbol} = GroupWord{T}(T[s]) # ############################################################################### +function hash_internal(W::GWord) + reduce!(W) + return hash(syllables(W), hash(typeof(W), hash(parent(W)))) +end + function hash(W::GWord, h::UInt) - W.modified && reduce!(W) + if ismodified(W) + W.savedhash = hash_internal(W) + unsetmodified!(W) + end return xor(W.savedhash, h) end From 7b211014e3e8aac0ac1e9db1989dfc4dc1b3ed08 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Tue, 24 Mar 2020 23:47:50 +0100 Subject: [PATCH 02/34] create and use uniform API for GSymbols --- src/AutGroup.jl | 4 ---- src/FPGroups.jl | 6 ------ src/FreeGroup.jl | 6 ------ src/Groups.jl | 13 ++++++++++++- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index 09ed682..1c007a7 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -304,8 +304,6 @@ function change_pow(s::AutSymbol, n::Integer) end end -length(s::AutSymbol) = abs(s.pow) - ############################################################################### # # String I/O @@ -329,8 +327,6 @@ end # ############################################################################### -inv(f::AutSymbol) = change_pow(f, -f.pow) - ############################################################################### # # Misc diff --git a/src/FPGroups.jl b/src/FPGroups.jl index ef5b036..53b2ca7 100644 --- a/src/FPGroups.jl +++ b/src/FPGroups.jl @@ -95,10 +95,6 @@ end # ############################################################################### -change_pow(s::FPSymbol, n::Int) = FPSymbol(s.id, n) - -length(s::FPSymbol) = abs(s.pow) - ############################################################################### # # String I/O @@ -127,8 +123,6 @@ end # ############################################################################### -inv(s::FPSymbol) = change_pow(s, -s.pow) - ############################################################################### # # Binary operations diff --git a/src/FreeGroup.jl b/src/FreeGroup.jl index 5f77479..eab073a 100644 --- a/src/FreeGroup.jl +++ b/src/FreeGroup.jl @@ -80,10 +80,6 @@ end # ############################################################################### -change_pow(s::FreeSymbol, n::Int) = FreeSymbol(s.id, n) - -length(s::FreeSymbol) = abs(s.pow) - ############################################################################### # # String I/O @@ -106,5 +102,3 @@ end # Inversion # ############################################################################### - -inv(s::FreeSymbol) = change_pow(s, -s.pow) diff --git a/src/Groups.jl b/src/Groups.jl index 64d4e25..d9a5391 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -33,7 +33,18 @@ Base.one(G::Generic.PermGroup) = G(collect(1:G.n), false) """ abstract type GSymbol end -abstract type GWord{T<:GSymbol} <:GroupElem end +Base.iterate(s::GS, i=1) where GS<:GSymbol = i <= abs(s.pow) ? (GS(s.id, sign(s.pow)), i+1) : nothing +Base.length(s::GSymbol) = abs(s.pow) +Base.size(s::GSymbol) = (length(s), ) +Base.eltype(s::GS) where GS<:GSymbol = GS +Base.isone(s::GSymbol) = iszero(s.pow) + +change_pow(s::S, n::Integer) where S<:GSymbol = S(s.id, n) +Base.inv(s::GSymbol) = change_pow(s, -s.pow) + +hash(s::S, h::UInt) where S<:GSymbol = hash(s.id, hash(s.pow, hash(S, h))) + +abstract type GWord{T<:GSymbol} <: GroupElem end @doc doc""" W::GroupWord{T} <: GWord{T<:GSymbol} <:GroupElem From b2d6c14515949ae23afa1649c262186ebd67aa27 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Tue, 24 Mar 2020 23:53:29 +0100 Subject: [PATCH 03/34] use syllables api for GWords --- src/FPGroups.jl | 2 +- src/FreeGroup.jl | 2 +- src/Groups.jl | 48 ++++++++++++++++++++---------------------------- 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/src/FPGroups.jl b/src/FPGroups.jl index 53b2ca7..440c6dd 100644 --- a/src/FPGroups.jl +++ b/src/FPGroups.jl @@ -66,7 +66,7 @@ function Base.one(G::FPGroup) end function (G::FPGroup)(w::GWord) - if length(w) == 0 + if isempty(w) return one(G) end diff --git a/src/FreeGroup.jl b/src/FreeGroup.jl index eab073a..a9b1408 100644 --- a/src/FreeGroup.jl +++ b/src/FreeGroup.jl @@ -59,7 +59,7 @@ function Base.one(G::FreeGroup) end function (G::FreeGroup)(w::GroupWord{FreeSymbol}) - if length(w) > 0 + if length(syllables(w)) > 0 for s in w.symbols i = findfirst(g -> g.id == s.id, G.gens) i == 0 && throw(DomainError( diff --git a/src/Groups.jl b/src/Groups.jl index d9a5391..bc7752c 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -62,16 +62,22 @@ abstract type GWord{T<:GSymbol} <: GroupElem end """ mutable struct GroupWord{T} <: GWord{T} - symbols::Vector{T} - savedhash::UInt - modified::Bool - parent::Group + symbols::Vector{T} + modified::Bool + savedhash::UInt + parent::Group - function GroupWord{T}(symbols::Vector{T}) where {T} - return new{T}(symbols, hash(symbols), true) - end + function GroupWord{T}(symbols::Vector{T}) where {T} + return new{T}(symbols, true, zero(UInt)) + end end +syllablelength(w::GWord) = length(w.symbols) +syllables(w::GWord) = w.symbols +ismodified(w::GWord) = w.modified +setmodified!(w::GWord) = (w.modified = true; w) +unsetmodified!(w::GWord) = (w.modified = false; w) + abstract type AbstractFPGroup <: Group end ############################################################################### @@ -128,19 +134,7 @@ end # WARNING: Due to specialised (constant) hash function of GWords this one is actually necessary! function deepcopy_internal(W::T, dict::IdDict) where {T<:GWord} G = parent(W) - return G(T(deepcopy(W.symbols))) -end - -length(W::GWord) = sum([length(s) for s in W.symbols]) - -function deleteids!(W::GWord) - to_delete = Int[] - for i in 1:length(W.symbols) - if W.symbols[i].pow == 0 - push!(to_delete, i) - end - end - deleteat!(W.symbols, to_delete) + return G(T(deepcopy(syllables(W)))) end function freereduce!(W::GWord) @@ -157,7 +151,7 @@ function freereduce!(W::GWord) W.symbols[i] = change_pow(W.symbols[i], 0) end end - deleteids!(W) + filter!(!isone, syllables(w)) return reduced end @@ -242,10 +236,9 @@ function (==)(W::GWord, Z::GWord) end function (==)(s::GSymbol, t::GSymbol) - s.pow == t.pow || return false - s.pow == 0 && return true - s.id == t.id || return false - return true + isone(s) && isone(t) && return true + s.pow == t.pow && s.id == t.id && return true + return false end ############################################################################### @@ -333,13 +326,12 @@ end # ############################################################################### -function inv(W::T) where {T<:GWord} +function inv(W::T) where T<:GWord if length(W) == 0 return W else G = parent(W) - w = T(reverse([inv(s) for s in W.symbols])) - w.modified = true + w = T([inv(s) for s in Iterators.reverse(syllables(W))]) return G(w) end end From 8abebbbd0cacad16a7ceb1101ed25a5a258c4ecd Mon Sep 17 00:00:00 2001 From: kalmarek Date: Tue, 24 Mar 2020 23:55:42 +0100 Subject: [PATCH 04/34] use the new api in freereduce! --- src/Groups.jl | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/Groups.jl b/src/Groups.jl index bc7752c..c0af92a 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -137,38 +137,32 @@ function deepcopy_internal(W::T, dict::IdDict) where {T<:GWord} return G(T(deepcopy(syllables(W)))) end -function freereduce!(W::GWord) +function freereduce!(::Type{Bool}, w::GWord) reduced = true - for i in 1:length(W.symbols) - 1 - if W.symbols[i].pow == 0 + for i in 1:syllablelength(w)-1 + s, ns = syllables(w)[i], syllables(w)[i+1] + if isone(s) continue - elseif W.symbols[i].id == W.symbols[i+1].id + elseif s.id == ns.id reduced = false - p1 = W.symbols[i].pow - p2 = W.symbols[i+1].pow + setmodified!(w) + p1 = s.pow + p2 = ns.pow - W.symbols[i+1] = change_pow(W.symbols[i], p1 + p2) - W.symbols[i] = change_pow(W.symbols[i], 0) + syllables(w)[i+1] = change_pow(s, p1 + p2) + syllables(w)[i] = change_pow(s, 0) end end filter!(!isone, syllables(w)) return reduced end -function reduce!(W::GWord) - if length(W) < 2 - deleteids!(W) - else - reduced = false - while !reduced - reduced = freereduce!(W) - end +function freereduce!(w::GWord) + reduced = false + while !reduced + reduced = freereduce!(Bool, w) end - - W.savedhash = hash(W.symbols, hash(typeof(W), hash(parent(W), zero(UInt)))) - W.modified = false - - return W + return w end @doc doc""" From 8688d422503051c90fe93f9e6979642f77963c17 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Tue, 24 Mar 2020 23:56:30 +0100 Subject: [PATCH 05/34] reduce defaults to freereduce now --- src/Groups.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Groups.jl b/src/Groups.jl index c0af92a..363c0d4 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -165,16 +165,16 @@ function freereduce!(w::GWord) return w end +reduce!(w::GWord) = freereduce!(w) + @doc doc""" - reduce(W::GWord) + reduce(w::GWord) > performs reduction/simplification of a group element (word in generators). -> The default reduction is the free group reduction, i.e. consists of -> multiplying adjacent symbols with the same `id` identifier and deleting the -> identity elements from `W.symbols`. +> The default reduction is the free group reduction > More specific procedures should be dispatched on `GWord`s type parameter. """ -reduce(W::GWord) = reduce!(deepcopy(W)) +reduce(w::GWord) = reduce!(deepcopy(w)) @doc doc""" gens(G::AbstractFPGroups) From bc1063f0fdf9ea5ceaf310c6f086a3cf49ce3a2e Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 00:27:47 +0100 Subject: [PATCH 06/34] use the new reduce for Automorphisms and FPWords --- src/AutGroup.jl | 17 ++++------------- src/FPGroups.jl | 14 ++++---------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index 1c007a7..ceb9edf 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -361,20 +361,11 @@ function simplifyperms!(W::Automorphism{N}) where N return reduced end -function reduce!(W::Automorphism) - if length(W) == 0 - return W - elseif length(W.symbols) == 1 - deleteids!(W) - else - reduced = false - while !reduced - reduced = simplifyperms!(W) && freereduce!(W) - end +function reduce!(w::Automorphism) + reduced = false + while !reduced + reduced = simplifyperms!(Bool, w) && freereduce!(Bool, w) end - - W.modified = true - return W end diff --git a/src/FPGroups.jl b/src/FPGroups.jl index 440c6dd..878c4ba 100644 --- a/src/FPGroups.jl +++ b/src/FPGroups.jl @@ -134,17 +134,11 @@ end (*)(s::FPSymbol, W::FPGroupElem) = l_multiply(W, [s]) function reduce!(W::FPGroupElem) - if length(W) < 2 - deleteat!(W.symbols, findall(x -> x.pow == 0, W.symbols)) - else - reduced = false - while !reduced - reduced = freereduce!(W) || replace_all!(W, parent(W).rels) - end + reduced = false + while !reduced + W = replace(W, parent(W).rels) + reduced = freereduce!(Bool, W) end - - W.savedhash = hash(W.symbols, hash(typeof(W))) - W.modified = false return W end From 189850858f0fe6e06da41180906a16f35daeaa8a Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 00:38:07 +0100 Subject: [PATCH 07/34] add to syllables commit --- src/Groups.jl | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Groups.jl b/src/Groups.jl index 363c0d4..3840b96 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -216,17 +216,10 @@ end # ############################################################################### -function (==)(W::GWord, Z::GWord) - parent(W) == parent(Z) || return false - - W.modified && reduce!(W) - Z.modified && reduce!(Z) - - if W.savedhash != Z.savedhash - return false - end - - return W.symbols == Z.symbols +function (==)(W::T, Z::T) where T <: GWord + parent(W) != parent(Z) && return false + hash(W) != hash(Z) && return false + return syllables(W) == syllables(Z) end function (==)(s::GSymbol, t::GSymbol) From 6c53b3b7c0896844d9050626e679d7177cb71620 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 00:42:23 +0100 Subject: [PATCH 08/34] favour append! and prepend! in place of rmul! and lmul! --- src/AutGroup.jl | 6 ++++-- src/Groups.jl | 50 +++++++++++++++++++------------------------------ 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index ceb9edf..31acf96 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -65,12 +65,14 @@ parent_type(::Automorphism{N}) where N = AutGroup{N} ############################################################################### function (ϱ::RTransvect)(v, pow::Integer=1) - @inbounds Groups.r_multiply!(v[ϱ.i], (v[ϱ.j]^pow).symbols, reduced=false) + append!(v[ϱ.i], v[ϱ.j]^pow) + freereduce!(v[ϱ.i]) return v end function (λ::LTransvect)(v, pow::Integer=1) - @inbounds Groups.l_multiply!(v[λ.i], (v[λ.j]^pow).symbols, reduced=false) + prepend!(v[λ.i], v[λ.j]^pow) + freereduce!(v[λ.i]) return v end diff --git a/src/Groups.jl b/src/Groups.jl index 3840b96..e700437 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -234,44 +234,32 @@ end # ############################################################################### -function AbstractAlgebra.mul!(out::GWord, x::GWord, y::GWord; reduced::Bool=true) - resize!(out.symbols, length(x.symbols)+length(y.symbols)) - for i in eachindex(x.symbols) - out.symbols[i] = x.symbols[i] - end - for i in eachindex(y.symbols) - out.symbols[length(x.symbols)+i] = y.symbols[i] - end - if reduced - reduce!(out) - end - return out +function Base.append!(w::GWord{T}, v::AbstractVector{T}) where T + append!(syllables(w), v) + return w end -function r_multiply!(W::GWord, x; reduced::Bool=true) - if length(x) > 0 - append!(W.symbols, x) - end - if reduced - reduce!(W) - end - return W +function Base.prepend!(w::GWord{T}, v::AbstractVector{T}) where T + prepend!(syllables(w), v) + return w end -function l_multiply!(W::GWord, x; reduced::Bool=true) - if length(x) > 0 - prepend!(W.symbols, x) +Base.append!(w::T, v::T) where T <: GWord = append!(w, syllables(v)) +Base.prepend!(w::T, v::T) where T <: GWord = prepend!(w, syllables(v)) + +for (mul, f) in ((:rmul!, :push!), (:lmul!, :pushfirst!)) + @eval begin + function $mul(out::T, w::T, s::GSymbol) where T <:GWord + $f(syllables(out), s) + return freereduce!(out) + end + end +end + + end end - if reduced - reduce!(W) - end - return W end -r_multiply(W::GWord, x; reduced=true) = - r_multiply!(deepcopy(W),x, reduced=reduced) -l_multiply(W::GWord, x; reduced=true) = - l_multiply!(deepcopy(W),x, reduced=reduced) (*)(W::GWord, Z::GWord) = r_multiply(W, Z.symbols) (*)(W::GWord, s::GSymbol) = r_multiply(W, [s]) From 02ce259eb4f9f8f7b4fce8b7c76b6f4db9c4206a Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 00:43:04 +0100 Subject: [PATCH 09/34] rewrite simplifyperms! in the new word api --- src/AutGroup.jl | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index 31acf96..3838ad9 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -335,31 +335,22 @@ end # ############################################################################### -function getperm(s::AutSymbol) - if s.pow != 1 - @warn("Power for perm_symbol should be never 0!") - return s.fn.perm^s.pow - else - return s.fn.perm - end -end +getperm(s::AutSymbol) = s.fn.perm^s.pow -function simplifyperms!(W::Automorphism{N}) where N +function simplifyperms!(::Type{Bool}, w::Automorphism{N}) where N reduced = true - to_delete = Int[] - for i in 1:length(W.symbols)-1 - if W.symbols[i].pow == 0 + for i in 1:syllablelength(w)-1 + s, ns = syllables(w)[i], syllables(w)[i+1] + if isone(s) continue - elseif W.symbols[i].fn isa PermAut && W.symbols[i+1].fn isa PermAut + elseif s.fn isa PermAut && ns.fn isa PermAut reduced = false - c = W.symbols[i] - n = W.symbols[i+1] - W.symbols[i+1] = perm_autsymbol(getperm(c)*getperm(n)) - push!(to_delete, i) + setmodified!(w) + syllables(w)[i+1] = AutSymbol(getperm(s)*getperm(ns)) + syllables(w)[i] = change_pow(s, 0) end end - deleteat!(W.symbols, to_delete) - deleteids!(W) + filter!(!isone, syllables(w)) return reduced end From b9c2a90bae34c43d1c04808a9494f364a373602a Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 00:46:23 +0100 Subject: [PATCH 10/34] rename *_autsymbol to more descriptive names --- src/AutGroup.jl | 65 ++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index 3838ad9..7838011 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -25,14 +25,14 @@ end struct Identity end struct AutSymbol <: GSymbol - id::Symbol - pow::Int8 - fn::Union{LTransvect, RTransvect, PermAut, FlipAut, Identity} + id::Symbol + pow::Int8 + fn::Union{LTransvect, RTransvect, PermAut, FlipAut, Identity} end mutable struct AutGroup{N} <: AbstractFPGroup - objectGroup::FreeGroup - gens::Vector{AutSymbol} + objectGroup::FreeGroup + gens::Vector{AutSymbol} end mutable struct Automorphism{N} <: GWord{AutSymbol} @@ -113,42 +113,43 @@ function id_autsymbol() return AutSymbol(Symbol("(id)"), 0, Identity()) end -function rmul_autsymbol(i::Integer, j::Integer; pow::Integer=1) +function transvection_R(i::Integer, j::Integer, pow::Integer=1) id = Symbol("ϱ", subscriptify(i), subscriptify(j)) return AutSymbol(id, pow, RTransvect(i, j)) end -function lmul_autsymbol(i::Integer, j::Integer; pow::Integer=1) +function transvection_L(i::Integer, j::Integer, pow::Integer=1) id = Symbol("λ", subscriptify(i), subscriptify(j)) return AutSymbol(id, pow, LTransvect(i, j)) end -function flip_autsymbol(i::Integer; pow::Integer=1) - if iseven(pow) - return id_autsymbol() - else - id = Symbol("ɛ", subscriptify(i)) - return AutSymbol(id, 1, FlipAut(i)) - end +function flip(i::Integer, pow::Integer=1) + iseven(pow) && return id_autsymbol() + id = Symbol("ɛ", subscriptify(i)) + return AutSymbol(id, 1, FlipAut(i)) end -function perm_autsymbol(p::Generic.Perm{I}; pow::Integer=one(I)) where I<:Integer +function AutSymbol(p::Generic.Perm, pow::Integer=1) if pow != 1 p = p^pow end - for i in eachindex(p.d) - if p.d[i] != i - id = Symbol("σ", [subscriptify(i) for i in p.d]...) - return AutSymbol(id, 1, PermAut(p)) - end + + if any(p.d[i] != i for i in eachindex(p.d)) + id = Symbol("σ", "₍", [subscriptify(i) for i in p.d]..., "₎") + return AutSymbol(id, 1, PermAut(p)) end return id_autsymbol() end -function perm_autsymbol(a::Vector{<:Integer}) - return perm_autsymbol(Generic.Perm(Vector{Int8}(a), false)) +function AutSymbol(a::Vector{<:Integer}, pow=1) + return AutSymbol(Generic.Perm(convert(Vector{Int8}, a)), pow) end +ϱ(i::Integer, j::Integer, pow::Integer=1) = transvection_R(i, j, pow=pow) +λ(i::Integer, j::Integer, pow::Integer=1) = transvection_L(i, j, pow=pow) +ε(i::Integer, pow::Integer=1) = flip(i, pow=pow) +σ(v, pow=1) = AutSymbol(v, pow=pow) + function domain(G::AutGroup{N}) where N F = G.objectGroup gg = gens(F) @@ -168,17 +169,16 @@ function AutGroup(G::FreeGroup; special=false) indexing = [[i,j] for i in 1:n for j in 1:n if i≠j] - rmuls = [rmul_autsymbol(i,j) for (i,j) in indexing] - lmuls = [lmul_autsymbol(i,j) for (i,j) in indexing] + rmuls = [transvection_R(i,j) for (i,j) in indexing] + lmuls = [transvection_L(i,j) for (i,j) in indexing] append!(S, [rmuls; lmuls]) if !special - flips = [flip_autsymbol(i) for i in 1:n] - syms = [perm_autsymbol(p) for p in PermutationGroup(n)][2:end] + flips = [flip(i) for i in 1:n] + syms = [AutSymbol(p) for p in PermutationGroup(Int8(n))][2:end] append!(S, [flips; syms]) - end return AutGroup{n}(G, S) end @@ -291,18 +291,17 @@ function change_pow(s::AutSymbol, n::Integer) end symbol = s.fn if symbol isa FlipAut - return flip_autsymbol(symbol.i, pow=n) + return flip(symbol.i, n) elseif symbol isa PermAut - return perm_autsymbol(symbol.perm, pow=n) + return AutSymbol(symbol.perm, n) elseif symbol isa RTransvect - return rmul_autsymbol(symbol.i, symbol.j, pow=n) + return transvection_R(symbol.i, symbol.j, n) elseif symbol isa LTransvect - return lmul_autsymbol(symbol.i, symbol.j, pow=n) + return transvection_L(symbol.i, symbol.j, n) elseif symbol isa Identity return s else - warn("Changing power of an unknown type of symbol! $s") - return AutSymbol(s.id, n, s.fn) + throw(DomainError("Unknown type of AutSymbol: $s")) end end From a1ebf530f4091c2211321b28f04889c4cb09e060 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 00:47:45 +0100 Subject: [PATCH 11/34] in automoprhism evaluation no need to freereduce! Symbols already do so, where appropriate --- src/AutGroup.jl | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index 7838011..18c7af7 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -217,20 +217,16 @@ end # ############################################################################### -function (s::AutSymbol)(v::NTuple{N, T}) where {N, T} - if s.pow != 0 - v = s.fn(v, s.pow)::NTuple{N, T} - end - return v -end +(s::AutSymbol)(v::NTuple{N, T}) where {N, T} = s.fn(v, s.pow)::NTuple{N, T} function (f::Automorphism{N})(v::NTuple{N, T}) where {N, T} - for (i, s) in enumerate(f.symbols) + for s in syllables(f) v = s(v)::NTuple{N, T} - if i % 5 == 0 - freereduce!.(v) - end + # if iszero(i % 3) + # freereduce!.(v) + # end end + # return freereduce!.(v) return v end From 8248039d63bd6d7741fc747e2b63c08cc1ce81cd Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 00:51:53 +0100 Subject: [PATCH 12/34] simplify actions of PermAut and FlipAut --- src/AutGroup.jl | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index 18c7af7..accbd26 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -77,25 +77,19 @@ function (λ::LTransvect)(v, pow::Integer=1) end function (σ::PermAut)(v, pow::Integer=1) - w = deepcopy(v) - if pow == 1 - @inbounds for k in eachindex(v) - v[k].symbols = w[σ.perm.d[k]].symbols - end - else - s = (σ.perm^pow).d - @inbounds for k in eachindex(v) - v[k].symbols = w[s[k]].symbols - end - end - return v + w = deepcopy(v) + s = (σ.perm^pow).d + @inbounds for k in eachindex(v) + v[k].symbols = w[s[k]].symbols + end + return v end function (ɛ::FlipAut)(v, pow::Integer=1) - @inbounds if isodd(pow) - v[ɛ.i].symbols = inv(v[ɛ.i]).symbols - end - return v + @inbounds if isodd(pow) + v[ɛ.i].symbols = inv(v[ɛ.i]).symbols + end + return v end (::Identity)(v, pow::Integer=1) = v From 99d5bc2f8c8b1430fa0b51dc8d1a20634c4a3884 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 03:28:45 +0100 Subject: [PATCH 13/34] fast equality for Automorphisms --- src/AutGroup.jl | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index accbd26..d58c2b6 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -249,24 +249,29 @@ function compute_images(g::Automorphism) end function (==)(g::Automorphism{N}, h::Automorphism{N}) where N - parent(g) == parent(h) || return false + img_c, imh_c = false, false - if !g.modified && !h.modified - if g.savedhash != h.savedhash - return false - end + if ismodified(g) + img = compute_images(g) + img_c = true end - # expensive: - g_im = reduce!.(evaluate(g)) - h_im = reduce!.(evaluate(h)) - # cheap: - g.savedhash = hash(g, g_im) - g.modified = false - h.savedhash = hash(h, h_im) - h.modified = false + if ismodified(h) + imh = compute_images(h) + imh_c = true + end - return g_im == h_im + @assert !ismodified(g) && !ismodified(h) + # cheap + hash(g) != hash(h) && return false # hashes differ, so images must have differed as well + # equal elements, or possibly hash conflict + if !img_c + img = compute_images(g) + end + if !imh_c + imh = compute_images(h) + end + return img == imh end ############################################################################### From a3db467bd17d7fa1295fbbf917d1d52b161b70ec Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 03:33:43 +0100 Subject: [PATCH 14/34] make one fully abstract method --- src/AutGroup.jl | 8 +------- src/FPGroups.jl | 9 +-------- src/FreeGroup.jl | 6 ------ src/Groups.jl | 20 +++++++++++++++++--- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index d58c2b6..28374bb 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -54,7 +54,7 @@ export Automorphism, AutGroup, Aut, SAut # ############################################################################### -elem_type(::AutGroup{N}) where N = Automorphism{N} +elem_type(::Type{AutGroup{N}}) where N = Automorphism{N} parent_type(::Automorphism{N}) where N = AutGroup{N} @@ -188,12 +188,6 @@ SAut(G::Group) = AutGroup(G, special=true) Automorphism{N}(s::AutSymbol) where N = Automorphism{N}(AutSymbol[s]) -function Base.one(G::AutGroup{N}) where N - id = Automorphism{N}(id_autsymbol()) - id.parent = G - return id -end - function (G::AutGroup{N})(f::AutSymbol) where N g = Automorphism{N}([f]) g.parent = G diff --git a/src/FPGroups.jl b/src/FPGroups.jl index 878c4ba..0c472cb 100644 --- a/src/FPGroups.jl +++ b/src/FPGroups.jl @@ -30,10 +30,9 @@ export FPGroupElem, FPGroup # ############################################################################### +elem_type(::Type{FPGroup}) = FPGroupElem parent_type(::Type{FPGroupElem}) = FPGroup -elem_type(::FPGroup) = FPGroupElem - ############################################################################### # # FPSymbol constructors @@ -59,12 +58,6 @@ FPGroup(H::FreeGroup) = FPGroup([FPSymbol(s) for s in H.gens]) # ############################################################################### -function Base.one(G::FPGroup) - id = FPGroupElem(FPSymbol[]) - id.parent = G - return id -end - function (G::FPGroup)(w::GWord) if isempty(w) return one(G) diff --git a/src/FreeGroup.jl b/src/FreeGroup.jl index a9b1408..36cf7c0 100644 --- a/src/FreeGroup.jl +++ b/src/FreeGroup.jl @@ -52,12 +52,6 @@ FreeGroup(a::Vector) = FreeGroup(FreeSymbol.(a)) # ############################################################################### -function Base.one(G::FreeGroup) - id = FreeGroupElem(FreeSymbol[]) - id.parent = G - return id -end - function (G::FreeGroup)(w::GroupWord{FreeSymbol}) if length(syllables(w)) > 0 for s in w.symbols diff --git a/src/Groups.jl b/src/Groups.jl index e700437..3fe742d 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -15,13 +15,25 @@ export elements using LinearAlgebra using Markdown -Base.one(G::Generic.PermGroup) = G(collect(1:G.n), false) + +Base.one(G::Generic.PermGroup) = Generic.Perm(G.n) +Base.one(r::NCRingElem) = one(parent(r)) ############################################################################### # # ParentType / ObjectType definition # -############################################################################### + +abstract type AbstractFPGroup <: Group end + +function Base.one(G::Gr) where Gr <: AbstractFPGroup + El = elem_type(G) + id = El(eltype(El)[]) + id.parent = G + return id +end + +elem_type(G::Gr) where Gr <:AbstractFPGroup = elem_type(Gr) # fallback definition @doc doc""" ::GSymbol @@ -46,6 +58,8 @@ hash(s::S, h::UInt) where S<:GSymbol = hash(s.id, hash(s.pow, hash(S, h))) abstract type GWord{T<:GSymbol} <: GroupElem end +# fallback definitions +Base.eltype(w::GW) where GW<:GWord = eltype(GW) @doc doc""" W::GroupWord{T} <: GWord{T<:GSymbol} <:GroupElem > Basic representation of element of a finitely presented group. `W.symbols` @@ -77,8 +91,8 @@ syllables(w::GWord) = w.symbols ismodified(w::GWord) = w.modified setmodified!(w::GWord) = (w.modified = true; w) unsetmodified!(w::GWord) = (w.modified = false; w) +Base.one(w::GWord) = one(parent(w)) -abstract type AbstractFPGroup <: Group end ############################################################################### # From f7bf1598ee95149bff7f01e26a9c3fd04654cdb2 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 03:36:36 +0100 Subject: [PATCH 15/34] make multiplication abstract --- src/FPGroups.jl | 4 ---- src/Groups.jl | 35 +++++++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/FPGroups.jl b/src/FPGroups.jl index 0c472cb..e04726a 100644 --- a/src/FPGroups.jl +++ b/src/FPGroups.jl @@ -122,10 +122,6 @@ end # ############################################################################### -(*)(W::FPGroupElem, Z::FPGroupElem) = r_multiply(W, Z.symbols) -(*)(W::FPGroupElem, s::FPSymbol) = r_multiply(W, [s]) -(*)(s::FPSymbol, W::FPGroupElem) = l_multiply(W, [s]) - function reduce!(W::FPGroupElem) reduced = false while !reduced diff --git a/src/Groups.jl b/src/Groups.jl index 3fe742d..7ac68ab 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -270,14 +270,32 @@ for (mul, f) in ((:rmul!, :push!), (:lmul!, :pushfirst!)) end end - end +function rmul!(out::T, x::T, y::T) where T<: GWord + if out === x + out = deepcopy(out) + return freereduce!(append!(out, y)) + elseif out === y + out = deepcopy(out) + return freereduce!(prepend!(out, x)) + else + slenx = syllablelength(x) + sleny = syllablelength(y) + resize!(syllables(out), slenx+sleny) + syllables(out)[1:slenx] .= syllables(x) + syllables(out)[slenx+1:slenx+sleny] .= syllables(y) + return freereduce!(out) end end +lmul!(out::T, x::T, y::T) where T <: GWord = rmul!(out, y, x) -(*)(W::GWord, Z::GWord) = r_multiply(W, Z.symbols) -(*)(W::GWord, s::GSymbol) = r_multiply(W, [s]) -(*)(s::GSymbol, W::GWord) = l_multiply(W, [s]) +function AbstractAlgebra.mul!(out::T, x::T, y::T) where T <: GWord + return rmul!(out, x, y) +end + +(*)(W::GW, Z::GW) where GW <: GWord = rmul!(deepcopy(W), W, Z) +(*)(W::GWord, s::GSymbol) = rmul!(deepcopy(W), W, s) +(*)(s::GSymbol, W::GWord) = lmul!(deepcopy(W), W, s) function power_by_squaring(W::GWord, p::Integer) if p < 0 @@ -293,18 +311,19 @@ function power_by_squaring(W::GWord, p::Integer) t = trailing_zeros(p) + 1 p >>= t while (t -= 1) > 0 - r_multiply!(W, W.symbols) + append!(W, W) end Z = deepcopy(W) while p > 0 t = trailing_zeros(p) + 1 p >>= t while (t -= 1) >= 0 - r_multiply!(W, W.symbols) + append!(W, W) end - r_multiply!(Z, W.symbols) + append!(Z, W) end - return Z + + return freereduce!(Z) end (^)(x::GWord, n::Integer) = power_by_squaring(x,n) From 9bb2aba6f6e0f86b261140e1a3ab5f172b1b1908 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 03:38:33 +0100 Subject: [PATCH 16/34] add issubword, issubsymbol --- src/Groups.jl | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Groups.jl b/src/Groups.jl index 7ac68ab..c153849 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -348,11 +348,32 @@ end # # Replacement of symbols / sub-words # -############################################################################### issubsymbol(s::GSymbol, t::GSymbol) = s.id == t.id && (0 ≤ s.pow ≤ t.pow || 0 ≥ s.pow ≥ t.pow) +function issubsymbol(s::FreeSymbol, w::GWord, sindex::Integer) + @boundscheck 1 ≤ sindex ≤ syllablelength(w) || throw(BoundsError(w, sindex)) + return issubsymbol(s, syllables(w)[sindex]) +end + +function issubword(z::GWord, w::GWord, sindex::Integer) + isempty(z) && return true + @boundscheck 1 ≤ sindex ≤ syllablelength(w) || throw(BoundsError(w, sindex)) + n = syllablelength(z) + n == 1 && return issubsymbol(first(syllables(z)), syllables(w)[sindex]) + + lastindex = sindex + n - 1 + lastindex > syllablelength(w) && return false + + issubsymbol(first(z), syllables(w)[sindex]) || return false + issubsymbol(syllables(z)[end], syllables(w)[lastindex]) || return false + for (zidx, widx) in zip(2:n-1, sindex+1:lastindex-1) + syllables(z)[zidx] == syllables(w)[widx] || return false + end + return true +end + """doc Find the first linear index k>=i such that Z < W.symbols[k:k+length(Z)-1] """ From 5f0a33d335c54607e7d5b81023aa3d35fe54531e Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 03:54:12 +0100 Subject: [PATCH 17/34] overhaul findnext, findprev and replace --- src/Groups.jl | 214 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 139 insertions(+), 75 deletions(-) diff --git a/src/Groups.jl b/src/Groups.jl index c153849..ac93cc4 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -7,11 +7,9 @@ import AbstractAlgebra: order, gens, matrix_repr import Base: length, ==, hash, show, convert, eltype, iterate import Base: inv, reduce, *, ^, power_by_squaring -import Base: findfirst, findnext +import Base: findfirst, findnext, replace import Base: deepcopy_internal -export elements - using LinearAlgebra using Markdown @@ -375,90 +373,156 @@ function issubword(z::GWord, w::GWord, sindex::Integer) end """doc -Find the first linear index k>=i such that Z < W.symbols[k:k+length(Z)-1] +Find the first syllable index k>=i such that Z < syllables(W)[k:k+syllablelength(Z)-1] """ -function findnext(W::GWord, Z::GWord, i::Int) - n = length(Z.symbols) - if n == 0 - return 0 - elseif n == 1 - for idx in i:lastindex(W.symbols) - if issubsymbol(Z.symbols[1], W.symbols[idx]) - return idx - end - end - return 0 - else - for idx in i:lastindex(W.symbols) - n + 1 - foundfirst = issubsymbol(Z.symbols[1], W.symbols[idx]) - lastmatch = issubsymbol(Z.symbols[end], W.symbols[idx+n-1]) - if foundfirst && lastmatch - # middles match: - if view(Z.symbols, 2:n-1) == view(W.symbols, idx+1:idx+n-2) - return idx - end - end - end - end - return 0 +function findnext(subword::GWord, word::GWord, start::Integer) + @boundscheck 1 ≤ start ≤ syllablelength(word) || throw(BoundsError(word, start)) + isempty(subword) && return start + stop = syllablelength(word) - syllablelength(subword) +1 + + for idx in start:1:stop + issubword(subword, word, idx) && return idx + end + return nothing end -findfirst(W::GWord, Z::GWord) = findnext(W, Z, 1) +function findnext(s::FreeSymbol, word::GWord, start::Integer) + @boundscheck 1 ≤ start ≤ syllablelength(word) || throw(BoundsError(word, start)) + isone(s) && return start + stop = syllablelength(word) -function replace!(W::GWord, index, toreplace::GWord, replacement::GWord; check=true) - n = length(toreplace.symbols) - if n == 0 - return reduce!(W) - - elseif n == 1 - if check - @assert issubsymbol(toreplace.symbols[1], W.symbols[index]) - end - - first = change_pow(W.symbols[index], - W.symbols[index].pow - toreplace.symbols[1].pow) - last = change_pow(W.symbols[index], 0) - - else - if check - @assert issubsymbol(toreplace.symbols[1], W.symbols[index]) - @assert W.symbols[index+1:index+n-2] == toreplace.symbols[2:end-1] - @assert issubsymbol(toreplace.symbols[end], W.symbols[index+n-1]) - end - - first = change_pow(W.symbols[index], - W.symbols[index].pow - toreplace.symbols[1].pow) - last = change_pow(W.symbols[index+n-1], - W.symbols[index+n-1].pow - toreplace.symbols[end].pow) - end - - replacement = first * replacement * last - splice!(W.symbols, index:index+n-1, replacement.symbols) - return reduce!(W) + for idx in start:1:stop + issubsymbol(s, word, idx) && return idx + end + return nothing end -function replace(W::GWord, index, toreplace::GWord, replacement::GWord) - replace!(deepcopy(W), index, toreplace, replacement) +function findprev(subword::GWord, word::GWord, start::Integer) + @boundscheck 1 ≤ start ≤ syllablelength(word) || throw(BoundsError(word, start)) + isempty(subword) && return start + stop = 1 + + for idx in start:-1:1 + issubword(subword, word, idx) && return idx + end + return nothing end -function replace_all!(W::T,subst_dict::Dict{T,T}) where {T<:GWord} - modified = false +function findprev(s::FreeSymbol, word::GWord, start::Integer) + @boundscheck 1 ≤ start ≤ syllablelength(word) || throw(BoundsError(word, start)) + isone(s) && return start + stop = 1 + + for idx in start:-1:stop + issubsymbol(s, word, idx) && return idx + end + return nothing +end + +findfirst(subword::GWord, word::GWord) = findnext(subword, word, 1) +findlast(subword::GWord, word::GWord) = + findprev(subword, word, syllablelength(word)-syllablelength(subword)+1) + +function replace!(out::GW, W::GW, lhs_rhs::Pair{GS, T}; count::Integer=typemax(Int)) where + {GS<:GSymbol, T<:GWord, GW<:GWord} + (count == 0 || isempty(W)) && return W + count < 0 && throw(DomainError(count, "`count` must be non-negative.")) + + lhs, rhs = lhs_rhs + + sW = syllables(W) + sW_idx = 1 + r = something(findnext(lhs, W, sW_idx), 0) + + sout = syllables(out) + resize!(sout, 0) + sizehint!(sout, syllablelength(W)) + + c = 0 + + while !iszero(r) + append!(sout, view(sW, sW_idx:r-1)) + a, b = divrem(sW[r].pow, lhs.pow) + + if b != 0 + push!(sout, change_pow(sW[r], b)) + end + + append!(sout, repeat(syllables(rhs), a)) + + sW_idx = r+1 + sW_idx > syllablelength(W) && break + + r = something(findnext(lhs, W, sW_idx), 0) + c += 1 + c == count && break + end + append!(sout, sW[sW_idx:end]) + return freereduce!(out) +end + +function replace!(out::GW, W::GW, lhs_rhs::Pair{T, T}; count::Integer=typemax(Int)) where + {GW<:GWord, T <: GWord} + (count == 0 || isempty(W)) && return W + count < 0 && throw(DomainError(count, "`count` must be non-negative.")) + + lhs, rhs = lhs_rhs + lhs_slen = syllablelength(lhs) + lhs_slen == 1 && return replace!(out, W, first(syllables(lhs))=>rhs; count=count) + + sW = syllables(W) + sW_idx = 1 + r = something(findnext(lhs, W, sW_idx), 0) + + sout = syllables(out) + resize!(sout, 0) + sizehint!(sout, syllablelength(W)) + + c = 0 + + while !iszero(r) + append!(sout, view(sW, sW_idx:r-1)) + + exp = sW[r].pow - first(syllables(lhs)).pow + if exp != 0 + push!(sout, change_pow(sW[r], exp)) + end + + append!(sout, syllables(rhs)) + + exp = sW[r+lhs_slen-1].pow - last(syllables(lhs)).pow + if exp != 0 + push!(sout, change_pow(sW[r+lhs_slen-1], exp)) + end + + sW_idx = r+lhs_slen + sW_idx > syllablelength(W) && break + + r = something(findnext(lhs, W, sW_idx), 0) + c += 1 + c == count && break + end + + # copy the rest + append!(sout, sW[sW_idx:end]) + return freereduce!(out) +end + +function replace(W::GW, lhs_rhs::Pair{T, T}; count::Integer=typemax(Int)) where + {GW<:GWord, T <: GWord} + return replace!(one(W), W, lhs_rhs; count=count) +end + +function replace(W::GW, subst_dict::Dict{T,T}) where {GW<:GWord, T<:GWord} + out = W for toreplace in reverse!(sort!(collect(keys(subst_dict)), by=length)) replacement = subst_dict[toreplace] - i = findfirst(W, toreplace) - while i ≠ 0 - modified = true - replace!(W,i,toreplace, replacement) - i = findnext(W, toreplace, i) + if length(toreplace) > length(out) + continue end + out = replace(out, toreplace=>replacement) end - return modified -end - -function replace_all(W::T, subst_dict::Dict{T,T}) where {T<:GWord} - W = deepcopy(W) - replace_all!(W, subst_dict) - return W + return out end ############################################################################### From b92276ade280e5765c57861cb22e1d9796d12181 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 04:05:28 +0100 Subject: [PATCH 18/34] separate GSymbols --- src/Groups.jl | 28 ++-------------------------- src/symbols.jl | 18 ++++++++++++++++++ src/types.jl | 11 +++++++++++ 3 files changed, 31 insertions(+), 26 deletions(-) create mode 100644 src/symbols.jl create mode 100644 src/types.jl diff --git a/src/Groups.jl b/src/Groups.jl index ac93cc4..6a53c04 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -22,7 +22,8 @@ Base.one(r::NCRingElem) = one(parent(r)) # ParentType / ObjectType definition # -abstract type AbstractFPGroup <: Group end +include("types.jl") +include("gsymbols.jl") function Base.one(G::Gr) where Gr <: AbstractFPGroup El = elem_type(G) @@ -34,25 +35,7 @@ end elem_type(G::Gr) where Gr <:AbstractFPGroup = elem_type(Gr) # fallback definition @doc doc""" - ::GSymbol -> Abstract type which all group symbols of AbstractFPGroups should subtype. Each -> concrete subtype should implement fields: -> * `id` which is the `Symbol` representation/identification of a symbol -> * `pow` which is the (multiplicative) exponent of a symbol. -""" -abstract type GSymbol end - -Base.iterate(s::GS, i=1) where GS<:GSymbol = i <= abs(s.pow) ? (GS(s.id, sign(s.pow)), i+1) : nothing -Base.length(s::GSymbol) = abs(s.pow) -Base.size(s::GSymbol) = (length(s), ) -Base.eltype(s::GS) where GS<:GSymbol = GS -Base.isone(s::GSymbol) = iszero(s.pow) - -change_pow(s::S, n::Integer) where S<:GSymbol = S(s.id, n) -Base.inv(s::GSymbol) = change_pow(s, -s.pow) - -hash(s::S, h::UInt) where S<:GSymbol = hash(s.id, hash(s.pow, hash(S, h))) abstract type GWord{T<:GSymbol} <: GroupElem end @@ -233,13 +216,6 @@ function (==)(W::T, Z::T) where T <: GWord hash(W) != hash(Z) && return false return syllables(W) == syllables(Z) end - -function (==)(s::GSymbol, t::GSymbol) - isone(s) && isone(t) && return true - s.pow == t.pow && s.id == t.id && return true - return false -end - ############################################################################### # # Binary operators diff --git a/src/symbols.jl b/src/symbols.jl new file mode 100644 index 0000000..3e0c023 --- /dev/null +++ b/src/symbols.jl @@ -0,0 +1,18 @@ +change_pow(s::S, n::Integer) where S<:GSymbol = S(s.id, n) + +function Base.iterate(s::GS, i=1) where GS<:GSymbol + return i <= abs(s.pow) ? (GS(s.id, sign(s.pow)), i+1) : nothing +end +Base.length(s::GSymbol) = abs(s.pow) +Base.size(s::GSymbol) = (length(s), ) +Base.eltype(s::GS) where GS<:GSymbol = GS + +Base.isone(s::GSymbol) = iszero(s.pow) +Base.inv(s::GSymbol) = change_pow(s, -s.pow) +Base.hash(s::S, h::UInt) where S<:GSymbol = hash(s.id, hash(s.pow, hash(S, h))) + +function (==)(s::GSymbol, t::GSymbol) + isone(s) && isone(t) && return true + s.pow == t.pow && s.id == t.id && return true + return false +end diff --git a/src/types.jl b/src/types.jl new file mode 100644 index 0000000..9d93fc8 --- /dev/null +++ b/src/types.jl @@ -0,0 +1,11 @@ +abstract type AbstractFPGroup <: Group end + +@doc doc""" + ::GSymbol +> Represents a syllable. +> Abstract type which all group symbols of AbstractFPGroups should subtype. Each +> concrete subtype should implement fields: +> * `id` which is the `Symbol` representation/identification of a symbol +> * `pow` which is the (multiplicative) exponent of a symbol. +""" +abstract type GSymbol end From 263444c2a944c36d147bb32ca8e03acff2b79575 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 04:16:21 +0100 Subject: [PATCH 19/34] add basic fallbacks --- src/Groups.jl | 18 +----------------- src/fallbacks.jl | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 17 deletions(-) create mode 100644 src/fallbacks.jl diff --git a/src/Groups.jl b/src/Groups.jl index 6a53c04..76d3752 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -14,25 +14,9 @@ using LinearAlgebra using Markdown -Base.one(G::Generic.PermGroup) = Generic.Perm(G.n) -Base.one(r::NCRingElem) = one(parent(r)) - -############################################################################### -# -# ParentType / ObjectType definition -# - include("types.jl") include("gsymbols.jl") - -function Base.one(G::Gr) where Gr <: AbstractFPGroup - El = elem_type(G) - id = El(eltype(El)[]) - id.parent = G - return id -end - -elem_type(G::Gr) where Gr <:AbstractFPGroup = elem_type(Gr) # fallback definition +include("fallbacks.jl") @doc doc""" diff --git a/src/fallbacks.jl b/src/fallbacks.jl new file mode 100644 index 0000000..bc1d1db --- /dev/null +++ b/src/fallbacks.jl @@ -0,0 +1,17 @@ +# workarounds +Base.one(G::Generic.PermGroup) = Generic.Perm(G.n) +Base.one(r::NCRingElem) = one(parent(r)) + +# fallback definitions +# note: the user should implement those on type, when possible +Base.eltype(w::GW) where GW<:GWord = eltype(GW) +AbstractAlgebra.elem_type(G::Gr) where Gr <:AbstractFPGroup = elem_type(Gr) + +AbstractAlgebra.parent_type(g::Gw) where Gw <:GWord = parent_type(parent(Gr)) + +function Base.one(G::Gr) where Gr <: AbstractFPGroup + El = elem_type(G) + id = El(eltype(El)[]) + id.parent = G + return id +end From 43ad81d4ddfac61c9511de601964fa25053d29ab Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 04:19:02 +0100 Subject: [PATCH 20/34] separate GWords --- src/Groups.jl | 50 +------------------------------------------------- src/types.jl | 29 +++++++++++++++++++++++++++++ src/words.jl | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 49 deletions(-) create mode 100644 src/words.jl diff --git a/src/Groups.jl b/src/Groups.jl index 76d3752..45c7b54 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -17,53 +17,10 @@ using Markdown include("types.jl") include("gsymbols.jl") include("fallbacks.jl") +include("words.jl") @doc doc""" - - -abstract type GWord{T<:GSymbol} <: GroupElem end - -# fallback definitions -Base.eltype(w::GW) where GW<:GWord = eltype(GW) -@doc doc""" - W::GroupWord{T} <: GWord{T<:GSymbol} <:GroupElem -> Basic representation of element of a finitely presented group. `W.symbols` -> fieldname contains particular group symbols which multiplied constitute a -> group element, i.e. a word in generators. -> As reduction (inside group) of such word may be time consuming we provide -> `savedhash` and `modified` fields as well: -> hash (used e.g. in the `unique` function) is calculated by reducing the word, -> setting `modified` flag to `false` and computing the hash which is stored in -> `savedhash` field. -> whenever word `W` is changed `W.modified` is set to `false`; -> Future comparisons don't perform reduction (and use `savedhash`) as long as -> `modified` flag remains `false`. - """ -mutable struct GroupWord{T} <: GWord{T} - symbols::Vector{T} - modified::Bool - savedhash::UInt - parent::Group - - function GroupWord{T}(symbols::Vector{T}) where {T} - return new{T}(symbols, true, zero(UInt)) - end -end - -syllablelength(w::GWord) = length(w.symbols) -syllables(w::GWord) = w.symbols -ismodified(w::GWord) = w.modified -setmodified!(w::GWord) = (w.modified = true; w) -unsetmodified!(w::GWord) = (w.modified = false; w) -Base.one(w::GWord) = one(parent(w)) - - -############################################################################### -# -# Includes -# -############################################################################### include("FreeGroup.jl") include("FPGroups.jl") @@ -86,11 +43,6 @@ parent(w::GWord{T}) where {T<:GSymbol} = w.parent # ############################################################################### -GroupWord(s::T) where {T<:GSymbol} = GroupWord{T}(T[s]) -GroupWord{T}(s::T) where {T<:GSymbol} = GroupWord{T}(T[s]) -GroupWord(w::GroupWord{T}) where {T<:GSymbol} = w -convert(::Type{GroupWord{T}}, s::T) where {T<:GSymbol} = GroupWord{T}(T[s]) - ############################################################################### # # Basic manipulation diff --git a/src/types.jl b/src/types.jl index 9d93fc8..320b04f 100644 --- a/src/types.jl +++ b/src/types.jl @@ -9,3 +9,32 @@ abstract type AbstractFPGroup <: Group end > * `pow` which is the (multiplicative) exponent of a symbol. """ abstract type GSymbol end + +abstract type GWord{T<:GSymbol} <: GroupElem end + +@doc doc""" + W::GroupWord{T} <: GWord{T<:GSymbol} <:GroupElem +> Basic representation of element of a finitely presented group. `W.symbols` +> fieldname contains particular group symbols which multiplied constitute a +> group element, i.e. a word in generators. +> As reduction (inside group) of such word may be time consuming we provide +> `savedhash` and `modified` fields as well: +> hash (used e.g. in the `unique` function) is calculated by reducing the word, +> setting `modified` flag to `false` and computing the hash which is stored in +> `savedhash` field. +> whenever word `W` is changed `W.modified` is set to `false`; +> Future comparisons don't perform reduction (and use `savedhash`) as long as +> `modified` flag remains `false`. + +""" + +mutable struct GroupWord{T} <: GWord{T} + symbols::Vector{T} + modified::Bool + savedhash::UInt + parent::Group + + function GroupWord{T}(symbols::Vector{<:GSymbol}) where T + return new{T}(symbols, true, zero(UInt)) + end +end diff --git a/src/words.jl b/src/words.jl new file mode 100644 index 0000000..ab08337 --- /dev/null +++ b/src/words.jl @@ -0,0 +1,39 @@ +syllablelength(w::GWord) = length(w.symbols) +syllables(w::GWord) = w.symbols +ismodified(w::GWord) = w.modified +setmodified!(w::GWord) = (w.modified = true; w) +unsetmodified!(w::GWord) = (w.modified = false; w) +savehash!(w::GWord, h::UInt) = (w.savedhash = h; w) +savedhash(w::GWord) = w.savedhash +parent(w::GWord) = w.parent +hasparent(w::GWord) = isdefined(w, :parent) +setparent!(w::GWord, G::AbstractFPGroup) = (w.parent = G; w) + +Base.isempty(w::GWord) = isempty(syllables(w)) +Base.isone(w::GWord) = (freereduce!(w); isempty(w)) +Base.one(w::GWord) = one(parent(w)) + +function Base.iterate(w::GWord, state=(syllable=1, pow=1)) + state.syllable > syllablelength(w) && return nothing + next = iterate(syllables(w)[state.syllable], state.pow) + next === nothing && return iterate(w, (syllable=state.syllable+1, pow=1)) + return first(next), (syllable=state.syllable, pow=last(next)) +end + +Base.eltype(::Type{<:GWord{T}}) where T = T +Base.length(w::GWord) = isempty(w) ? 0 : sum(length, syllables(w)) +Base.size(w::GWord) = (length(w),) +Base.lastindex(w::GWord) = length(w) + +Base.@propagate_inbounds function Base.getindex(w::GWord, i::Integer) + csum = 0 + idx = 0 + @boundscheck 0 < i <= length(w) || throw(BoundsError(w, i)) + while csum < i + idx += 1 + csum += length(syllables(w)[idx]) + end + return first(syllables(w)[idx]) +end + +# no setindex! for syllable based words From e84152a9cfbfde311f5c2e4ba45c390be56d4a52 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 04:40:14 +0100 Subject: [PATCH 21/34] separate hashing and freereduce --- src/Groups.jl | 93 +---------------------------------------------- src/freereduce.jl | 43 ++++++++++++++++++++++ src/hashing.jl | 34 +++++++++++++++++ 3 files changed, 79 insertions(+), 91 deletions(-) create mode 100644 src/freereduce.jl create mode 100644 src/hashing.jl diff --git a/src/Groups.jl b/src/Groups.jl index 45c7b54..949efd2 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -18,9 +18,8 @@ include("types.jl") include("gsymbols.jl") include("fallbacks.jl") include("words.jl") - -@doc doc""" -""" +include("hashing.jl") +include("freereduce.jl") include("FreeGroup.jl") include("FPGroups.jl") @@ -29,83 +28,6 @@ include("AutGroup.jl") include("DirectPower.jl") include("WreathProducts.jl") -############################################################################### -# -# Type and parent object methods -# -############################################################################### - -parent(w::GWord{T}) where {T<:GSymbol} = w.parent - -############################################################################### -# -# ParentType / ObjectType constructors -# -############################################################################### - -############################################################################### -# -# Basic manipulation -# -############################################################################### - -function hash_internal(W::GWord) - reduce!(W) - return hash(syllables(W), hash(typeof(W), hash(parent(W)))) -end - -function hash(W::GWord, h::UInt) - if ismodified(W) - W.savedhash = hash_internal(W) - unsetmodified!(W) - end - return xor(W.savedhash, h) -end - -# WARNING: Due to specialised (constant) hash function of GWords this one is actually necessary! -function deepcopy_internal(W::T, dict::IdDict) where {T<:GWord} - G = parent(W) - return G(T(deepcopy(syllables(W)))) -end - -function freereduce!(::Type{Bool}, w::GWord) - reduced = true - for i in 1:syllablelength(w)-1 - s, ns = syllables(w)[i], syllables(w)[i+1] - if isone(s) - continue - elseif s.id == ns.id - reduced = false - setmodified!(w) - p1 = s.pow - p2 = ns.pow - - syllables(w)[i+1] = change_pow(s, p1 + p2) - syllables(w)[i] = change_pow(s, 0) - end - end - filter!(!isone, syllables(w)) - return reduced -end - -function freereduce!(w::GWord) - reduced = false - while !reduced - reduced = freereduce!(Bool, w) - end - return w -end - -reduce!(w::GWord) = freereduce!(w) - -@doc doc""" - reduce(w::GWord) -> performs reduction/simplification of a group element (word in generators). -> The default reduction is the free group reduction -> More specific procedures should be dispatched on `GWord`s type parameter. - -""" -reduce(w::GWord) = reduce!(deepcopy(w)) @doc doc""" gens(G::AbstractFPGroups) @@ -141,17 +63,6 @@ function show(io::IO, s::T) where {T<:GSymbol} end end -############################################################################### -# -# Comparison -# -############################################################################### - -function (==)(W::T, Z::T) where T <: GWord - parent(W) != parent(Z) && return false - hash(W) != hash(Z) && return false - return syllables(W) == syllables(Z) -end ############################################################################### # # Binary operators diff --git a/src/freereduce.jl b/src/freereduce.jl new file mode 100644 index 0000000..ef56c2c --- /dev/null +++ b/src/freereduce.jl @@ -0,0 +1,43 @@ +############################################################################### +# +# Naive reduction +# + +function freereduce!(::Type{Bool}, w::GWord) + reduced = true + for i in 1:syllablelength(w)-1 + s, ns = syllables(w)[i], syllables(w)[i+1] + if isone(s) + continue + elseif s.id == ns.id + reduced = false + setmodified!(w) + p1 = s.pow + p2 = ns.pow + + syllables(w)[i+1] = change_pow(s, p1 + p2) + syllables(w)[i] = change_pow(s, 0) + end + end + filter!(!isone, syllables(w)) + return reduced +end + +function freereduce!(w::GWord) + reduced = false + while !reduced + reduced = freereduce!(Bool, w) + end + return w +end + +reduce!(w::GWord) = freereduce!(w) + +@doc doc""" + reduce(w::GWord) +> performs reduction/simplification of a group element (word in generators). +> The default reduction is the free group reduction +> More specific procedures should be dispatched on `GWord`s type parameter. + +""" +reduce(w::GWord) = reduce!(deepcopy(w)) diff --git a/src/hashing.jl b/src/hashing.jl new file mode 100644 index 0000000..ff799eb --- /dev/null +++ b/src/hashing.jl @@ -0,0 +1,34 @@ +############################################################################### +# +# hashing, deepcopy and == +# + +function hash_internal(W::GWord) + reduce!(W) + h = hasparent(W) ? hash(parent(W)) : zero(UInt) + return hash(syllables(W), hash(typeof(W), h)) +end + +function hash(W::GWord, h::UInt) + if ismodified(W) + savehash!(W, hash_internal(W)) + unsetmodified!(W) + end + return xor(savedhash(W), h) +end + +# WARNING: Due to specialised (constant) hash function of GWords this one is actually necessary! +function Base.deepcopy_internal(W::T, dict::IdDict) where T<:GWord + G = parent(W) + g = T(deepcopy(syllables(W))) + setparent!(g, G) + return g +end + +function (==)(W::T, Z::T) where T <: GWord + hash(W) != hash(Z) && return false # distinguishes parent and parentless words + if hasparent(W) && hasparent(Z) + parent(W) != parent(Z) && return false + end + return syllables(W) == syllables(Z) +end From afa0988ebc3af9fcbb6d5a0e97b4e5a2be785690 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 04:41:23 +0100 Subject: [PATCH 22/34] separate arithmetic --- src/Groups.jl | 97 +---------------------------------------------- src/arithmetic.jl | 93 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 96 deletions(-) create mode 100644 src/arithmetic.jl diff --git a/src/Groups.jl b/src/Groups.jl index 949efd2..0934ddb 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -20,6 +20,7 @@ include("fallbacks.jl") include("words.jl") include("hashing.jl") include("freereduce.jl") +include("arithmetic.jl") include("FreeGroup.jl") include("FPGroups.jl") @@ -62,102 +63,6 @@ function show(io::IO, s::T) where {T<:GSymbol} print(io, string((s.id))*"^$(s.pow)") end end - -############################################################################### -# -# Binary operators -# -############################################################################### - -function Base.append!(w::GWord{T}, v::AbstractVector{T}) where T - append!(syllables(w), v) - return w -end - -function Base.prepend!(w::GWord{T}, v::AbstractVector{T}) where T - prepend!(syllables(w), v) - return w -end - -Base.append!(w::T, v::T) where T <: GWord = append!(w, syllables(v)) -Base.prepend!(w::T, v::T) where T <: GWord = prepend!(w, syllables(v)) - -for (mul, f) in ((:rmul!, :push!), (:lmul!, :pushfirst!)) - @eval begin - function $mul(out::T, w::T, s::GSymbol) where T <:GWord - $f(syllables(out), s) - return freereduce!(out) - end - end -end - -function rmul!(out::T, x::T, y::T) where T<: GWord - if out === x - out = deepcopy(out) - return freereduce!(append!(out, y)) - elseif out === y - out = deepcopy(out) - return freereduce!(prepend!(out, x)) - else - slenx = syllablelength(x) - sleny = syllablelength(y) - resize!(syllables(out), slenx+sleny) - syllables(out)[1:slenx] .= syllables(x) - syllables(out)[slenx+1:slenx+sleny] .= syllables(y) - return freereduce!(out) - end -end - -lmul!(out::T, x::T, y::T) where T <: GWord = rmul!(out, y, x) - -function AbstractAlgebra.mul!(out::T, x::T, y::T) where T <: GWord - return rmul!(out, x, y) -end - -(*)(W::GW, Z::GW) where GW <: GWord = rmul!(deepcopy(W), W, Z) -(*)(W::GWord, s::GSymbol) = rmul!(deepcopy(W), W, s) -(*)(s::GSymbol, W::GWord) = lmul!(deepcopy(W), W, s) - -function power_by_squaring(W::GWord, p::Integer) - if p < 0 - return power_by_squaring(inv(W), -p) - elseif p == 0 - return one(parent(W)) - elseif p == 1 - return W - elseif p == 2 - return W*W - end - W = deepcopy(W) - t = trailing_zeros(p) + 1 - p >>= t - while (t -= 1) > 0 - append!(W, W) - end - Z = deepcopy(W) - while p > 0 - t = trailing_zeros(p) + 1 - p >>= t - while (t -= 1) >= 0 - append!(W, W) - end - append!(Z, W) - end - - return freereduce!(Z) -end - -(^)(x::GWord, n::Integer) = power_by_squaring(x,n) - -############################################################################### -# -# Inversion -# -############################################################################### - -function inv(W::T) where T<:GWord - if length(W) == 0 - return W else G = parent(W) w = T([inv(s) for s in Iterators.reverse(syllables(W))]) diff --git a/src/arithmetic.jl b/src/arithmetic.jl new file mode 100644 index 0000000..933c5ec --- /dev/null +++ b/src/arithmetic.jl @@ -0,0 +1,93 @@ +function Base.inv(W::T) where T<:GWord + length(W) == 0 && return W + G = parent(W) + w = T([inv(s) for s in Iterators.reverse(syllables(W))]) + return setparent!(w, G) +end + +############################################################################### +# +# Binary operators +# + +function Base.append!(w::GWord{T}, v::AbstractVector{T}) where T + append!(syllables(w), v) + return w +end + +function Base.prepend!(w::GWord{T}, v::AbstractVector{T}) where T + prepend!(syllables(w), v) + return w +end + +Base.append!(w::T, v::T) where T <: GWord = append!(w, syllables(v)) +Base.prepend!(w::T, v::T) where T <: GWord = prepend!(w, syllables(v)) + +for (mul, f) in ((:rmul!, :push!), (:lmul!, :pushfirst!)) + @eval begin + function $mul(out::T, w::T, s::GSymbol) where T <:GWord + resize!(syllables(out), syllablelength(w)) + syllables(out) .= syllables(w) + $f(syllables(out), s) + return freereduce!(out) + end + end +end + +function rmul!(out::T, x::T, y::T) where T<: GWord + if out === x + out = deepcopy(out) + return freereduce!(append!(out, y)) + elseif out === y + out = deepcopy(out) + return freereduce!(prepend!(out, x)) + else + slenx = syllablelength(x) + sleny = syllablelength(y) + resize!(syllables(out), slenx+sleny) + syllables(out)[1:slenx] .= syllables(x) + syllables(out)[slenx+1:slenx+sleny] .= syllables(y) + return freereduce!(out) + end +end + +lmul!(out::T, x::T, y::T) where T <: GWord = rmul!(out, y, x) + +function AbstractAlgebra.mul!(out::T, x::T, y::T) where T <: GWord + return rmul!(out, x, y) +end + +(*)(W::GW, Z::GW) where GW <: GWord = rmul!(deepcopy(W), W, Z) +(*)(W::GWord, s::GSymbol) = rmul!(deepcopy(W), W, s) +(*)(s::GSymbol, W::GWord) = lmul!(deepcopy(W), W, s) + +function power_by_squaring(W::GWord, p::Integer) + if p < 0 + return power_by_squaring(inv(W), -p) + elseif p == 0 + return one(W) + elseif p == 1 + return W + elseif p == 2 + return W*W + end + W = deepcopy(W) + t = trailing_zeros(p) + 1 + p >>= t + while (t -= 1) > 0 + append!(W, W) + end + Z = deepcopy(W) + while p > 0 + t = trailing_zeros(p) + 1 + p >>= t + while (t -= 1) >= 0 + append!(W, W) + end + append!(Z, W) + end + + return freereduce!(Z) +end + +(^)(x::GWord, n::Integer) = power_by_squaring(x,n) From 2196b7d2565daa9744cd876cc2de3850eba7ae68 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 04:42:47 +0100 Subject: [PATCH 23/34] find and replace, finally --- src/Groups.jl | 184 +-------------------------------------------- src/findreplace.jl | 182 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 183 deletions(-) create mode 100644 src/findreplace.jl diff --git a/src/Groups.jl b/src/Groups.jl index 0934ddb..baf308a 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -21,6 +21,7 @@ include("words.jl") include("hashing.jl") include("freereduce.jl") include("arithmetic.jl") +include("findreplace.jl") include("FreeGroup.jl") include("FPGroups.jl") @@ -70,189 +71,6 @@ end end end -############################################################################### -# -# Replacement of symbols / sub-words -# - -issubsymbol(s::GSymbol, t::GSymbol) = - s.id == t.id && (0 ≤ s.pow ≤ t.pow || 0 ≥ s.pow ≥ t.pow) - -function issubsymbol(s::FreeSymbol, w::GWord, sindex::Integer) - @boundscheck 1 ≤ sindex ≤ syllablelength(w) || throw(BoundsError(w, sindex)) - return issubsymbol(s, syllables(w)[sindex]) -end - -function issubword(z::GWord, w::GWord, sindex::Integer) - isempty(z) && return true - @boundscheck 1 ≤ sindex ≤ syllablelength(w) || throw(BoundsError(w, sindex)) - n = syllablelength(z) - n == 1 && return issubsymbol(first(syllables(z)), syllables(w)[sindex]) - - lastindex = sindex + n - 1 - lastindex > syllablelength(w) && return false - - issubsymbol(first(z), syllables(w)[sindex]) || return false - issubsymbol(syllables(z)[end], syllables(w)[lastindex]) || return false - for (zidx, widx) in zip(2:n-1, sindex+1:lastindex-1) - syllables(z)[zidx] == syllables(w)[widx] || return false - end - return true -end - -"""doc -Find the first syllable index k>=i such that Z < syllables(W)[k:k+syllablelength(Z)-1] -""" -function findnext(subword::GWord, word::GWord, start::Integer) - @boundscheck 1 ≤ start ≤ syllablelength(word) || throw(BoundsError(word, start)) - isempty(subword) && return start - stop = syllablelength(word) - syllablelength(subword) +1 - - for idx in start:1:stop - issubword(subword, word, idx) && return idx - end - return nothing -end - -function findnext(s::FreeSymbol, word::GWord, start::Integer) - @boundscheck 1 ≤ start ≤ syllablelength(word) || throw(BoundsError(word, start)) - isone(s) && return start - stop = syllablelength(word) - - for idx in start:1:stop - issubsymbol(s, word, idx) && return idx - end - return nothing -end - -function findprev(subword::GWord, word::GWord, start::Integer) - @boundscheck 1 ≤ start ≤ syllablelength(word) || throw(BoundsError(word, start)) - isempty(subword) && return start - stop = 1 - - for idx in start:-1:1 - issubword(subword, word, idx) && return idx - end - return nothing -end - -function findprev(s::FreeSymbol, word::GWord, start::Integer) - @boundscheck 1 ≤ start ≤ syllablelength(word) || throw(BoundsError(word, start)) - isone(s) && return start - stop = 1 - - for idx in start:-1:stop - issubsymbol(s, word, idx) && return idx - end - return nothing -end - -findfirst(subword::GWord, word::GWord) = findnext(subword, word, 1) -findlast(subword::GWord, word::GWord) = - findprev(subword, word, syllablelength(word)-syllablelength(subword)+1) - -function replace!(out::GW, W::GW, lhs_rhs::Pair{GS, T}; count::Integer=typemax(Int)) where - {GS<:GSymbol, T<:GWord, GW<:GWord} - (count == 0 || isempty(W)) && return W - count < 0 && throw(DomainError(count, "`count` must be non-negative.")) - - lhs, rhs = lhs_rhs - - sW = syllables(W) - sW_idx = 1 - r = something(findnext(lhs, W, sW_idx), 0) - - sout = syllables(out) - resize!(sout, 0) - sizehint!(sout, syllablelength(W)) - - c = 0 - - while !iszero(r) - append!(sout, view(sW, sW_idx:r-1)) - a, b = divrem(sW[r].pow, lhs.pow) - - if b != 0 - push!(sout, change_pow(sW[r], b)) - end - - append!(sout, repeat(syllables(rhs), a)) - - sW_idx = r+1 - sW_idx > syllablelength(W) && break - - r = something(findnext(lhs, W, sW_idx), 0) - c += 1 - c == count && break - end - append!(sout, sW[sW_idx:end]) - return freereduce!(out) -end - -function replace!(out::GW, W::GW, lhs_rhs::Pair{T, T}; count::Integer=typemax(Int)) where - {GW<:GWord, T <: GWord} - (count == 0 || isempty(W)) && return W - count < 0 && throw(DomainError(count, "`count` must be non-negative.")) - - lhs, rhs = lhs_rhs - lhs_slen = syllablelength(lhs) - lhs_slen == 1 && return replace!(out, W, first(syllables(lhs))=>rhs; count=count) - - sW = syllables(W) - sW_idx = 1 - r = something(findnext(lhs, W, sW_idx), 0) - - sout = syllables(out) - resize!(sout, 0) - sizehint!(sout, syllablelength(W)) - - c = 0 - - while !iszero(r) - append!(sout, view(sW, sW_idx:r-1)) - - exp = sW[r].pow - first(syllables(lhs)).pow - if exp != 0 - push!(sout, change_pow(sW[r], exp)) - end - - append!(sout, syllables(rhs)) - - exp = sW[r+lhs_slen-1].pow - last(syllables(lhs)).pow - if exp != 0 - push!(sout, change_pow(sW[r+lhs_slen-1], exp)) - end - - sW_idx = r+lhs_slen - sW_idx > syllablelength(W) && break - - r = something(findnext(lhs, W, sW_idx), 0) - c += 1 - c == count && break - end - - # copy the rest - append!(sout, sW[sW_idx:end]) - return freereduce!(out) -end - -function replace(W::GW, lhs_rhs::Pair{T, T}; count::Integer=typemax(Int)) where - {GW<:GWord, T <: GWord} - return replace!(one(W), W, lhs_rhs; count=count) -end - -function replace(W::GW, subst_dict::Dict{T,T}) where {GW<:GWord, T<:GWord} - out = W - for toreplace in reverse!(sort!(collect(keys(subst_dict)), by=length)) - replacement = subst_dict[toreplace] - if length(toreplace) > length(out) - continue - end - out = replace(out, toreplace=>replacement) - end - return out -end - ############################################################################### # # Misc diff --git a/src/findreplace.jl b/src/findreplace.jl new file mode 100644 index 0000000..50fc358 --- /dev/null +++ b/src/findreplace.jl @@ -0,0 +1,182 @@ +############################################################################### +# +# Replacement of symbols / sub-words +# + +issubsymbol(s::GSymbol, t::GSymbol) = + s.id == t.id && (0 ≤ s.pow ≤ t.pow || 0 ≥ s.pow ≥ t.pow) + +function issubsymbol(s::FreeSymbol, w::GWord, sindex::Integer) + @boundscheck 1 ≤ sindex ≤ syllablelength(w) || throw(BoundsError(w, sindex)) + return issubsymbol(s, syllables(w)[sindex]) +end + +function issubword(z::GWord, w::GWord, sindex::Integer) + isempty(z) && return true + @boundscheck 1 ≤ sindex ≤ syllablelength(w) || throw(BoundsError(w, sindex)) + n = syllablelength(z) + n == 1 && return issubsymbol(first(syllables(z)), syllables(w)[sindex]) + + lastindex = sindex + n - 1 + lastindex > syllablelength(w) && return false + + issubsymbol(first(z), syllables(w)[sindex]) || return false + issubsymbol(syllables(z)[end], syllables(w)[lastindex]) || return false + for (zidx, widx) in zip(2:n-1, sindex+1:lastindex-1) + syllables(z)[zidx] == syllables(w)[widx] || return false + end + return true +end + +"""doc +Find the first syllable index k>=i such that Z < syllables(W)[k:k+syllablelength(Z)-1] +""" +function findnext(subword::GWord, word::GWord, start::Integer) + @boundscheck 1 ≤ start ≤ syllablelength(word) || throw(BoundsError(word, start)) + isempty(subword) && return start + stop = syllablelength(word) - syllablelength(subword) +1 + + for idx in start:1:stop + issubword(subword, word, idx) && return idx + end + return nothing +end + +function findnext(s::FreeSymbol, word::GWord, start::Integer) + @boundscheck 1 ≤ start ≤ syllablelength(word) || throw(BoundsError(word, start)) + isone(s) && return start + stop = syllablelength(word) + + for idx in start:1:stop + issubsymbol(s, word, idx) && return idx + end + return nothing +end + +function findprev(subword::GWord, word::GWord, start::Integer) + @boundscheck 1 ≤ start ≤ syllablelength(word) || throw(BoundsError(word, start)) + isempty(subword) && return start + stop = 1 + + for idx in start:-1:1 + issubword(subword, word, idx) && return idx + end + return nothing +end + +function findprev(s::FreeSymbol, word::GWord, start::Integer) + @boundscheck 1 ≤ start ≤ syllablelength(word) || throw(BoundsError(word, start)) + isone(s) && return start + stop = 1 + + for idx in start:-1:stop + issubsymbol(s, word, idx) && return idx + end + return nothing +end + +findfirst(subword::GWord, word::GWord) = findnext(subword, word, 1) +findlast(subword::GWord, word::GWord) = + findprev(subword, word, syllablelength(word)-syllablelength(subword)+1) + +function replace!(out::GW, W::GW, lhs_rhs::Pair{GS, T}; count::Integer=typemax(Int)) where + {GS<:GSymbol, T<:GWord, GW<:GWord} + (count == 0 || isempty(W)) && return W + count < 0 && throw(DomainError(count, "`count` must be non-negative.")) + + lhs, rhs = lhs_rhs + + sW = syllables(W) + sW_idx = 1 + r = something(findnext(lhs, W, sW_idx), 0) + + sout = syllables(out) + resize!(sout, 0) + sizehint!(sout, syllablelength(W)) + + c = 0 + + while !iszero(r) + append!(sout, view(sW, sW_idx:r-1)) + a, b = divrem(sW[r].pow, lhs.pow) + + if b != 0 + push!(sout, change_pow(sW[r], b)) + end + + append!(sout, repeat(syllables(rhs), a)) + + sW_idx = r+1 + sW_idx > syllablelength(W) && break + + r = something(findnext(lhs, W, sW_idx), 0) + c += 1 + c == count && break + end + append!(sout, sW[sW_idx:end]) + return freereduce!(out) +end + +function replace!(out::GW, W::GW, lhs_rhs::Pair{T, T}; count::Integer=typemax(Int)) where + {GW<:GWord, T <: GWord} + (count == 0 || isempty(W)) && return W + count < 0 && throw(DomainError(count, "`count` must be non-negative.")) + + lhs, rhs = lhs_rhs + lhs_slen = syllablelength(lhs) + lhs_slen == 1 && return replace!(out, W, first(syllables(lhs))=>rhs; count=count) + + sW = syllables(W) + sW_idx = 1 + r = something(findnext(lhs, W, sW_idx), 0) + + sout = syllables(out) + resize!(sout, 0) + sizehint!(sout, syllablelength(W)) + + c = 0 + + while !iszero(r) + append!(sout, view(sW, sW_idx:r-1)) + + exp = sW[r].pow - first(syllables(lhs)).pow + if exp != 0 + push!(sout, change_pow(sW[r], exp)) + end + + append!(sout, syllables(rhs)) + + exp = sW[r+lhs_slen-1].pow - last(syllables(lhs)).pow + if exp != 0 + push!(sout, change_pow(sW[r+lhs_slen-1], exp)) + end + + sW_idx = r+lhs_slen + sW_idx > syllablelength(W) && break + + r = something(findnext(lhs, W, sW_idx), 0) + c += 1 + c == count && break + end + + # copy the rest + append!(sout, sW[sW_idx:end]) + return freereduce!(out) +end + +function replace(W::GW, lhs_rhs::Pair{T, T}; count::Integer=typemax(Int)) where + {GW<:GWord, T <: GWord} + return replace!(one(W), W, lhs_rhs; count=count) +end + +function replace(W::GW, subst_dict::Dict{T,T}) where {GW<:GWord, T<:GWord} + out = W + for toreplace in reverse!(sort!(collect(keys(subst_dict)), by=length)) + replacement = subst_dict[toreplace] + if length(toreplace) > length(out) + continue + end + out = replace(out, toreplace=>replacement) + end + return out +end From f8aedc207f163ec97e957ce2ca283f9221e7b4d5 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 05:00:16 +0100 Subject: [PATCH 24/34] make FPGroups more connected to their freepreimages --- src/FPGroups.jl | 66 ++++++++++++++++++++++++++----------------------- src/symbols.jl | 2 ++ 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/FPGroups.jl b/src/FPGroups.jl index e04726a..556ca50 100644 --- a/src/FPGroups.jl +++ b/src/FPGroups.jl @@ -12,14 +12,14 @@ end FPGroupElem = GroupWord{FPSymbol} mutable struct FPGroup <: AbstractFPGroup - gens::Vector{FPSymbol} - rels::Dict{FPGroupElem, FPGroupElem} + gens::Vector{FPSymbol} + rels::Dict{FreeGroupElem, FreeGroupElem} - function FPGroup(gens::Vector{T}, rels::Dict{FPGroupElem, FPGroupElem}) where {T<:GSymbol} - G = new(gens) - G.rels = Dict(G(k) => G(v) for (k,v) in rels) - return G - end + function FPGroup(gens::Vector{T}, rels::Dict{FreeGroupElem, FreeGroupElem}) where {T<:GSymbol} + G = new(gens) + G.rels = Dict(G(k) => G(v) for (k,v) in rels) + return G + end end export FPGroupElem, FPGroup @@ -45,7 +45,7 @@ FPSymbol(s::GSymbol) = FPSymbol(s.id, s.pow) convert(::Type{FPSymbol}, s::FreeSymbol) = FPSymbol(s.id, s.pow) -FPGroup(gens::Vector{FPSymbol}) = FPGroup(gens, Dict{FPGroupElem, FPGroupElem}()) +FPGroup(gens::Vector{FPSymbol}) = FPGroup(gens, Dict{FreeGroupElem, FreeGroupElem}()) FPGroup(a::Vector{String}) = FPGroup([FPSymbol(i) for i in a]) @@ -80,13 +80,13 @@ function (G::FPGroup)(w::GWord) return reduce!(w) end -(G::FPGroup)(s::FPSymbol) = G(FPGroupElem(s)) ############################################################################### # # Basic manipulation # ############################################################################### +(G::FPGroup)(s::GSymbol) = G(FPGroupElem(s)) ############################################################################### # @@ -137,31 +137,35 @@ end # ############################################################################### -function add_rels!(G::FPGroup, newrels::Dict{FPGroupElem,FPGroupElem}) - for w in keys(newrels) - if !(w in keys(G.rels)) - G.rels[w] = G(newrels[w]) - end - end +freepreimage(G::FPGroup) = parent(first(keys(G.rels))) +freepreimage(g::FPGroupElem) = freepreimage(parent(g))(syllables(g)) + +function add_rels!(G::FPGroup, newrels::Dict{FreeGroupElem,FreeGroupElem}) + for w in keys(newrels) + haskey(G.rels, w) && continue + G.rels[w] = newrels[w] + end + return G end function Base.:/(G::FPGroup, newrels::Vector{FPGroupElem}) - for r in newrels - parent(r) == G || throw(DomainError( - "Can not form quotient group: $r is not an element of $G")) - end - H = deepcopy(G) - newrels = Dict(H(r) => one(H) for r in newrels) - add_rels!(H, newrels) - return H + for r in newrels + parent(r) == G || throw(DomainError( + "Can not form quotient group: $r is not an element of $G")) + end + H = deepcopy(G) + F = freepreimage(H) + newrels = Dict(freepreimage(r) => one(F) for r in newrels) + add_rels!(H, newrels) + return H end -function Base.:/(G::FreeGroup, rels::Vector{FreeGroupElem}) - for r in rels - parent(r) == G || throw(DomainError( - "Can not form quotient group: $r is not an element of $G")) - end - H = FPGroup(deepcopy(G)) - H.rels = Dict(H(rel) => one(H) for rel in unique(rels)) - return H +function Base.:/(F::FreeGroup, rels::Vector{FreeGroupElem}) + for r in rels + parent(r) == F || throw(DomainError( + "Can not form quotient group: $r is not an element of $F")) + end + G = FPGroup(FPSymbol.(F.gens)) + G.rels = Dict(rel => one(F) for rel in unique(rels)) + return G end diff --git a/src/symbols.jl b/src/symbols.jl index 3e0c023..7989fdc 100644 --- a/src/symbols.jl +++ b/src/symbols.jl @@ -16,3 +16,5 @@ function (==)(s::GSymbol, t::GSymbol) s.pow == t.pow && s.id == t.id && return true return false end + +Base.convert(::Type{GS}, s::GSymbol) where GS<:GSymbol = GS(s.id, s.pow) From 12be3b75bc50309ddc219158171120803ca32470 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 05:21:57 +0100 Subject: [PATCH 25/34] add missing constructors and converts --- src/symbols.jl | 1 + src/types.jl | 5 ++++- src/words.jl | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/symbols.jl b/src/symbols.jl index 7989fdc..dd766ff 100644 --- a/src/symbols.jl +++ b/src/symbols.jl @@ -18,3 +18,4 @@ function (==)(s::GSymbol, t::GSymbol) end Base.convert(::Type{GS}, s::GSymbol) where GS<:GSymbol = GS(s.id, s.pow) +Base.convert(::Type{GS}, s::GS) where GS<:GSymbol = s diff --git a/src/types.jl b/src/types.jl index 320b04f..1de0a34 100644 --- a/src/types.jl +++ b/src/types.jl @@ -34,7 +34,10 @@ mutable struct GroupWord{T} <: GWord{T} savedhash::UInt parent::Group - function GroupWord{T}(symbols::Vector{<:GSymbol}) where T + function GroupWord{T}(symbols::AbstractVector{<:GSymbol}) where T return new{T}(symbols, true, zero(UInt)) end + GroupWord(v::AbstractVector{T}) where T<:GSymbol = GroupWord{T}(v) + GroupWord{T}(s::GSymbol) where T<:GSymbol = GroupWord{T}(T[s]) + GroupWord(s::T) where T<:GSymbol = GroupWord{T}(s) end diff --git a/src/words.jl b/src/words.jl index ab08337..9ef7383 100644 --- a/src/words.jl +++ b/src/words.jl @@ -37,3 +37,5 @@ Base.@propagate_inbounds function Base.getindex(w::GWord, i::Integer) end # no setindex! for syllable based words + +Base.convert(::Type{GW}, s::GSymbol) where GW <: GWord = GW(s) From b125871697320de84575fc4cefa5edca8ee70ad5 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 05:23:06 +0100 Subject: [PATCH 26/34] unify the two definitions of generate_balls; rename to metric_ball --- src/Groups.jl | 50 ++++++++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/src/Groups.jl b/src/Groups.jl index baf308a..3f5a27d 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -75,45 +75,31 @@ end # # Misc # -############################################################################### -function generate_balls(S::AbstractVector{T}, Id::T=one(parent(first(S))); - radius=2, op=*) where T<:GroupElem +@doc doc""" + gens(G::AbstractFPGroups) +> returns vector of generators of `G`, as its elements. +""" +AbstractAlgebra.gens(G::AbstractFPGroup) = G.(G.gens) + +@doc doc""" + metric_ball(S::Vector{GroupElem}, center=Id; radius=2, op=*) +Compute metric ball as a list of elements of non-decreasing length, given the +word-length metric on 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 generate_balls(S::AbstractVector{T}, center::T=one(first(S)); + radius=2, op=*) where T<:Union{GroupElem, NCRingElem} sizes = Int[] - B = [Id] + B = [one(first(S))] for i in 1:radius BB = [op(i,j) for (i,j) in Base.product(B,S)] B = unique([B; vec(BB)]) push!(sizes, length(B)) end - return B, sizes + isone(center) && return B, sizes + return c.*B, sizes end -function generate_balls(S::AbstractVector{T}, Id::T=one(parent(first(S))); - radius=2, op=*) where {T<:NCRingElem} - sizes = Int[] - B = [Id] - for i in 1:radius - BB = [op(i,j) for (i,j) in Base.product(B,S)] - B = unique([B; vec(BB)]) - push!(sizes, length(B)) - end - return B, sizes -end - -########### iteration for GFField - - -length(F::AbstractAlgebra.GFField) = order(F) - -function iterate(F::AbstractAlgebra.GFField, s=0) - if s >= order(F) - return nothing - else - return F(s), s+1 - end -end - -eltype(::Type{AbstractAlgebra.GFField{I}}) where I = AbstractAlgebra.gfelem{I} - end # of module Groups From ac4ee69fc6ceb602fb63b58e102180e603004beb Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 05:24:34 +0100 Subject: [PATCH 27/34] general cleanup --- src/FPGroups.jl | 53 ++++++++++---------------------------------- src/FreeGroup.jl | 57 ++++++++++++++---------------------------------- src/Groups.jl | 37 ++++++++++--------------------- 3 files changed, 39 insertions(+), 108 deletions(-) diff --git a/src/FPGroups.jl b/src/FPGroups.jl index 556ca50..c5ca9b8 100644 --- a/src/FPGroups.jl +++ b/src/FPGroups.jl @@ -28,35 +28,29 @@ export FPGroupElem, FPGroup # # Type and parent object methods # -############################################################################### -elem_type(::Type{FPGroup}) = FPGroupElem -parent_type(::Type{FPGroupElem}) = FPGroup +AbstractAlgebra.elem_type(::Type{FPGroup}) = FPGroupElem +AbstractAlgebra.parent_type(::Type{FPGroupElem}) = FPGroup ############################################################################### # # FPSymbol constructors # -############################################################################### FPSymbol(s::Symbol) = FPSymbol(s, 1) FPSymbol(s::String) = FPSymbol(Symbol(s)) FPSymbol(s::GSymbol) = FPSymbol(s.id, s.pow) -convert(::Type{FPSymbol}, s::FreeSymbol) = FPSymbol(s.id, s.pow) - +FPGroup(n::Int, symbol::String="f") = FPGroup([Symbol(symbol,i) for i in 1:n]) +FPGroup(a::AbstractVector) = FPGroup([FPSymbol(i) for i in a]) FPGroup(gens::Vector{FPSymbol}) = FPGroup(gens, Dict{FreeGroupElem, FreeGroupElem}()) -FPGroup(a::Vector{String}) = FPGroup([FPSymbol(i) for i in a]) - -FPGroup(n::Int, symbol::String="f") = FPGroup(["$symbol$i" for i in 1:n]) FPGroup(H::FreeGroup) = FPGroup([FPSymbol(s) for s in H.gens]) ############################################################################### # # Parent object call overloads # -############################################################################### function (G::FPGroup)(w::GWord) if isempty(w) @@ -80,48 +74,23 @@ function (G::FPGroup)(w::GWord) return reduce!(w) end - -############################################################################### -# -# Basic manipulation -# -############################################################################### (G::FPGroup)(s::GSymbol) = G(FPGroupElem(s)) ############################################################################### # # String I/O # -############################################################################### function show(io::IO, G::FPGroup) - print(io, "FPgroup on $(length(G.gens)) generators ") - strrels = join(G.rels, ", ") - if length(strrels) > 300 - print(io, "⟨ ", join(G.gens, ", "), " | $(length(G.rels)) relation(s) ⟩.") - else - print(io, "⟨ ", join(G.gens, ", "), " | ", join(G.rels, ", "), " ⟩.") - end + print(io, "FPgroup on $(length(G.gens)) generators ") + strrels = join(G.rels, ", ") + if length(strrels) > 200 + print(io, "⟨ ", join(G.gens, ", "), " | $(length(G.rels)) relation(s) ⟩.") + else + print(io, "⟨ ", join(G.gens, ", "), " | ", join(G.rels, ", "), " ⟩.") + end end -############################################################################### -# -# Comparison -# -############################################################################### - -############################################################################### -# -# Inversion -# -############################################################################### - -############################################################################### -# -# Binary operations -# -############################################################################### - function reduce!(W::FPGroupElem) reduced = false while !reduced diff --git a/src/FreeGroup.jl b/src/FreeGroup.jl index 36cf7c0..9800d62 100644 --- a/src/FreeGroup.jl +++ b/src/FreeGroup.jl @@ -2,7 +2,6 @@ # # FreeSymbol/FreeGroupElem/FreeGroup definition # -############################################################################### struct FreeSymbol <: GSymbol id::Symbol @@ -14,7 +13,7 @@ FreeGroupElem = GroupWord{FreeSymbol} mutable struct FreeGroup <: AbstractFPGroup gens::Vector{FreeSymbol} - function FreeGroup(gens::Vector{T}) where {T<:GSymbol} + function FreeGroup(gens::AbstractVector{T}) where {T<:GSymbol} G = new(gens) G.gens = gens return G @@ -27,72 +26,48 @@ export FreeGroupElem, FreeGroup # # Type and parent object methods # -############################################################################### -elem_type(::Type{FreeGroup}) = FreeGroupElem - -parent_type(::Type{FreeGroupElem}) = FreeGroup +AbstractAlgebra.elem_type(::Type{FreeGroup}) = FreeGroupElem +AbstractAlgebra.parent_type(::Type{FreeGroupElem}) = FreeGroup ############################################################################### # # FreeSymbol constructors # -############################################################################### FreeSymbol(s::Symbol) = FreeSymbol(s,1) -FreeSymbol(s::String) = FreeSymbol(Symbol(s)) +FreeSymbol(s::AbstractString) = FreeSymbol(Symbol(s)) +FreeSymbol(s::GSymbol) = FreeSymbol(s.id, s.pow) FreeGroup(n::Int, symbol::String="f") = FreeGroup([Symbol(symbol,i) for i in 1:n]) - -FreeGroup(a::Vector) = FreeGroup(FreeSymbol.(a)) +FreeGroup(a::AbstractVector) = FreeGroup(FreeSymbol.(a)) ############################################################################### # # Parent object call overloads # -############################################################################### function (G::FreeGroup)(w::GroupWord{FreeSymbol}) - if length(syllables(w)) > 0 - for s in w.symbols - i = findfirst(g -> g.id == s.id, G.gens) - i == 0 && throw(DomainError( - "Symbol $s does not belong to $G.")) - s.pow % G.gens[i].pow == 0 || throw(DomainError( - "Symbol $s doesn't belong to $G.")) - end + for s in syllables(w) + i = findfirst(g -> g.id == s.id, G.gens) + isnothing(i) && throw(DomainError( + "Symbol $s does not belong to $G.")) + s.pow % G.gens[i].pow == 0 || throw(DomainError( + "Symbol $s doesn't belong to $G.")) end - w.parent = G - return w + setparent!(w, G) + return reduce!(w) end -(G::FreeGroup)(s::FreeSymbol) = G(FreeGroupElem(s)) - -############################################################################### -# -# Basic manipulation -# -############################################################################### +(G::FreeGroup)(s::GSymbol) = G(FreeGroupElem(s)) +(G::FreeGroup)(v::AbstractVector{<:GSymbol}) = G(FreeGroupElem(FreeSymbol.(v))) ############################################################################### # # String I/O # -############################################################################### function show(io::IO, G::FreeGroup) print(io, "Free group on $(length(G.gens)) generators: ") join(io, G.gens, ", ") end - -############################################################################### -# -# Comparison -# -############################################################################### - -############################################################################### -# -# Inversion -# -############################################################################### diff --git a/src/Groups.jl b/src/Groups.jl index 3f5a27d..443dd46 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -15,7 +15,12 @@ using Markdown include("types.jl") -include("gsymbols.jl") + +include("FreeGroup.jl") +include("FPGroups.jl") +include("AutGroup.jl") + +include("symbols.jl") include("fallbacks.jl") include("words.jl") include("hashing.jl") @@ -23,51 +28,33 @@ include("freereduce.jl") include("arithmetic.jl") include("findreplace.jl") -include("FreeGroup.jl") -include("FPGroups.jl") -include("AutGroup.jl") - include("DirectPower.jl") include("WreathProducts.jl") -@doc doc""" - gens(G::AbstractFPGroups) -> returns vector of generators of `G`, as its elements. - -""" -gens(G::AbstractFPGroup) = [G(g) for g in G.gens] - ############################################################################### # # String I/O # -############################################################################### @doc doc""" show(io::IO, W::GWord) > The actual string produced by show depends on the eltype of `W.symbols`. """ -function show(io::IO, W::GWord) +function Base.show(io::IO, W::GWord) if length(W) == 0 print(io, "(id)") else - join(io, [string(s) for s in W.symbols], "*") + join(io, (string(s) for s in syllables(W)), "*") end end -function show(io::IO, s::T) where {T<:GSymbol} - if s.pow == 1 - print(io, string(s.id)) - else - print(io, string((s.id))*"^$(s.pow)") - end -end +function Base.show(io::IO, s::T) where {T<:GSymbol} + if s.pow == 1 + print(io, string(s.id)) else - G = parent(W) - w = T([inv(s) for s in Iterators.reverse(syllables(W))]) - return G(w) + print(io, "$(s.id)^$(s.pow)") end end From 7d95338e3302c9eed886406d2e5c2398765d1f44 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 05:25:02 +0100 Subject: [PATCH 28/34] fix tests --- test/AutGroup-tests.jl | 127 ++++++++++++++++++++-------------------- test/FPGroup-tests.jl | 13 ++-- test/FreeGroup-tests.jl | 39 ++++++------ 3 files changed, 87 insertions(+), 92 deletions(-) diff --git a/test/AutGroup-tests.jl b/test/AutGroup-tests.jl index 8a0c9b9..3d140d6 100644 --- a/test/AutGroup-tests.jl +++ b/test/AutGroup-tests.jl @@ -8,72 +8,73 @@ f = Groups.AutSymbol(:a, 1, Groups.FlipAut(2)) @test isa(f, Groups.GSymbol) @test isa(f, Groups.AutSymbol) - @test isa(Groups.perm_autsymbol(Int8.([1,2,3,4])), Groups.AutSymbol) - @test isa(Groups.rmul_autsymbol(1,2), Groups.AutSymbol) - @test isa(Groups.lmul_autsymbol(3,4), Groups.AutSymbol) - @test isa(Groups.flip_autsymbol(3), Groups.AutSymbol) + @test isa(Groups.AutSymbol(perm"(4)"), Groups.AutSymbol) + @test isa(Groups.AutSymbol([2,3,4,1]), Groups.AutSymbol) + @test isa(Groups.transvection_R(1,2), Groups.AutSymbol) + @test isa(Groups.transvection_R(3,4), Groups.AutSymbol) + @test isa(Groups.flip(3), Groups.AutSymbol) end a,b,c,d = gens(FreeGroup(4)) D = NTuple{4,FreeGroupElem}([a,b,c,d]) - @testset "flip_autsymbol correctness" begin - @test Groups.flip_autsymbol(1)(deepcopy(D)) == (a^-1, b,c,d) - @test Groups.flip_autsymbol(2)(deepcopy(D)) == (a, b^-1,c,d) - @test Groups.flip_autsymbol(3)(deepcopy(D)) == (a, b,c^-1,d) - @test Groups.flip_autsymbol(4)(deepcopy(D)) == (a, b,c,d^-1) - @test inv(Groups.flip_autsymbol(1))(deepcopy(D)) == (a^-1, b,c,d) - @test inv(Groups.flip_autsymbol(2))(deepcopy(D)) == (a, b^-1,c,d) - @test inv(Groups.flip_autsymbol(3))(deepcopy(D)) == (a, b,c^-1,d) - @test inv(Groups.flip_autsymbol(4))(deepcopy(D)) == (a, b,c,d^-1) + @testset "flip correctness" begin + @test Groups.flip(1)(deepcopy(D)) == (a^-1, b,c,d) + @test Groups.flip(2)(deepcopy(D)) == (a, b^-1,c,d) + @test Groups.flip(3)(deepcopy(D)) == (a, b,c^-1,d) + @test Groups.flip(4)(deepcopy(D)) == (a, b,c,d^-1) + @test inv(Groups.flip(1))(deepcopy(D)) == (a^-1, b,c,d) + @test inv(Groups.flip(2))(deepcopy(D)) == (a, b^-1,c,d) + @test inv(Groups.flip(3))(deepcopy(D)) == (a, b,c^-1,d) + @test inv(Groups.flip(4))(deepcopy(D)) == (a, b,c,d^-1) end - @testset "perm_autsymbol correctness" begin - σ = Groups.perm_autsymbol([1,2,3,4]) + @testset "perm correctness" begin + σ = Groups.AutSymbol(perm"(4)") @test σ(deepcopy(D)) == deepcopy(D) @test inv(σ)(deepcopy(D)) == deepcopy(D) - σ = Groups.perm_autsymbol([2,3,4,1]) + σ = Groups.AutSymbol(perm"(1,2,3,4)") @test σ(deepcopy(D)) == (b, c, d, a) @test inv(σ)(deepcopy(D)) == (d, a, b, c) - σ = Groups.perm_autsymbol([2,1,4,3]) + σ = Groups.AutSymbol(perm"(1,2)(4,3)") @test σ(deepcopy(D)) == (b, a, d, c) @test inv(σ)(deepcopy(D)) == (b, a, d, c) - σ = Groups.perm_autsymbol([2,3,1,4]) + σ = Groups.AutSymbol(perm"(1,2,3)(4)") @test σ(deepcopy(D)) == (b, c, a, d) @test inv(σ)(deepcopy(D)) == (c, a, b, d) end - @testset "rmul/lmul_autsymbol correctness" begin + @testset "rmul/transvection_R correctness" begin i,j = 1,2 - r = Groups.rmul_autsymbol(i,j) - l = Groups.lmul_autsymbol(i,j) + r = Groups.transvection_R(i,j) + l = Groups.transvection_L(i,j) @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.rmul_autsymbol(i,j) - l = Groups.lmul_autsymbol(i,j) + r = Groups.transvection_R(i,j) + l = Groups.transvection_L(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.rmul_autsymbol(i,j) - l = Groups.lmul_autsymbol(i,j) + r = Groups.transvection_R(i,j) + l = Groups.transvection_L(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.rmul_autsymbol(i,j) - l = Groups.lmul_autsymbol(i,j) + r = Groups.transvection_R(i,j) + l = Groups.transvection_L(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) @@ -94,40 +95,40 @@ @test length(Groups.gens(A)) == 0 A = AutGroup(FreeGroup(2)) @test length(Groups.gens(A)) == 7 - gens = Groups.gens(A) + Agens = Groups.gens(A) - @test isa(A(Groups.rmul_autsymbol(1,2)), Automorphism) - @test A(Groups.rmul_autsymbol(1,2)) in gens + @test isa(A(Groups.transvection_R(1,2)), Automorphism) + @test A(Groups.transvection_R(1,2)) in Agens - @test isa(A(Groups.rmul_autsymbol(2,1)), Automorphism) - @test A(Groups.rmul_autsymbol(2,1)) in gens + @test isa(A(Groups.transvection_R(2,1)), Automorphism) + @test A(Groups.transvection_R(2,1)) in Agens - @test isa(A(Groups.lmul_autsymbol(1,2)), Automorphism) - @test A(Groups.lmul_autsymbol(1,2)) in gens + @test isa(A(Groups.transvection_R(1,2)), Automorphism) + @test A(Groups.transvection_R(1,2)) in Agens - @test isa(A(Groups.lmul_autsymbol(2,1)), Automorphism) - @test A(Groups.lmul_autsymbol(2,1)) in gens + @test isa(A(Groups.transvection_R(2,1)), Automorphism) + @test A(Groups.transvection_R(2,1)) in Agens - @test isa(A(Groups.flip_autsymbol(1)), Automorphism) - @test A(Groups.flip_autsymbol(1)) in gens + @test isa(A(Groups.flip(1)), Automorphism) + @test A(Groups.flip(1)) in Agens - @test isa(A(Groups.flip_autsymbol(2)), Automorphism) - @test A(Groups.flip_autsymbol(2)) in gens + @test isa(A(Groups.flip(2)), Automorphism) + @test A(Groups.flip(2)) in Agens - @test isa(A(Groups.perm_autsymbol([2,1])), Automorphism) - @test A(Groups.perm_autsymbol([2,1])) in gens + @test isa(A(Groups.AutSymbol(perm"(1,2)")), Automorphism) + @test A(Groups.AutSymbol(perm"(1,2)")) in Agens end A = AutGroup(FreeGroup(4)) @testset "eltary functions" begin - f = Groups.perm_autsymbol([2,3,4,1]) + f = Groups.AutSymbol(perm"(1,2,3,4)") @test (Groups.change_pow(f, 2)).pow == 1 @test (Groups.change_pow(f, -2)).pow == 1 @test (inv(f)).pow == 1 - f = Groups.perm_autsymbol([2,1,4,3]) + f = Groups.AutSymbol(perm"(1,2)(3,4)") @test isa(inv(f), Groups.AutSymbol) @test_throws MethodError f*f @@ -136,14 +137,15 @@ end @testset "reductions/arithmetic" begin - f = Groups.perm_autsymbol([2,3,4,1]) + f = Groups.AutSymbol(perm"(1,2,3,4)") - f² = Groups.r_multiply(A(f), [f], reduced=false) - @test Groups.simplifyperms!(f²) == false + f² = append!(A(f), [f]) + @test Groups.simplifyperms!(Bool, f²) == false @test f²^2 == one(A) + @test !isone(f²) - a = A(Groups.rmul_autsymbol(1,2))*Groups.flip_autsymbol(2) - b = Groups.flip_autsymbol(2)*A(inv(Groups.rmul_autsymbol(1,2))) + a = A(Groups.transvection_L(1,2))*Groups.flip(2) + b = Groups.flip(2)*A(inv(Groups.transvection_L(1,2))) @test a*b == b*a @test a^3 * b^3 == one(A) g,h = Groups.gens(A)[[1,8]] # (g, h) = (ϱ₁₂, ϱ₃₂) @@ -164,27 +166,22 @@ # Not so simple arithmetic: applying starting on the left: # ϱ₁₂*ϱ₂₁⁻¹*λ₁₂*ε₂ == σ₂₁₃₄ - g = A(Groups.rmul_autsymbol(1,2)) + g = A(Groups.transvection_R(1,2)) x1, x2, x3, x4 = Groups.domain(A) @test g(Groups.domain(A)) == (x1*x2, x2, x3, x4) - g = g*inv(A(Groups.rmul_autsymbol(2,1))) + g = g*inv(A(Groups.transvection_R(2,1))) @test g(Groups.domain(A)) == (x1*x2, x1^-1, x3, x4) - g = g*A(Groups.lmul_autsymbol(1,2)) + g = g*A(Groups.transvection_L(1,2)) @test g(Groups.domain(A)) == (x2, x1^-1, x3, x4) - g = g*A(Groups.flip_autsymbol(2)) + g = g*A(Groups.flip(2)) @test g(Groups.domain(A)) == (x2, x1, x3, x4) - @test g(Groups.domain(A)) == A(Groups.perm_autsymbol([2,1,3,4]))(Groups.domain(A)) + @test g(Groups.domain(A)) == A(Groups.AutSymbol(perm"(1,2)(4)"))(Groups.domain(A)) - @test g == A(Groups.perm_autsymbol([2,1,3,4])) + @test g == A(Groups.AutSymbol(perm"(1,2)(4)")) g_im = g(Groups.domain(A)) - @test length(g_im[1]) == 5 - @test length(g_im[2]) == 3 - @test length(g_im[3]) == 1 - @test length(g_im[4]) == 1 - @test length.(Groups.reduce!.(g_im)) == (1,1,1,1) - + @test length.(g_im) == (1,1,1,1) end @testset "specific Aut(F4) tests" begin @@ -219,8 +216,8 @@ M = Matrix{Int}(I, N, N) M[1,2] = 1 - ϱ₁₂ = G(Groups.rmul_autsymbol(1,2)) - λ₁₂ = G(Groups.rmul_autsymbol(1,2)) + ϱ₁₂ = G(Groups.transvection_R(1,2)) + λ₁₂ = G(Groups.transvection_R(1,2)) @test Groups.linear_repr(ϱ₁₂) == M @test Groups.linear_repr(λ₁₂) == M @@ -235,14 +232,14 @@ M = Matrix{Int}(I, N, N) M[2,2] = -1 - ε₂ = G(Groups.flip_autsymbol(2)) + ε₂ = G(Groups.flip(2)) @test Groups.linear_repr(ε₂) == M @test Groups.linear_repr(ε₂^2) == Matrix{Int}(I, N, N) M = [0 1 0; 0 0 1; 1 0 0] - σ = G(Groups.perm_autsymbol([2,3,1])) + σ = G(Groups.AutSymbol(perm"(1,2,3)")) @test Groups.linear_repr(σ) == M @test Groups.linear_repr(σ^3) == Matrix{Int}(I, 3, 3) @test Groups.linear_repr(σ)^3 == Matrix{Int}(I, 3, 3) diff --git a/test/FPGroup-tests.jl b/test/FPGroup-tests.jl index 831b78e..ddf29e9 100644 --- a/test/FPGroup-tests.jl +++ b/test/FPGroup-tests.jl @@ -1,15 +1,18 @@ @testset "FPGroups definitions" begin F = FreeGroup(["a", "b", "c"]) - a,b,c = gens(F) + a,b,c = Groups.gens(F) R = [a^2, a*b*a, c*b*a] @test F/R isa FPGroup @test F isa FreeGroup G = F/R - A,B,C = gens(G) + A,B,C = Groups.gens(G) - @test A^2 == one(G) - @test A*B*A*A == A - @test A*A*B*A == B*A + @test Groups.reduce!(A^2) == one(G) + @test Groups.reduce!(A*B*A*A) == A + @test Groups.reduce!(A*A*B*A) == A + + @test Groups.freepreimage(G) == F + @test Groups.freepreimage(B^2) == b^2 @test G/[B^2, C*B*C] isa FPGroup end diff --git a/test/FreeGroup-tests.jl b/test/FreeGroup-tests.jl index cd0df27..da39e75 100644 --- a/test/FreeGroup-tests.jl +++ b/test/FreeGroup-tests.jl @@ -55,23 +55,18 @@ end @testset "internal arithmetic" begin - @test Vector{Groups.FreeGroupElem}([s,t]) == [Groups.GroupWord(s), Groups.GroupWord(t)] @test (s*s).symbols == (s^2).symbols @test hash([t^1,s^1]) == hash([t^2*inv(t),s*inv(s)*s]) t_symb = Groups.FreeSymbol(:t) tt = deepcopy(t) - @test string(Groups.r_multiply!(tt,[inv(t_symb)]; reduced=true)) == - "(id)" + @test string(Groups.rmul!(tt, tt, inv(t_symb))) == "(id)" tt = deepcopy(t) - @test string(Groups.r_multiply!(tt,[inv(t_symb)]; reduced=false)) == - "t*t^-1" + @test string(append!(tt, [inv(t_symb)])) == "t*t^-1" tt = deepcopy(t) - @test string(Groups.l_multiply!(tt,[inv(t_symb)]; reduced=true)) == - "(id)" + @test string(Groups.lmul!(tt, tt, inv(t_symb))) == "(id)" tt = deepcopy(t) - @test string(Groups.l_multiply!(tt,[inv(t_symb)]; reduced=false)) == - "t^-1*t" + @test string(prepend!(tt, [inv(t_symb)])) == "t^-1*t" end @testset "reductions" begin @@ -79,7 +74,7 @@ end @test length((one(G)*one(G)).symbols) == 0 @test one(G) == one(G)*one(G) w = deepcopy(s) - push!(w.symbols, (s^-1).symbols[1]) + push!(Groups.syllables(w), (s^-1).symbols[1]) @test Groups.reduce!(w) == one(parent(w)) o = (t*s)^3 @test o == t*s*t*s*t*s @@ -116,23 +111,23 @@ end @test Groups.issubsymbol(inv(b), Groups.change_pow(b,-2)) == true c = s*t*s^-1*t^-1 - @test findfirst(c, s^-1*t^-1) == 3 - @test findnext(c*s^-1, s^-1*t^-1,3) == 3 - @test findnext(c*s^-1*t^-1, s^-1*t^-1,4) == 5 - @test findfirst(c*t, c) == 0 + @test findfirst(s^-1*t^-1, c) == 3 + @test findnext(s^-1*t^-1, c*s^-1,3) == 3 + @test findnext(s^-1*t^-1, c*s^-1*t^-1,4) == 5 + @test findfirst(c, c*t) === nothing w = s*t*s^-1 subst = Dict{FreeGroupElem, FreeGroupElem}(w => s^1, s*t^-1 => t^4) - @test Groups.replace(c, 1, s*t, one(G)) == s^-1*t^-1 - @test Groups.replace(c, 1, w, subst[w]) == s*t^-1 - @test Groups.replace(s*c*t^-1, 1, w, subst[w]) == s^2*t^-2 - @test Groups.replace(t*c*t, 2, w, subst[w]) == t*s - @test Groups.replace_all(s*c*s*c*s, subst) == s*t^4*s*t^4*s + @test Groups.replace(c, s*t=>one(G)) == s^-1*t^-1 + @test Groups.replace(c, w=>subst[w]) == s*t^-1 + @test Groups.replace(s*c*t^-1, w=>subst[w]) == s^2*t^-2 + @test Groups.replace(t*c*t, w=>subst[w]) == t*s + @test Groups.replace(s*c*s*c*s, subst) == s*t^4*s*t^4*s G = FreeGroup(["x", "y"]) x,y = gens(G) - @test Groups.replace(x*y^9, 2, y^2, y) == x*y^8 - @test Groups.replace(x^3, 1, x^2, y) == x*y - @test Groups.replace(y*x^3*y, 2, x^2, y) == y*x*y^2 + @test Groups.replace(x*y^9, y^2=>y) == x*y^5 + @test Groups.replace(x^3, x^2=>y) == x*y + @test Groups.replace(y*x^3*y, x^2=>y) == y*x*y^2 end end From 622f5bc6b308f73a115d19d55f739831bec95b20 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 13:46:32 +0100 Subject: [PATCH 29/34] fix coercion to FPGroup --- src/FPGroups.jl | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/FPGroups.jl b/src/FPGroups.jl index c5ca9b8..ff25389 100644 --- a/src/FPGroups.jl +++ b/src/FPGroups.jl @@ -53,25 +53,20 @@ FPGroup(H::FreeGroup) = FPGroup([FPSymbol(s) for s in H.gens]) # function (G::FPGroup)(w::GWord) - if isempty(w) - return one(G) - end + if isempty(w) + return one(G) + end - if eltype(w.symbols) == FreeSymbol - w = FPGroupElem(FPSymbol.(w.symbols)) - end + @boundscheck for s in syllables(w) + i = findfirst(g -> g.id == s.id, G.gens) + i == 0 && throw(DomainError("Symbol $s does not belong to $G.")) + s.pow % G.gens[i].pow != 0 && throw( + DomainError("Symbol $s doesn't belong to $G.")) + end - if eltype(w.symbols) == FPSymbol - for s in w.symbols - i = findfirst(g -> g.id == s.id, G.gens) - i == 0 && throw(DomainError( - "Symbol $s does not belong to $G.")) - s.pow % G.gens[i].pow == 0 || throw(DomainError( - "Symbol $s doesn't belong to $G.")) - end - end - w.parent = G - return reduce!(w) + w = FPGroupElem(FPSymbol.(syllables(w))) + setparent!(w, G) + return reduce!(w) end (G::FPGroup)(s::GSymbol) = G(FPGroupElem(s)) From 0bee697ed82c6e1c40fe247e443972faa943e33e Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 13:48:44 +0100 Subject: [PATCH 30/34] update to AbstractAlgebra v0.9 --- Project.toml | 6 +++--- src/AutGroup.jl | 2 +- src/WreathProducts.jl | 15 +++++++++------ src/fallbacks.jl | 3 +-- test/AutGroup-tests.jl | 2 +- test/DirectPower-tests.jl | 10 +++++----- test/WreathProd-tests.jl | 12 ++++++------ 7 files changed, 26 insertions(+), 24 deletions(-) diff --git a/Project.toml b/Project.toml index 5b2d400..5964d55 100644 --- a/Project.toml +++ b/Project.toml @@ -8,11 +8,11 @@ AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" +[compat] +AbstractAlgebra = "^0.9.0" + [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test"] - -[compat] -AbstractAlgebra = "^0.7.0" diff --git a/src/AutGroup.jl b/src/AutGroup.jl index 28374bb..3499ec7 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -170,7 +170,7 @@ function AutGroup(G::FreeGroup; special=false) if !special flips = [flip(i) for i in 1:n] - syms = [AutSymbol(p) for p in PermutationGroup(Int8(n))][2:end] + syms = [AutSymbol(p) for p in SymmetricGroup(Int8(n))][2:end] append!(S, [flips; syms]) end diff --git a/src/WreathProducts.jl b/src/WreathProducts.jl index 18f0961..3754892 100644 --- a/src/WreathProducts.jl +++ b/src/WreathProducts.jl @@ -1,5 +1,7 @@ export WreathProduct, WreathProductElem +import AbstractAlgebra: AbstractPermutationGroup, AbstractPerm + ############################################################################### # # WreathProduct / WreathProductElem @@ -19,22 +21,23 @@ export WreathProduct, WreathProductElem * `N::Group` : the single factor of the group $N$ * `P::Generic.PermGroup` : full `PermutationGroup` """ -struct WreathProduct{N, T<:Group, PG<:Generic.PermGroup} <: Group +struct WreathProduct{N, T<:Group, PG<:AbstractPermutationGroup} <: Group N::DirectPowerGroup{N, T} P::PG - function WreathProduct(Gr::T, P::PG) where {T, PG<:Generic.PermGroup} - N = DirectPowerGroup(Gr, Int(P.n)) - return new{Int(P.n), T, PG}(N, P) + function WreathProduct(G::Gr, P::PG) where + {Gr <: Group, PG <: AbstractPermutationGroup} + N = DirectPowerGroup(G, Int(P.n)) + return new{Int(P.n), Gr, PG}(N, P) end end -struct WreathProductElem{N, T<:GroupElem, P<:Generic.Perm} <: GroupElem +struct WreathProductElem{N, T<:GroupElem, P<:AbstractPerm} <: GroupElem n::DirectPowerGroupElem{N, T} p::P function WreathProductElem(n::DirectPowerGroupElem{N,T}, p::P, - check::Bool=true) where {N, T, P<:Generic.Perm} + check::Bool=true) where {N, T, P<:AbstractPerm} if check N == length(p.d) || throw(DomainError( "Can't form WreathProductElem: lengths differ")) diff --git a/src/fallbacks.jl b/src/fallbacks.jl index bc1d1db..8fccc98 100644 --- a/src/fallbacks.jl +++ b/src/fallbacks.jl @@ -1,6 +1,5 @@ # workarounds -Base.one(G::Generic.PermGroup) = Generic.Perm(G.n) -Base.one(r::NCRingElem) = one(parent(r)) +Base.one(G::Generic.SymmetricGroup) = Generic.Perm(G.n) # fallback definitions # note: the user should implement those on type, when possible diff --git a/test/AutGroup-tests.jl b/test/AutGroup-tests.jl index 3d140d6..8699874 100644 --- a/test/AutGroup-tests.jl +++ b/test/AutGroup-tests.jl @@ -1,6 +1,6 @@ @testset "Automorphisms" begin - G = PermutationGroup(Int8(4)) + G = SymmetricGroup(Int8(4)) @testset "AutSymbol" begin @test_throws MethodError Groups.AutSymbol(:a) diff --git a/test/DirectPower-tests.jl b/test/DirectPower-tests.jl index 5eb4a80..65722f5 100644 --- a/test/DirectPower-tests.jl +++ b/test/DirectPower-tests.jl @@ -3,11 +3,11 @@ ×(a,b) = Groups.DirectPower(a,b) @testset "Constructors" begin - G = PermutationGroup(3) + G = SymmetricGroup(3) @test Groups.DirectPowerGroup(G,2) isa AbstractAlgebra.Group @test G×G isa AbstractAlgebra.Group - @test Groups.DirectPowerGroup(G,2) isa Groups.DirectPowerGroup{2, Generic.PermGroup{Int64}} + @test Groups.DirectPowerGroup(G,2) isa Groups.DirectPowerGroup{2, Generic.SymmetricGroup{Int64}} @test (G×G)×G == DirectPowerGroup(G, 3) @test (G×G)×G == (G×G)×G @@ -42,7 +42,7 @@ end @testset "Basic arithmetic" begin - G = PermutationGroup(3) + G = SymmetricGroup(3) GG = G×G i = perm"(1,3)" g = perm"(1,2,3)" @@ -65,7 +65,7 @@ end @testset "elem/parent_types" begin - G = PermutationGroup(3) + G = SymmetricGroup(3) g = perm"(1,2,3)" @test elem_type(G×G) == DirectPowerGroupElem{2, elem_type(G)} @@ -75,7 +75,7 @@ end @testset "Misc" begin - G = PermutationGroup(3) + G = SymmetricGroup(3) GG = Groups.DirectPowerGroup(G,3) @test order(GG) == 216 diff --git a/test/WreathProd-tests.jl b/test/WreathProd-tests.jl index 33aafb7..158e056 100644 --- a/test/WreathProd-tests.jl +++ b/test/WreathProd-tests.jl @@ -1,6 +1,6 @@ @testset "WreathProducts" begin - S_3 = PermutationGroup(3) - S_2 = PermutationGroup(2) + S_3 = SymmetricGroup(3) + S_2 = SymmetricGroup(2) b = perm"(1,2,3)" a = perm"(1,2)" @@ -8,7 +8,7 @@ @test Groups.WreathProduct(S_2, S_3) isa AbstractAlgebra.Group B3 = Groups.WreathProduct(S_2, S_3) @test B3 isa Groups.WreathProduct - @test B3 isa WreathProduct{3, Generic.PermGroup{Int}, Generic.PermGroup{Int}} + @test B3 isa WreathProduct{3, Generic.SymmetricGroup{Int}, Generic.SymmetricGroup{Int}} aa = Groups.DirectPowerGroupElem((a^0 ,a, a^2)) @@ -37,7 +37,7 @@ @test elem_type(B3) == Groups.WreathProductElem{3, Generic.Perm{Int}, Generic.Perm{Int}} - @test parent_type(typeof(one(B3))) == Groups.WreathProduct{3, parent_type(typeof(one(B3.N.group))), Generic.PermGroup{Int}} + @test parent_type(typeof(one(B3))) == Groups.WreathProduct{3, parent_type(typeof(one(B3.N.group))), Generic.SymmetricGroup{Int}} @test parent(one(B3)) == Groups.WreathProduct(S_2,S_3) @test parent(one(B3)) == B3 @@ -64,7 +64,7 @@ end @testset "Group arithmetic" begin - B4 = Groups.WreathProduct(PermutationGroup(3), PermutationGroup(4)) + B4 = Groups.WreathProduct(SymmetricGroup(3), SymmetricGroup(4)) id, a, b = perm"(3)", perm"(1,2)(3)", perm"(1,2,3)" @@ -84,7 +84,7 @@ end @testset "Iteration" begin - Wr = WreathProduct(PermutationGroup(2),PermutationGroup(4)) + Wr = WreathProduct(SymmetricGroup(2),SymmetricGroup(4)) elts = collect(Wr) @test elts isa Vector{Groups.WreathProductElem{4, Generic.Perm{Int}, Generic.Perm{Int}}} From c1f54ad41e01e8e51079c43344fa47309cc75fac Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 13:48:58 +0100 Subject: [PATCH 31/34] bump Groups to v0.4 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 5964d55..58a8539 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Groups" uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557" authors = ["Marek Kaluba "] -version = "0.3.0" +version = "0.4.0" [deps] AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" From ebefc7e399cec73195fa0765865e7644510f5e35 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 15:42:20 +0100 Subject: [PATCH 32/34] reshuffle AutGroup --- src/AutGroup.jl | 241 +++++++++++++++++++----------------------------- 1 file changed, 94 insertions(+), 147 deletions(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index 3499ec7..1c9206a 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -1,8 +1,9 @@ +export Automorphism, AutGroup, Aut, SAut + ############################################################################### # # AutSymbol/ AutGroup / Automorphism # -############################################################################### struct RTransvect i::Int8 @@ -30,70 +31,6 @@ struct AutSymbol <: GSymbol fn::Union{LTransvect, RTransvect, PermAut, FlipAut, Identity} end -mutable struct AutGroup{N} <: AbstractFPGroup - objectGroup::FreeGroup - gens::Vector{AutSymbol} -end - -mutable struct Automorphism{N} <: GWord{AutSymbol} - symbols::Vector{AutSymbol} - modified::Bool - savedhash::UInt - parent::AutGroup{N} - - function Automorphism{N}(f::Vector{AutSymbol}) where {N} - return new{N}(f, true, zero(UInt)) - end -end - -export Automorphism, AutGroup, Aut, SAut - -############################################################################### -# -# Type and parent object methods -# -############################################################################### - -elem_type(::Type{AutGroup{N}}) where N = Automorphism{N} - -parent_type(::Automorphism{N}) where N = AutGroup{N} - -############################################################################### -# -# AutSymbol defining functions -# -############################################################################### - -function (ϱ::RTransvect)(v, pow::Integer=1) - append!(v[ϱ.i], v[ϱ.j]^pow) - freereduce!(v[ϱ.i]) - return v -end - -function (λ::LTransvect)(v, pow::Integer=1) - prepend!(v[λ.i], v[λ.j]^pow) - freereduce!(v[λ.i]) - return v -end - -function (σ::PermAut)(v, pow::Integer=1) - w = deepcopy(v) - s = (σ.perm^pow).d - @inbounds for k in eachindex(v) - v[k].symbols = w[s[k]].symbols - end - return v -end - -function (ɛ::FlipAut)(v, pow::Integer=1) - @inbounds if isodd(pow) - v[ɛ.i].symbols = inv(v[ɛ.i]).symbols - end - return v -end - -(::Identity)(v, pow::Integer=1) = v - # taken from ValidatedNumerics, under under the MIT "Expat" License: # https://github.com/JuliaIntervals/ValidatedNumerics.jl/blob/master/LICENSE.md function subscriptify(n::Integer) @@ -135,26 +72,54 @@ function AutSymbol(p::Generic.Perm, pow::Integer=1) return id_autsymbol() end -function AutSymbol(a::Vector{<:Integer}, pow=1) - return AutSymbol(Generic.Perm(convert(Vector{Int8}, a)), pow) -end +ϱ(i::Integer, j::Integer, pow::Integer=1) = transvection_R(i, j, pow) +λ(i::Integer, j::Integer, pow::Integer=1) = transvection_L(i, j, pow) +ε(i::Integer, pow::Integer=1) = flip(i, pow) +σ(v::Generic.Perm, pow::Integer=1) = AutSymbol(v, pow) -ϱ(i::Integer, j::Integer, pow::Integer=1) = transvection_R(i, j, pow=pow) -λ(i::Integer, j::Integer, pow::Integer=1) = transvection_L(i, j, pow=pow) -ε(i::Integer, pow::Integer=1) = flip(i, pow=pow) -σ(v, pow=1) = AutSymbol(v, pow=pow) - -function domain(G::AutGroup{N}) where N - F = G.objectGroup - gg = gens(F) - return ntuple(i->gg[i], N) +function change_pow(s::AutSymbol, n::Integer) + if n == zero(n) + return id_autsymbol() + end + symbol = s.fn + if symbol isa FlipAut + return flip(symbol.i, n) + elseif symbol isa PermAut + return AutSymbol(symbol.perm, n) + elseif symbol isa RTransvect + return transvection_R(symbol.i, symbol.j, n) + elseif symbol isa LTransvect + return transvection_L(symbol.i, symbol.j, n) + elseif symbol isa Identity + return s + else + throw(DomainError("Unknown type of AutSymbol: $s")) + end end ############################################################################### # -# AutGroup / Automorphism constructors +# AutGroup / Automorphism # -############################################################################### + +mutable struct AutGroup{N} <: AbstractFPGroup + objectGroup::FreeGroup + gens::Vector{AutSymbol} +end + +mutable struct Automorphism{N} <: GWord{AutSymbol} + symbols::Vector{AutSymbol} + modified::Bool + savedhash::UInt + parent::AutGroup{N} + + function Automorphism{N}(f::Vector{AutSymbol}) where {N} + return new{N}(f, true, zero(UInt)) + end +end + +elem_type(::Type{AutGroup{N}}) where N = Automorphism{N} +parent_type(::Type{Automorphism{N}}) where N = AutGroup{N} function AutGroup(G::FreeGroup; special=false) S = AutSymbol[] @@ -163,14 +128,14 @@ function AutGroup(G::FreeGroup; special=false) indexing = [[i,j] for i in 1:n for j in 1:n if i≠j] - rmuls = [transvection_R(i,j) for (i,j) in indexing] - lmuls = [transvection_L(i,j) for (i,j) in indexing] + rmuls = [ϱ(i,j) for (i,j) in indexing] + lmuls = [λ(i,j) for (i,j) in indexing] append!(S, [rmuls; lmuls]) if !special - flips = [flip(i) for i in 1:n] - syms = [AutSymbol(p) for p in SymmetricGroup(Int8(n))][2:end] + flips = [ε(i) for i in 1:n] + syms = [σ(p) for p in SymmetricGroup(Int8(n))][2:end] append!(S, [flips; syms]) end @@ -180,64 +145,86 @@ end Aut(G::Group) = AutGroup(G) SAut(G::Group) = AutGroup(G, special=true) -############################################################################### -# -# Types call overloads -# -############################################################################### - Automorphism{N}(s::AutSymbol) where N = Automorphism{N}(AutSymbol[s]) function (G::AutGroup{N})(f::AutSymbol) where N g = Automorphism{N}([f]) - g.parent = G + setparent!(g, G) return g end -function (G::AutGroup{N})(g::Automorphism{N}) where N - g.parent = G - return g +(G::AutGroup{N})(g::Automorphism{N}) where N = (setparent!(g, G); g) + +############################################################################### +# +# AutSymbol defining functions && evaluation +# NOTE: all automorphisms operate on a tuple of FreeWords INPLACE! +# + +function (ϱ::RTransvect)(v, pow::Integer=1) + append!(v[ϱ.i], v[ϱ.j]^pow) + freereduce!(v[ϱ.i]) + return v end +function (λ::LTransvect)(v, pow::Integer=1) + prepend!(v[λ.i], v[λ.j]^pow) + freereduce!(v[λ.i]) + return v +end + +function (σ::PermAut)(v, pow::Integer=1) + w = deepcopy(v) + s = (σ.perm^pow).d + @inbounds for k in eachindex(v) + v[k].symbols = w[s[k]].symbols + end + return v +end + +function (ɛ::FlipAut)(v, pow::Integer=1) + @inbounds if isodd(pow) + v[ɛ.i].symbols = inv(v[ɛ.i]).symbols + end + return v +end + +(::Identity)(v, pow::Integer=1) = v + ############################################################################### # # Functional call overloads for evaluation of AutSymbol and Automorphism # -############################################################################### (s::AutSymbol)(v::NTuple{N, T}) where {N, T} = s.fn(v, s.pow)::NTuple{N, T} function (f::Automorphism{N})(v::NTuple{N, T}) where {N, T} for s in syllables(f) v = s(v)::NTuple{N, T} - # if iszero(i % 3) - # freereduce!.(v) - # end end - # return freereduce!.(v) return v end +function domain(G::AutGroup{N}) where N + F = G.objectGroup + return ntuple(i->F(F.gens[i]), N) +end + evaluate(f::Automorphism) = f(domain(parent(f))) ############################################################################### # -# Comparison +# hashing && equality # -############################################################################### - -const HASHINGCONST = 0x7d28276b01874b19 # hash(Automorphism) - -hash(s::AutSymbol, h::UInt) = hash(s.id, hash(s.pow, hash(AutSymbol, h))) function hash_internal(g::Automorphism, images = freereduce!.(evaluate(g)), - h::UInt = HASHINGCONST) + h::UInt = 0x7d28276b01874b19) # hash(Automorphism) return hash(images, hash(parent(g), h)) end function compute_images(g::Automorphism) images = reduce!.(evaluate(g)) - g.savedhash = hash_internal(g, images) + savehash!(g, hash_internal(g, images)) unsetmodified!(g) return images end @@ -268,37 +255,10 @@ function (==)(g::Automorphism{N}, h::Automorphism{N}) where N return img == imh end -############################################################################### -# -# Basic manipulation -# -############################################################################### - -function change_pow(s::AutSymbol, n::Integer) - if n == zero(n) - return id_autsymbol() - end - symbol = s.fn - if symbol isa FlipAut - return flip(symbol.i, n) - elseif symbol isa PermAut - return AutSymbol(symbol.perm, n) - elseif symbol isa RTransvect - return transvection_R(symbol.i, symbol.j, n) - elseif symbol isa LTransvect - return transvection_L(symbol.i, symbol.j, n) - elseif symbol isa Identity - return s - else - throw(DomainError("Unknown type of AutSymbol: $s")) - end -end - ############################################################################### # # String I/O # -############################################################################### function show(io::IO, G::AutGroup) print(io, "Automorphism Group of $(G.objectGroup)\n") @@ -307,21 +267,8 @@ end ############################################################################### # -# Binary operators +# Reduction # -############################################################################### - -############################################################################### -# -# Inversion -# -############################################################################### - -############################################################################### -# -# Misc -# -############################################################################### getperm(s::AutSymbol) = s.fn.perm^s.pow From 6d22c82ab388de1ca56ded89cb16111da2b83b59 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 15:43:38 +0100 Subject: [PATCH 33/34] create a general/saner homomorphism evaluation architecture --- src/AutGroup.jl | 24 ++++++++++++++---------- src/Groups.jl | 14 ++++++++++++++ test/AutGroup-tests.jl | 42 +++++++++++++++++++++--------------------- 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index 1c9206a..2fd2185 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -297,24 +297,28 @@ function reduce!(w::Automorphism) return W end -function linear_repr(A::Automorphism{N}, hom=matrix_repr) where N - return reduce(*, linear_repr.(A.symbols, N, hom), init=hom(Identity(),N,1)) -end +############################################################################### +# +# Abelianization (natural Representation to GL(N,Z)) +# -linear_repr(a::AutSymbol, n::Int, hom) = hom(a.fn, n, a.pow) +abelianize(A::Automorphism{N}) where N = image(A, abelianize; n=N) -function matrix_repr(a::Union{RTransvect, LTransvect}, n::Int, pow) +# homomorphism definition +abelianize(; n::Integer=1) = Matrix{Int}(I, n, n) +abelianize(a::AutSymbol; n::Int=1) = abelianize(a.fn, n, a.pow) + +function abelianize(a::Union{RTransvect, LTransvect}, n::Int, pow) x = Matrix{Int}(I, n, n) x[a.i,a.j] = pow return x end -function matrix_repr(a::FlipAut, n::Int, pow) +function abelianize(a::FlipAut, n::Int, pow) x = Matrix{Int}(I, n, n) - x[a.i,a.i] = -1^pow + x[a.i,a.i] = -1 return x end -matrix_repr(a::PermAut, n::Int, pow) = Matrix{Int}(I, n, n)[(a.perm^pow).d, :] - -matrix_repr(a::Identity, n::Int, pow) = Matrix{Int}(I, n, n) +abelianize(a::PermAut, n::Integer, pow) = Matrix{Int}(I, n, n)[(a.perm^pow).d, :] +abelianize(a::Identity, n::Integer, pow) = abelianize(;n=n) diff --git a/src/Groups.jl b/src/Groups.jl index 443dd46..9dc67cb 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -13,6 +13,7 @@ import Base: deepcopy_internal using LinearAlgebra using Markdown +export gens, FreeGroup, Aut, SAut include("types.jl") @@ -89,4 +90,17 @@ function generate_balls(S::AbstractVector{T}, center::T=one(first(S)); return c.*B, sizes end +@doc doc""" + image(A::GWord, homomorphism; kwargs...) +Evaluate homomorphism `homomorphism` on a GWord `A`. +`homomorphism` needs implement + > `hom(s; kwargs...)`, +where `hom(;kwargs...)` evaluates the value at the identity element. +""" +function image(w::GWord, hom; kwargs...) + return reduce(*, + (hom(s; kwargs...) for s in syllables(w)), + init = hom(;kwargs...)) +end + end # of module Groups diff --git a/test/AutGroup-tests.jl b/test/AutGroup-tests.jl index 8699874..b4e5a63 100644 --- a/test/AutGroup-tests.jl +++ b/test/AutGroup-tests.jl @@ -9,7 +9,7 @@ @test isa(f, Groups.GSymbol) @test isa(f, Groups.AutSymbol) @test isa(Groups.AutSymbol(perm"(4)"), Groups.AutSymbol) - @test isa(Groups.AutSymbol([2,3,4,1]), Groups.AutSymbol) + @test isa(Groups.AutSymbol(perm"(1,2,3,4)"), Groups.AutSymbol) @test isa(Groups.transvection_R(1,2), Groups.AutSymbol) @test isa(Groups.transvection_R(3,4), Groups.AutSymbol) @test isa(Groups.flip(3), Groups.AutSymbol) @@ -205,48 +205,48 @@ @test length(unique(B_2)) == 1777 end - @testset "linear_repr tests" begin - N = 3 + @testset "abelianization homomorphism" begin + N = 4 G = AutGroup(FreeGroup(N)) S = unique([gens(G); inv.(gens(G))]) R = 3 - @test Groups.linear_repr(one(G)) isa Matrix{Int} - @test Groups.linear_repr(one(G)) == Matrix{Int}(I, N, N) + @test Groups.abelianize(one(G)) isa Matrix{Int} + @test Groups.abelianize(one(G)) == Matrix{Int}(I, N, N) M = Matrix{Int}(I, N, N) M[1,2] = 1 - ϱ₁₂ = G(Groups.transvection_R(1,2)) - λ₁₂ = G(Groups.transvection_R(1,2)) + ϱ₁₂ = G(Groups.ϱ(1,2)) + λ₁₂ = G(Groups.λ(1,2)) - @test Groups.linear_repr(ϱ₁₂) == M - @test Groups.linear_repr(λ₁₂) == M + @test Groups.abelianize(ϱ₁₂) == M + @test Groups.abelianize(λ₁₂) == M M[1,2] = -1 - @test Groups.linear_repr(ϱ₁₂^-1) == M - @test Groups.linear_repr(λ₁₂^-1) == M + @test Groups.abelianize(ϱ₁₂^-1) == M + @test Groups.abelianize(λ₁₂^-1) == M - @test Groups.linear_repr(ϱ₁₂*λ₁₂^-1) == Matrix{Int}(I, N, N) - @test Groups.linear_repr(λ₁₂^-1*ϱ₁₂) == Matrix{Int}(I, N, N) + @test Groups.abelianize(ϱ₁₂*λ₁₂^-1) == Matrix{Int}(I, N, N) + @test Groups.abelianize(λ₁₂^-1*ϱ₁₂) == Matrix{Int}(I, N, N) M = Matrix{Int}(I, N, N) M[2,2] = -1 ε₂ = G(Groups.flip(2)) - @test Groups.linear_repr(ε₂) == M - @test Groups.linear_repr(ε₂^2) == Matrix{Int}(I, N, N) + @test Groups.abelianize(ε₂) == M + @test Groups.abelianize(ε₂^2) == Matrix{Int}(I, N, N) - M = [0 1 0; 0 0 1; 1 0 0] + M = [0 1 0 0; 0 0 0 1; 0 0 1 0; 1 0 0 0] - σ = G(Groups.AutSymbol(perm"(1,2,3)")) - @test Groups.linear_repr(σ) == M - @test Groups.linear_repr(σ^3) == Matrix{Int}(I, 3, 3) - @test Groups.linear_repr(σ)^3 == Matrix{Int}(I, 3, 3) + σ = G(Groups.AutSymbol(perm"(1,2,4)")) + @test Groups.abelianize(σ) == M + @test Groups.abelianize(σ^3) == Matrix{Int}(I, N, N) + @test Groups.abelianize(σ)^3 == Matrix{Int}(I, N, N) function test_homomorphism(S, r) for elts in Iterators.product([[g for g in S] for _ in 1:r]...) - prod(Groups.linear_repr.(elts)) == Groups.linear_repr(prod(elts)) || error("linear representaton test failed at $elts") + prod(Groups.abelianize.(elts)) == Groups.abelianize(prod(elts)) || error("linear representaton test failed at $elts") end return 0 end From 7bc26ece79caf6eb3cb97ea9600189ccca1971a4 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 25 Mar 2020 16:25:21 +0100 Subject: [PATCH 34/34] fix bug in reduce!(::Automorphism) --- src/AutGroup.jl | 2 +- test/AutGroup-tests.jl | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/AutGroup.jl b/src/AutGroup.jl index 2fd2185..232d98d 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -294,7 +294,7 @@ function reduce!(w::Automorphism) while !reduced reduced = simplifyperms!(Bool, w) && freereduce!(Bool, w) end - return W + return w end ############################################################################### diff --git a/test/AutGroup-tests.jl b/test/AutGroup-tests.jl index b4e5a63..7b29949 100644 --- a/test/AutGroup-tests.jl +++ b/test/AutGroup-tests.jl @@ -182,6 +182,15 @@ g_im = g(Groups.domain(A)) @test length.(g_im) == (1,1,1,1) + + g = A(Groups.σ(perm"(1,2)(4)")) + h = A(Groups.σ(perm"(2,3,4)")) + @test g*h isa Groups.Automorphism{4} + f = g*h + Groups + @test Groups.syllablelength(f) == 2 + @test Groups.reduce!(f) isa Groups.Automorphism{4} + @test Groups.syllablelength(f) == 1 end @testset "specific Aut(F4) tests" begin