mirror of
https://github.com/kalmarek/Groups.jl.git
synced 2024-12-25 18:15:29 +01:00
commit
c3eb34eee8
@ -1,18 +1,18 @@
|
||||
name = "Groups"
|
||||
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
|
||||
authors = ["Marek Kaluba <kalmar@amu.edu.pl>"]
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
|
||||
[deps]
|
||||
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"
|
||||
|
410
src/AutGroup.jl
410
src/AutGroup.jl
@ -1,8 +1,9 @@
|
||||
export Automorphism, AutGroup, Aut, SAut
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# AutSymbol/ AutGroup / Automorphism
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
struct RTransvect
|
||||
i::Int8
|
||||
@ -25,79 +26,11 @@ 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}
|
||||
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(::AutGroup{N}) where N = Automorphism{N}
|
||||
|
||||
parent_type(::Automorphism{N}) where N = AutGroup{N}
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# AutSymbol defining functions
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
function (ϱ::RTransvect)(v, pow::Integer=1)
|
||||
@inbounds Groups.r_multiply!(v[ϱ.i], (v[ϱ.j]^pow).symbols, reduced=false)
|
||||
return v
|
||||
end
|
||||
|
||||
function (λ::LTransvect)(v, pow::Integer=1)
|
||||
@inbounds Groups.l_multiply!(v[λ.i], (v[λ.j]^pow).symbols, reduced=false)
|
||||
return v
|
||||
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
|
||||
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)
|
||||
@ -111,53 +44,82 @@ 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))
|
||||
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)
|
||||
|
||||
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[]
|
||||
@ -166,17 +128,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 = [ϱ(i,j) for (i,j) in indexing]
|
||||
lmuls = [λ(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 = [ε(i) for i in 1:n]
|
||||
syms = [σ(p) for p in SymmetricGroup(Int8(n))][2:end]
|
||||
|
||||
append!(S, [flips; syms])
|
||||
|
||||
end
|
||||
return AutGroup{n}(G, S)
|
||||
end
|
||||
@ -184,134 +145,120 @@ 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 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
|
||||
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
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
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
|
||||
end
|
||||
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(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 = 0x7d28276b01874b19) # hash(Automorphism)
|
||||
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))
|
||||
savehash!(g, hash_internal(g, images))
|
||||
unsetmodified!(g)
|
||||
return images
|
||||
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
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# 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_autsymbol(symbol.i, pow=n)
|
||||
elseif symbol isa PermAut
|
||||
return perm_autsymbol(symbol.perm, pow=n)
|
||||
elseif symbol isa RTransvect
|
||||
return rmul_autsymbol(symbol.i, symbol.j, pow=n)
|
||||
elseif symbol isa LTransvect
|
||||
return lmul_autsymbol(symbol.i, symbol.j, pow=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)
|
||||
end
|
||||
end
|
||||
|
||||
length(s::AutSymbol) = abs(s.pow)
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# String I/O
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
function show(io::IO, G::AutGroup)
|
||||
print(io, "Automorphism Group of $(G.objectGroup)\n")
|
||||
@ -320,87 +267,58 @@ end
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Binary operators
|
||||
# Reduction
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Inversion
|
||||
#
|
||||
###############################################################################
|
||||
getperm(s::AutSymbol) = s.fn.perm^s.pow
|
||||
|
||||
inv(f::AutSymbol) = change_pow(f, -f.pow)
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Misc
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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)
|
||||
|
181
src/FPGroups.jl
181
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
|
||||
@ -28,131 +28,70 @@ export FPGroupElem, FPGroup
|
||||
#
|
||||
# Type and parent object methods
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
parent_type(::Type{FPGroupElem}) = FPGroup
|
||||
|
||||
elem_type(::FPGroup) = FPGroupElem
|
||||
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(gens::Vector{FPSymbol}) = FPGroup(gens, Dict{FPGroupElem, FPGroupElem}())
|
||||
|
||||
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 Base.one(G::FPGroup)
|
||||
id = FPGroupElem(FPSymbol[])
|
||||
id.parent = G
|
||||
return id
|
||||
end
|
||||
|
||||
function (G::FPGroup)(w::GWord)
|
||||
if length(w) == 0
|
||||
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::FPSymbol) = G(FPGroupElem(s))
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Basic manipulation
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
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)
|
||||
(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
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
inv(s::FPSymbol) = change_pow(s, -s.pow)
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Binary operations
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
(*)(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)
|
||||
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
|
||||
|
||||
@ -162,31 +101,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
|
||||
|
@ -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,86 +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 Base.one(G::FreeGroup)
|
||||
id = FreeGroupElem(FreeSymbol[])
|
||||
id.parent = G
|
||||
return id
|
||||
end
|
||||
|
||||
function (G::FreeGroup)(w::GroupWord{FreeSymbol})
|
||||
if length(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
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
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)
|
||||
(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
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
inv(s::FreeSymbol) = change_pow(s, -s.pow)
|
||||
|
455
src/Groups.jl
455
src/Groups.jl
@ -7,463 +7,100 @@ 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
|
||||
|
||||
Base.one(G::Generic.PermGroup) = G(collect(1:G.n), false)
|
||||
export gens, FreeGroup, Aut, SAut
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# ParentType / ObjectType 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
|
||||
|
||||
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}
|
||||
savedhash::UInt
|
||||
modified::Bool
|
||||
parent::Group
|
||||
|
||||
function GroupWord{T}(symbols::Vector{T}) where {T}
|
||||
return new{T}(symbols, hash(symbols), true)
|
||||
end
|
||||
end
|
||||
|
||||
abstract type AbstractFPGroup <: Group end
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Includes
|
||||
#
|
||||
###############################################################################
|
||||
include("types.jl")
|
||||
|
||||
include("FreeGroup.jl")
|
||||
include("FPGroups.jl")
|
||||
include("AutGroup.jl")
|
||||
|
||||
include("symbols.jl")
|
||||
include("fallbacks.jl")
|
||||
include("words.jl")
|
||||
include("hashing.jl")
|
||||
include("freereduce.jl")
|
||||
include("arithmetic.jl")
|
||||
include("findreplace.jl")
|
||||
|
||||
include("DirectPower.jl")
|
||||
include("WreathProducts.jl")
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Type and parent object methods
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
parent(w::GWord{T}) where {T<:GSymbol} = w.parent
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# ParentType / ObjectType constructors
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
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
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
function hash(W::GWord, h::UInt)
|
||||
W.modified && reduce!(W)
|
||||
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(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)
|
||||
end
|
||||
|
||||
function freereduce!(W::GWord)
|
||||
reduced = true
|
||||
for i in 1:length(W.symbols) - 1
|
||||
if W.symbols[i].pow == 0
|
||||
continue
|
||||
elseif W.symbols[i].id == W.symbols[i+1].id
|
||||
reduced = false
|
||||
p1 = W.symbols[i].pow
|
||||
p2 = W.symbols[i+1].pow
|
||||
|
||||
W.symbols[i+1] = change_pow(W.symbols[i], p1 + p2)
|
||||
W.symbols[i] = change_pow(W.symbols[i], 0)
|
||||
end
|
||||
end
|
||||
deleteids!(W)
|
||||
return reduced
|
||||
end
|
||||
|
||||
function reduce!(W::GWord)
|
||||
if length(W) < 2
|
||||
deleteids!(W)
|
||||
else
|
||||
reduced = false
|
||||
while !reduced
|
||||
reduced = freereduce!(W)
|
||||
end
|
||||
end
|
||||
|
||||
W.savedhash = hash(W.symbols, hash(typeof(W), hash(parent(W), zero(UInt))))
|
||||
W.modified = false
|
||||
|
||||
return W
|
||||
end
|
||||
|
||||
@doc doc"""
|
||||
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`.
|
||||
> More specific procedures should be dispatched on `GWord`s type parameter.
|
||||
|
||||
"""
|
||||
reduce(W::GWord) = reduce!(deepcopy(W))
|
||||
|
||||
@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
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Comparison
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
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
|
||||
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
|
||||
end
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Binary operators
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
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
|
||||
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
|
||||
end
|
||||
|
||||
function l_multiply!(W::GWord, x; reduced::Bool=true)
|
||||
if length(x) > 0
|
||||
prepend!(W.symbols, x)
|
||||
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])
|
||||
(*)(s::GSymbol, W::GWord) = l_multiply(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
|
||||
r_multiply!(W, W.symbols)
|
||||
end
|
||||
Z = deepcopy(W)
|
||||
while p > 0
|
||||
t = trailing_zeros(p) + 1
|
||||
p >>= t
|
||||
while (t -= 1) >= 0
|
||||
r_multiply!(W, W.symbols)
|
||||
end
|
||||
r_multiply!(Z, W.symbols)
|
||||
end
|
||||
return 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
|
||||
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(reverse([inv(s) for s in W.symbols]))
|
||||
w.modified = true
|
||||
return G(w)
|
||||
print(io, "$(s.id)^$(s.pow)")
|
||||
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)
|
||||
|
||||
"""doc
|
||||
Find the first linear index k>=i such that Z < W.symbols[k:k+length(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
|
||||
end
|
||||
|
||||
findfirst(W::GWord, Z::GWord) = findnext(W, Z, 1)
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
function replace(W::GWord, index, toreplace::GWord, replacement::GWord)
|
||||
replace!(deepcopy(W), index, toreplace, replacement)
|
||||
end
|
||||
|
||||
function replace_all!(W::T,subst_dict::Dict{T,T}) where {T<:GWord}
|
||||
modified = false
|
||||
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)
|
||||
end
|
||||
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
|
||||
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
|
||||
@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
|
||||
|
||||
########### 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
|
||||
|
@ -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"))
|
||||
|
93
src/arithmetic.jl
Normal file
93
src/arithmetic.jl
Normal file
@ -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)
|
16
src/fallbacks.jl
Normal file
16
src/fallbacks.jl
Normal file
@ -0,0 +1,16 @@
|
||||
# workarounds
|
||||
Base.one(G::Generic.SymmetricGroup) = Generic.Perm(G.n)
|
||||
|
||||
# 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
|
182
src/findreplace.jl
Normal file
182
src/findreplace.jl
Normal file
@ -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
|
43
src/freereduce.jl
Normal file
43
src/freereduce.jl
Normal file
@ -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))
|
34
src/hashing.jl
Normal file
34
src/hashing.jl
Normal file
@ -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
|
21
src/symbols.jl
Normal file
21
src/symbols.jl
Normal file
@ -0,0 +1,21 @@
|
||||
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
|
||||
|
||||
Base.convert(::Type{GS}, s::GSymbol) where GS<:GSymbol = GS(s.id, s.pow)
|
||||
Base.convert(::Type{GS}, s::GS) where GS<:GSymbol = s
|
43
src/types.jl
Normal file
43
src/types.jl
Normal file
@ -0,0 +1,43 @@
|
||||
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
|
||||
|
||||
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::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
|
41
src/words.jl
Normal file
41
src/words.jl
Normal file
@ -0,0 +1,41 @@
|
||||
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
|
||||
|
||||
Base.convert(::Type{GW}, s::GSymbol) where GW <: GWord = GW(s)
|
@ -1,6 +1,6 @@
|
||||
@testset "Automorphisms" begin
|
||||
|
||||
G = PermutationGroup(Int8(4))
|
||||
G = SymmetricGroup(Int8(4))
|
||||
|
||||
@testset "AutSymbol" begin
|
||||
@test_throws MethodError Groups.AutSymbol(:a)
|
||||
@ -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(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)
|
||||
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,31 @@
|
||||
# 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)
|
||||
|
||||
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
|
||||
@ -208,48 +214,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.rmul_autsymbol(1,2))
|
||||
λ₁₂ = G(Groups.rmul_autsymbol(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_autsymbol(2))
|
||||
ε₂ = 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.perm_autsymbol([2,3,1]))
|
||||
@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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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}}}
|
||||
|
Loading…
Reference in New Issue
Block a user