mirror of
https://github.com/kalmarek/Groups.jl.git
synced 2024-12-26 02:20:30 +01:00
commit
c3eb34eee8
@ -1,18 +1,18 @@
|
|||||||
name = "Groups"
|
name = "Groups"
|
||||||
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
|
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
|
||||||
authors = ["Marek Kaluba <kalmar@amu.edu.pl>"]
|
authors = ["Marek Kaluba <kalmar@amu.edu.pl>"]
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d"
|
AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d"
|
||||||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||||
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
|
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
|
||||||
|
|
||||||
|
[compat]
|
||||||
|
AbstractAlgebra = "^0.9.0"
|
||||||
|
|
||||||
[extras]
|
[extras]
|
||||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||||
|
|
||||||
[targets]
|
[targets]
|
||||||
test = ["Test"]
|
test = ["Test"]
|
||||||
|
|
||||||
[compat]
|
|
||||||
AbstractAlgebra = "^0.7.0"
|
|
||||||
|
440
src/AutGroup.jl
440
src/AutGroup.jl
@ -1,8 +1,9 @@
|
|||||||
|
export Automorphism, AutGroup, Aut, SAut
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# AutSymbol/ AutGroup / Automorphism
|
# AutSymbol/ AutGroup / Automorphism
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
struct RTransvect
|
struct RTransvect
|
||||||
i::Int8
|
i::Int8
|
||||||
@ -30,6 +31,77 @@ struct AutSymbol <: GSymbol
|
|||||||
fn::Union{LTransvect, RTransvect, PermAut, FlipAut, Identity}
|
fn::Union{LTransvect, RTransvect, PermAut, FlipAut, Identity}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# taken from ValidatedNumerics, under under the MIT "Expat" License:
|
||||||
|
# https://github.com/JuliaIntervals/ValidatedNumerics.jl/blob/master/LICENSE.md
|
||||||
|
function subscriptify(n::Integer)
|
||||||
|
subscript_0 = Int(0x2080) # Char(0x2080) -> subscript 0
|
||||||
|
@assert 0 <= n <= 9
|
||||||
|
return Char(subscript_0 + n)
|
||||||
|
# return [Char(subscript_0 + i) for i in reverse(digits(n))])
|
||||||
|
end
|
||||||
|
|
||||||
|
function id_autsymbol()
|
||||||
|
return AutSymbol(Symbol("(id)"), 0, Identity())
|
||||||
|
end
|
||||||
|
|
||||||
|
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 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(i::Integer, pow::Integer=1)
|
||||||
|
iseven(pow) && return id_autsymbol()
|
||||||
|
id = Symbol("ɛ", subscriptify(i))
|
||||||
|
return AutSymbol(id, 1, FlipAut(i))
|
||||||
|
end
|
||||||
|
|
||||||
|
function AutSymbol(p::Generic.Perm, pow::Integer=1)
|
||||||
|
if pow != 1
|
||||||
|
p = p^pow
|
||||||
|
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
|
||||||
|
|
||||||
|
ϱ(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 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
|
||||||
|
#
|
||||||
|
|
||||||
mutable struct AutGroup{N} <: AbstractFPGroup
|
mutable struct AutGroup{N} <: AbstractFPGroup
|
||||||
objectGroup::FreeGroup
|
objectGroup::FreeGroup
|
||||||
gens::Vector{AutSymbol}
|
gens::Vector{AutSymbol}
|
||||||
@ -46,46 +118,67 @@ mutable struct Automorphism{N} <: GWord{AutSymbol}
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
export Automorphism, AutGroup, Aut, SAut
|
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[]
|
||||||
|
n = length(gens(G))
|
||||||
|
n == 0 && return AutGroup{n}(G, S)
|
||||||
|
|
||||||
|
indexing = [[i,j] for i in 1:n for j in 1:n if i≠j]
|
||||||
|
|
||||||
|
rmuls = [ϱ(i,j) for (i,j) in indexing]
|
||||||
|
lmuls = [λ(i,j) for (i,j) in indexing]
|
||||||
|
|
||||||
|
append!(S, [rmuls; lmuls])
|
||||||
|
|
||||||
|
if !special
|
||||||
|
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
|
||||||
|
|
||||||
|
Aut(G::Group) = AutGroup(G)
|
||||||
|
SAut(G::Group) = AutGroup(G, special=true)
|
||||||
|
|
||||||
|
Automorphism{N}(s::AutSymbol) where N = Automorphism{N}(AutSymbol[s])
|
||||||
|
|
||||||
|
function (G::AutGroup{N})(f::AutSymbol) where N
|
||||||
|
g = Automorphism{N}([f])
|
||||||
|
setparent!(g, G)
|
||||||
|
return g
|
||||||
|
end
|
||||||
|
|
||||||
|
(G::AutGroup{N})(g::Automorphism{N}) where N = (setparent!(g, G); g)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# Type and parent object methods
|
# AutSymbol defining functions && evaluation
|
||||||
|
# NOTE: all automorphisms operate on a tuple of FreeWords INPLACE!
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
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)
|
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
|
return v
|
||||||
end
|
end
|
||||||
|
|
||||||
function (λ::LTransvect)(v, pow::Integer=1)
|
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
|
return v
|
||||||
end
|
end
|
||||||
|
|
||||||
function (σ::PermAut)(v, pow::Integer=1)
|
function (σ::PermAut)(v, pow::Integer=1)
|
||||||
w = deepcopy(v)
|
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
|
s = (σ.perm^pow).d
|
||||||
@inbounds for k in eachindex(v)
|
@inbounds for k in eachindex(v)
|
||||||
v[k].symbols = w[s[k]].symbols
|
v[k].symbols = w[s[k]].symbols
|
||||||
end
|
end
|
||||||
end
|
|
||||||
return v
|
return v
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -98,220 +191,74 @@ end
|
|||||||
|
|
||||||
(::Identity)(v, pow::Integer=1) = v
|
(::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)
|
|
||||||
subscript_0 = Int(0x2080) # Char(0x2080) -> subscript 0
|
|
||||||
@assert 0 <= n <= 9
|
|
||||||
return Char(subscript_0 + n)
|
|
||||||
# return [Char(subscript_0 + i) for i in reverse(digits(n))])
|
|
||||||
end
|
|
||||||
|
|
||||||
function id_autsymbol()
|
|
||||||
return AutSymbol(Symbol("(id)"), 0, Identity())
|
|
||||||
end
|
|
||||||
|
|
||||||
function rmul_autsymbol(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)
|
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
function perm_autsymbol(p::Generic.Perm{I}; pow::Integer=one(I)) where I<:Integer
|
|
||||||
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
|
|
||||||
end
|
|
||||||
return id_autsymbol()
|
|
||||||
end
|
|
||||||
|
|
||||||
function perm_autsymbol(a::Vector{<:Integer})
|
|
||||||
return perm_autsymbol(Generic.Perm(Vector{Int8}(a), false))
|
|
||||||
end
|
|
||||||
|
|
||||||
function domain(G::AutGroup{N}) where N
|
|
||||||
F = G.objectGroup
|
|
||||||
gg = gens(F)
|
|
||||||
return ntuple(i->gg[i], N)
|
|
||||||
end
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
#
|
|
||||||
# AutGroup / Automorphism constructors
|
|
||||||
#
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
function AutGroup(G::FreeGroup; special=false)
|
|
||||||
S = AutSymbol[]
|
|
||||||
n = length(gens(G))
|
|
||||||
n == 0 && return AutGroup{n}(G, S)
|
|
||||||
|
|
||||||
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]
|
|
||||||
|
|
||||||
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]
|
|
||||||
|
|
||||||
append!(S, [flips; syms])
|
|
||||||
|
|
||||||
end
|
|
||||||
return AutGroup{n}(G, S)
|
|
||||||
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
|
|
||||||
return g
|
|
||||||
end
|
|
||||||
|
|
||||||
function (G::AutGroup{N})(g::Automorphism{N}) where N
|
|
||||||
g.parent = G
|
|
||||||
return g
|
|
||||||
end
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# Functional call overloads for evaluation of AutSymbol and Automorphism
|
# Functional call overloads for evaluation of AutSymbol and Automorphism
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
function (s::AutSymbol)(v::NTuple{N, T}) where {N, T}
|
(s::AutSymbol)(v::NTuple{N, T}) where {N, T} = s.fn(v, s.pow)::NTuple{N, T}
|
||||||
if s.pow != 0
|
|
||||||
v = 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}
|
||||||
end
|
end
|
||||||
return v
|
return v
|
||||||
end
|
end
|
||||||
|
|
||||||
function (f::Automorphism{N})(v::NTuple{N, T}) where {N, T}
|
function domain(G::AutGroup{N}) where N
|
||||||
for (i, s) in enumerate(f.symbols)
|
F = G.objectGroup
|
||||||
v = s(v)::NTuple{N, T}
|
return ntuple(i->F(F.gens[i]), N)
|
||||||
if i % 5 == 0
|
|
||||||
freereduce!.(v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return v
|
|
||||||
end
|
end
|
||||||
|
|
||||||
evaluate(f::Automorphism) = f(domain(parent(f)))
|
evaluate(f::Automorphism) = f(domain(parent(f)))
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# Comparison
|
# hashing && equality
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
const HASHINGCONST = 0x7d28276b01874b19 # hash(Automorphism)
|
function hash_internal(g::Automorphism, images = freereduce!.(evaluate(g)),
|
||||||
|
h::UInt = 0x7d28276b01874b19) # hash(Automorphism)
|
||||||
hash(s::AutSymbol, h::UInt) = hash(s.id, hash(s.pow, hash(:AutSymbol, h)))
|
return hash(images, hash(parent(g), h))
|
||||||
|
|
||||||
function hash(g::Automorphism{N}, images, h::UInt=HASHINGCONST) where N
|
|
||||||
return hash(images, hash(parent(g), hash(Automorphism{N}, h)))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function hash(g::Automorphism, h::UInt)
|
function compute_images(g::Automorphism)
|
||||||
if g.modified
|
images = reduce!.(evaluate(g))
|
||||||
g_im = reduce!.(evaluate(g))
|
savehash!(g, hash_internal(g, images))
|
||||||
g.savedhash = hash(g, g_im)
|
unsetmodified!(g)
|
||||||
g.modified = false
|
return images
|
||||||
end
|
|
||||||
return xor(g.savedhash, h)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function (==)(g::Automorphism{N}, h::Automorphism{N}) where N
|
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 ismodified(g)
|
||||||
if g.savedhash != h.savedhash
|
img = compute_images(g)
|
||||||
return false
|
img_c = true
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# expensive:
|
if ismodified(h)
|
||||||
g_im = reduce!.(evaluate(g))
|
imh = compute_images(h)
|
||||||
h_im = reduce!.(evaluate(h))
|
imh_c = true
|
||||||
# cheap:
|
end
|
||||||
g.savedhash = hash(g, g_im)
|
|
||||||
g.modified = false
|
|
||||||
h.savedhash = hash(h, h_im)
|
|
||||||
h.modified = false
|
|
||||||
|
|
||||||
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
|
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
|
# String I/O
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
function show(io::IO, G::AutGroup)
|
function show(io::IO, G::AutGroup)
|
||||||
print(io, "Automorphism Group of $(G.objectGroup)\n")
|
print(io, "Automorphism Group of $(G.objectGroup)\n")
|
||||||
@ -320,87 +267,58 @@ end
|
|||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# Binary operators
|
# Reduction
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
###############################################################################
|
getperm(s::AutSymbol) = s.fn.perm^s.pow
|
||||||
#
|
|
||||||
# Inversion
|
|
||||||
#
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
inv(f::AutSymbol) = change_pow(f, -f.pow)
|
function simplifyperms!(::Type{Bool}, w::Automorphism{N}) where N
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
reduced = true
|
reduced = true
|
||||||
to_delete = Int[]
|
for i in 1:syllablelength(w)-1
|
||||||
for i in 1:length(W.symbols)-1
|
s, ns = syllables(w)[i], syllables(w)[i+1]
|
||||||
if W.symbols[i].pow == 0
|
if isone(s)
|
||||||
continue
|
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
|
reduced = false
|
||||||
c = W.symbols[i]
|
setmodified!(w)
|
||||||
n = W.symbols[i+1]
|
syllables(w)[i+1] = AutSymbol(getperm(s)*getperm(ns))
|
||||||
W.symbols[i+1] = perm_autsymbol(getperm(c)*getperm(n))
|
syllables(w)[i] = change_pow(s, 0)
|
||||||
push!(to_delete, i)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
deleteat!(W.symbols, to_delete)
|
filter!(!isone, syllables(w))
|
||||||
deleteids!(W)
|
|
||||||
return reduced
|
return reduced
|
||||||
end
|
end
|
||||||
|
|
||||||
function reduce!(W::Automorphism)
|
function reduce!(w::Automorphism)
|
||||||
if length(W) == 0
|
|
||||||
return W
|
|
||||||
elseif length(W.symbols) == 1
|
|
||||||
deleteids!(W)
|
|
||||||
else
|
|
||||||
reduced = false
|
reduced = false
|
||||||
while !reduced
|
while !reduced
|
||||||
reduced = simplifyperms!(W) && freereduce!(W)
|
reduced = simplifyperms!(Bool, w) && freereduce!(Bool, w)
|
||||||
end
|
end
|
||||||
end
|
return w
|
||||||
|
|
||||||
W.modified = true
|
|
||||||
|
|
||||||
return W
|
|
||||||
end
|
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 = Matrix{Int}(I, n, n)
|
||||||
x[a.i,a.j] = pow
|
x[a.i,a.j] = pow
|
||||||
return x
|
return x
|
||||||
end
|
end
|
||||||
|
|
||||||
function matrix_repr(a::FlipAut, n::Int, pow)
|
function abelianize(a::FlipAut, n::Int, pow)
|
||||||
x = Matrix{Int}(I, n, n)
|
x = Matrix{Int}(I, n, n)
|
||||||
x[a.i,a.i] = -1^pow
|
x[a.i,a.i] = -1
|
||||||
return x
|
return x
|
||||||
end
|
end
|
||||||
|
|
||||||
matrix_repr(a::PermAut, n::Int, pow) = Matrix{Int}(I, n, n)[(a.perm^pow).d, :]
|
abelianize(a::PermAut, n::Integer, pow) = Matrix{Int}(I, n, n)[(a.perm^pow).d, :]
|
||||||
|
abelianize(a::Identity, n::Integer, pow) = abelianize(;n=n)
|
||||||
matrix_repr(a::Identity, n::Int, pow) = Matrix{Int}(I, n, n)
|
|
||||||
|
125
src/FPGroups.jl
125
src/FPGroups.jl
@ -13,9 +13,9 @@ FPGroupElem = GroupWord{FPSymbol}
|
|||||||
|
|
||||||
mutable struct FPGroup <: AbstractFPGroup
|
mutable struct FPGroup <: AbstractFPGroup
|
||||||
gens::Vector{FPSymbol}
|
gens::Vector{FPSymbol}
|
||||||
rels::Dict{FPGroupElem, FPGroupElem}
|
rels::Dict{FreeGroupElem, FreeGroupElem}
|
||||||
|
|
||||||
function FPGroup(gens::Vector{T}, rels::Dict{FPGroupElem, FPGroupElem}) where {T<:GSymbol}
|
function FPGroup(gens::Vector{T}, rels::Dict{FreeGroupElem, FreeGroupElem}) where {T<:GSymbol}
|
||||||
G = new(gens)
|
G = new(gens)
|
||||||
G.rels = Dict(G(k) => G(v) for (k,v) in rels)
|
G.rels = Dict(G(k) => G(v) for (k,v) in rels)
|
||||||
return G
|
return G
|
||||||
@ -28,131 +28,70 @@ export FPGroupElem, FPGroup
|
|||||||
#
|
#
|
||||||
# Type and parent object methods
|
# Type and parent object methods
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
parent_type(::Type{FPGroupElem}) = FPGroup
|
AbstractAlgebra.elem_type(::Type{FPGroup}) = FPGroupElem
|
||||||
|
AbstractAlgebra.parent_type(::Type{FPGroupElem}) = FPGroup
|
||||||
elem_type(::FPGroup) = FPGroupElem
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# FPSymbol constructors
|
# FPSymbol constructors
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
FPSymbol(s::Symbol) = FPSymbol(s, 1)
|
FPSymbol(s::Symbol) = FPSymbol(s, 1)
|
||||||
FPSymbol(s::String) = FPSymbol(Symbol(s))
|
FPSymbol(s::String) = FPSymbol(Symbol(s))
|
||||||
FPSymbol(s::GSymbol) = FPSymbol(s.id, s.pow)
|
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])
|
FPGroup(H::FreeGroup) = FPGroup([FPSymbol(s) for s in H.gens])
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# Parent object call overloads
|
# Parent object call overloads
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
function Base.one(G::FPGroup)
|
|
||||||
id = FPGroupElem(FPSymbol[])
|
|
||||||
id.parent = G
|
|
||||||
return id
|
|
||||||
end
|
|
||||||
|
|
||||||
function (G::FPGroup)(w::GWord)
|
function (G::FPGroup)(w::GWord)
|
||||||
if length(w) == 0
|
if isempty(w)
|
||||||
return one(G)
|
return one(G)
|
||||||
end
|
end
|
||||||
|
|
||||||
if eltype(w.symbols) == FreeSymbol
|
@boundscheck for s in syllables(w)
|
||||||
w = FPGroupElem(FPSymbol.(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
|
||||||
|
|
||||||
if eltype(w.symbols) == FPSymbol
|
w = FPGroupElem(FPSymbol.(syllables(w)))
|
||||||
for s in w.symbols
|
setparent!(w, G)
|
||||||
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)
|
return reduce!(w)
|
||||||
end
|
end
|
||||||
|
|
||||||
(G::FPGroup)(s::FPSymbol) = G(FPGroupElem(s))
|
(G::FPGroup)(s::GSymbol) = 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)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# String I/O
|
# String I/O
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
function show(io::IO, G::FPGroup)
|
function show(io::IO, G::FPGroup)
|
||||||
print(io, "FPgroup on $(length(G.gens)) generators ")
|
print(io, "FPgroup on $(length(G.gens)) generators ")
|
||||||
strrels = join(G.rels, ", ")
|
strrels = join(G.rels, ", ")
|
||||||
if length(strrels) > 300
|
if length(strrels) > 200
|
||||||
print(io, "⟨ ", join(G.gens, ", "), " | $(length(G.rels)) relation(s) ⟩.")
|
print(io, "⟨ ", join(G.gens, ", "), " | $(length(G.rels)) relation(s) ⟩.")
|
||||||
else
|
else
|
||||||
print(io, "⟨ ", join(G.gens, ", "), " | ", join(G.rels, ", "), " ⟩.")
|
print(io, "⟨ ", join(G.gens, ", "), " | ", join(G.rels, ", "), " ⟩.")
|
||||||
end
|
end
|
||||||
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)
|
function reduce!(W::FPGroupElem)
|
||||||
if length(W) < 2
|
|
||||||
deleteat!(W.symbols, findall(x -> x.pow == 0, W.symbols))
|
|
||||||
else
|
|
||||||
reduced = false
|
reduced = false
|
||||||
while !reduced
|
while !reduced
|
||||||
reduced = freereduce!(W) || replace_all!(W, parent(W).rels)
|
W = replace(W, parent(W).rels)
|
||||||
|
reduced = freereduce!(Bool, W)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
W.savedhash = hash(W.symbols, hash(typeof(W)))
|
|
||||||
W.modified = false
|
|
||||||
return W
|
return W
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -162,12 +101,15 @@ end
|
|||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
function add_rels!(G::FPGroup, newrels::Dict{FPGroupElem,FPGroupElem})
|
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)
|
for w in keys(newrels)
|
||||||
if !(w in keys(G.rels))
|
haskey(G.rels, w) && continue
|
||||||
G.rels[w] = G(newrels[w])
|
G.rels[w] = newrels[w]
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
return G
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.:/(G::FPGroup, newrels::Vector{FPGroupElem})
|
function Base.:/(G::FPGroup, newrels::Vector{FPGroupElem})
|
||||||
@ -176,17 +118,18 @@ function Base.:/(G::FPGroup, newrels::Vector{FPGroupElem})
|
|||||||
"Can not form quotient group: $r is not an element of $G"))
|
"Can not form quotient group: $r is not an element of $G"))
|
||||||
end
|
end
|
||||||
H = deepcopy(G)
|
H = deepcopy(G)
|
||||||
newrels = Dict(H(r) => one(H) for r in newrels)
|
F = freepreimage(H)
|
||||||
|
newrels = Dict(freepreimage(r) => one(F) for r in newrels)
|
||||||
add_rels!(H, newrels)
|
add_rels!(H, newrels)
|
||||||
return H
|
return H
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.:/(G::FreeGroup, rels::Vector{FreeGroupElem})
|
function Base.:/(F::FreeGroup, rels::Vector{FreeGroupElem})
|
||||||
for r in rels
|
for r in rels
|
||||||
parent(r) == G || throw(DomainError(
|
parent(r) == F || throw(DomainError(
|
||||||
"Can not form quotient group: $r is not an element of $G"))
|
"Can not form quotient group: $r is not an element of $F"))
|
||||||
end
|
end
|
||||||
H = FPGroup(deepcopy(G))
|
G = FPGroup(FPSymbol.(F.gens))
|
||||||
H.rels = Dict(H(rel) => one(H) for rel in unique(rels))
|
G.rels = Dict(rel => one(F) for rel in unique(rels))
|
||||||
return H
|
return G
|
||||||
end
|
end
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#
|
#
|
||||||
# FreeSymbol/FreeGroupElem/FreeGroup definition
|
# FreeSymbol/FreeGroupElem/FreeGroup definition
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
struct FreeSymbol <: GSymbol
|
struct FreeSymbol <: GSymbol
|
||||||
id::Symbol
|
id::Symbol
|
||||||
@ -14,7 +13,7 @@ FreeGroupElem = GroupWord{FreeSymbol}
|
|||||||
mutable struct FreeGroup <: AbstractFPGroup
|
mutable struct FreeGroup <: AbstractFPGroup
|
||||||
gens::Vector{FreeSymbol}
|
gens::Vector{FreeSymbol}
|
||||||
|
|
||||||
function FreeGroup(gens::Vector{T}) where {T<:GSymbol}
|
function FreeGroup(gens::AbstractVector{T}) where {T<:GSymbol}
|
||||||
G = new(gens)
|
G = new(gens)
|
||||||
G.gens = gens
|
G.gens = gens
|
||||||
return G
|
return G
|
||||||
@ -27,86 +26,48 @@ export FreeGroupElem, FreeGroup
|
|||||||
#
|
#
|
||||||
# Type and parent object methods
|
# Type and parent object methods
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
elem_type(::Type{FreeGroup}) = FreeGroupElem
|
AbstractAlgebra.elem_type(::Type{FreeGroup}) = FreeGroupElem
|
||||||
|
AbstractAlgebra.parent_type(::Type{FreeGroupElem}) = FreeGroup
|
||||||
parent_type(::Type{FreeGroupElem}) = FreeGroup
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# FreeSymbol constructors
|
# FreeSymbol constructors
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
FreeSymbol(s::Symbol) = FreeSymbol(s,1)
|
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(n::Int, symbol::String="f") = FreeGroup([Symbol(symbol,i) for i in 1:n])
|
||||||
|
FreeGroup(a::AbstractVector) = FreeGroup(FreeSymbol.(a))
|
||||||
FreeGroup(a::Vector) = FreeGroup(FreeSymbol.(a))
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# Parent object call overloads
|
# Parent object call overloads
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
function Base.one(G::FreeGroup)
|
|
||||||
id = FreeGroupElem(FreeSymbol[])
|
|
||||||
id.parent = G
|
|
||||||
return id
|
|
||||||
end
|
|
||||||
|
|
||||||
function (G::FreeGroup)(w::GroupWord{FreeSymbol})
|
function (G::FreeGroup)(w::GroupWord{FreeSymbol})
|
||||||
if length(w) > 0
|
for s in syllables(w)
|
||||||
for s in w.symbols
|
|
||||||
i = findfirst(g -> g.id == s.id, G.gens)
|
i = findfirst(g -> g.id == s.id, G.gens)
|
||||||
i == 0 && throw(DomainError(
|
isnothing(i) && throw(DomainError(
|
||||||
"Symbol $s does not belong to $G."))
|
"Symbol $s does not belong to $G."))
|
||||||
s.pow % G.gens[i].pow == 0 || throw(DomainError(
|
s.pow % G.gens[i].pow == 0 || throw(DomainError(
|
||||||
"Symbol $s doesn't belong to $G."))
|
"Symbol $s doesn't belong to $G."))
|
||||||
end
|
end
|
||||||
end
|
setparent!(w, G)
|
||||||
w.parent = G
|
return reduce!(w)
|
||||||
return w
|
|
||||||
end
|
end
|
||||||
|
|
||||||
(G::FreeGroup)(s::FreeSymbol) = G(FreeGroupElem(s))
|
(G::FreeGroup)(s::GSymbol) = G(FreeGroupElem(s))
|
||||||
|
(G::FreeGroup)(v::AbstractVector{<:GSymbol}) = G(FreeGroupElem(FreeSymbol.(v)))
|
||||||
###############################################################################
|
|
||||||
#
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# String I/O
|
# String I/O
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
function show(io::IO, G::FreeGroup)
|
function show(io::IO, G::FreeGroup)
|
||||||
print(io, "Free group on $(length(G.gens)) generators: ")
|
print(io, "Free group on $(length(G.gens)) generators: ")
|
||||||
join(io, G.gens, ", ")
|
join(io, G.gens, ", ")
|
||||||
end
|
end
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
#
|
|
||||||
# Comparison
|
|
||||||
#
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
#
|
|
||||||
# Inversion
|
|
||||||
#
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
inv(s::FreeSymbol) = change_pow(s, -s.pow)
|
|
||||||
|
451
src/Groups.jl
451
src/Groups.jl
@ -7,463 +7,100 @@ import AbstractAlgebra: order, gens, matrix_repr
|
|||||||
|
|
||||||
import Base: length, ==, hash, show, convert, eltype, iterate
|
import Base: length, ==, hash, show, convert, eltype, iterate
|
||||||
import Base: inv, reduce, *, ^, power_by_squaring
|
import Base: inv, reduce, *, ^, power_by_squaring
|
||||||
import Base: findfirst, findnext
|
import Base: findfirst, findnext, replace
|
||||||
import Base: deepcopy_internal
|
import Base: deepcopy_internal
|
||||||
|
|
||||||
export elements
|
|
||||||
|
|
||||||
using LinearAlgebra
|
using LinearAlgebra
|
||||||
using Markdown
|
using Markdown
|
||||||
|
|
||||||
Base.one(G::Generic.PermGroup) = G(collect(1:G.n), false)
|
export gens, FreeGroup, Aut, SAut
|
||||||
|
|
||||||
###############################################################################
|
include("types.jl")
|
||||||
#
|
|
||||||
# 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("FreeGroup.jl")
|
include("FreeGroup.jl")
|
||||||
include("FPGroups.jl")
|
include("FPGroups.jl")
|
||||||
include("AutGroup.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("DirectPower.jl")
|
||||||
include("WreathProducts.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
|
# String I/O
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
@doc doc"""
|
@doc doc"""
|
||||||
show(io::IO, W::GWord)
|
show(io::IO, W::GWord)
|
||||||
> The actual string produced by show depends on the eltype of `W.symbols`.
|
> 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
|
if length(W) == 0
|
||||||
print(io, "(id)")
|
print(io, "(id)")
|
||||||
else
|
else
|
||||||
join(io, [string(s) for s in W.symbols], "*")
|
join(io, (string(s) for s in syllables(W)), "*")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function show(io::IO, s::T) where {T<:GSymbol}
|
function Base.show(io::IO, s::T) where {T<:GSymbol}
|
||||||
if s.pow == 1
|
if s.pow == 1
|
||||||
print(io, string(s.id))
|
print(io, string(s.id))
|
||||||
else
|
else
|
||||||
print(io, string((s.id))*"^$(s.pow)")
|
print(io, "$(s.id)^$(s.pow)")
|
||||||
end
|
end
|
||||||
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
|
|
||||||
else
|
|
||||||
G = parent(W)
|
|
||||||
w = T(reverse([inv(s) for s in W.symbols]))
|
|
||||||
w.modified = true
|
|
||||||
return G(w)
|
|
||||||
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
|
# Misc
|
||||||
#
|
#
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
function generate_balls(S::AbstractVector{T}, Id::T=one(parent(first(S)));
|
@doc doc"""
|
||||||
radius=2, op=*) where T<:GroupElem
|
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[]
|
sizes = Int[]
|
||||||
B = [Id]
|
B = [one(first(S))]
|
||||||
for i in 1:radius
|
for i in 1:radius
|
||||||
BB = [op(i,j) for (i,j) in Base.product(B,S)]
|
BB = [op(i,j) for (i,j) in Base.product(B,S)]
|
||||||
B = unique([B; vec(BB)])
|
B = unique([B; vec(BB)])
|
||||||
push!(sizes, length(B))
|
push!(sizes, length(B))
|
||||||
end
|
end
|
||||||
return B, sizes
|
isone(center) && return B, sizes
|
||||||
|
return c.*B, sizes
|
||||||
end
|
end
|
||||||
|
|
||||||
function generate_balls(S::AbstractVector{T}, Id::T=one(parent(first(S)));
|
@doc doc"""
|
||||||
radius=2, op=*) where {T<:NCRingElem}
|
image(A::GWord, homomorphism; kwargs...)
|
||||||
sizes = Int[]
|
Evaluate homomorphism `homomorphism` on a GWord `A`.
|
||||||
B = [Id]
|
`homomorphism` needs implement
|
||||||
for i in 1:radius
|
> `hom(s; kwargs...)`,
|
||||||
BB = [op(i,j) for (i,j) in Base.product(B,S)]
|
where `hom(;kwargs...)` evaluates the value at the identity element.
|
||||||
B = unique([B; vec(BB)])
|
"""
|
||||||
push!(sizes, length(B))
|
function image(w::GWord, hom; kwargs...)
|
||||||
end
|
return reduce(*,
|
||||||
return B, sizes
|
(hom(s; kwargs...) for s in syllables(w)),
|
||||||
|
init = hom(;kwargs...))
|
||||||
end
|
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
|
end # of module Groups
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
export WreathProduct, WreathProductElem
|
export WreathProduct, WreathProductElem
|
||||||
|
|
||||||
|
import AbstractAlgebra: AbstractPermutationGroup, AbstractPerm
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# WreathProduct / WreathProductElem
|
# WreathProduct / WreathProductElem
|
||||||
@ -19,22 +21,23 @@ export WreathProduct, WreathProductElem
|
|||||||
* `N::Group` : the single factor of the group $N$
|
* `N::Group` : the single factor of the group $N$
|
||||||
* `P::Generic.PermGroup` : full `PermutationGroup`
|
* `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}
|
N::DirectPowerGroup{N, T}
|
||||||
P::PG
|
P::PG
|
||||||
|
|
||||||
function WreathProduct(Gr::T, P::PG) where {T, PG<:Generic.PermGroup}
|
function WreathProduct(G::Gr, P::PG) where
|
||||||
N = DirectPowerGroup(Gr, Int(P.n))
|
{Gr <: Group, PG <: AbstractPermutationGroup}
|
||||||
return new{Int(P.n), T, PG}(N, P)
|
N = DirectPowerGroup(G, Int(P.n))
|
||||||
|
return new{Int(P.n), Gr, PG}(N, P)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
struct WreathProductElem{N, T<:GroupElem, P<:Generic.Perm} <: GroupElem
|
struct WreathProductElem{N, T<:GroupElem, P<:AbstractPerm} <: GroupElem
|
||||||
n::DirectPowerGroupElem{N, T}
|
n::DirectPowerGroupElem{N, T}
|
||||||
p::P
|
p::P
|
||||||
|
|
||||||
function WreathProductElem(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
|
if check
|
||||||
N == length(p.d) || throw(DomainError(
|
N == length(p.d) || throw(DomainError(
|
||||||
"Can't form WreathProductElem: lengths differ"))
|
"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
|
@testset "Automorphisms" begin
|
||||||
|
|
||||||
G = PermutationGroup(Int8(4))
|
G = SymmetricGroup(Int8(4))
|
||||||
|
|
||||||
@testset "AutSymbol" begin
|
@testset "AutSymbol" begin
|
||||||
@test_throws MethodError Groups.AutSymbol(:a)
|
@test_throws MethodError Groups.AutSymbol(:a)
|
||||||
@ -8,72 +8,73 @@
|
|||||||
f = Groups.AutSymbol(:a, 1, Groups.FlipAut(2))
|
f = Groups.AutSymbol(:a, 1, Groups.FlipAut(2))
|
||||||
@test isa(f, Groups.GSymbol)
|
@test isa(f, Groups.GSymbol)
|
||||||
@test isa(f, Groups.AutSymbol)
|
@test isa(f, Groups.AutSymbol)
|
||||||
@test isa(Groups.perm_autsymbol(Int8.([1,2,3,4])), Groups.AutSymbol)
|
@test isa(Groups.AutSymbol(perm"(4)"), Groups.AutSymbol)
|
||||||
@test isa(Groups.rmul_autsymbol(1,2), Groups.AutSymbol)
|
@test isa(Groups.AutSymbol(perm"(1,2,3,4)"), Groups.AutSymbol)
|
||||||
@test isa(Groups.lmul_autsymbol(3,4), Groups.AutSymbol)
|
@test isa(Groups.transvection_R(1,2), Groups.AutSymbol)
|
||||||
@test isa(Groups.flip_autsymbol(3), Groups.AutSymbol)
|
@test isa(Groups.transvection_R(3,4), Groups.AutSymbol)
|
||||||
|
@test isa(Groups.flip(3), Groups.AutSymbol)
|
||||||
end
|
end
|
||||||
|
|
||||||
a,b,c,d = gens(FreeGroup(4))
|
a,b,c,d = gens(FreeGroup(4))
|
||||||
D = NTuple{4,FreeGroupElem}([a,b,c,d])
|
D = NTuple{4,FreeGroupElem}([a,b,c,d])
|
||||||
|
|
||||||
@testset "flip_autsymbol correctness" begin
|
@testset "flip correctness" begin
|
||||||
@test Groups.flip_autsymbol(1)(deepcopy(D)) == (a^-1, b,c,d)
|
@test Groups.flip(1)(deepcopy(D)) == (a^-1, b,c,d)
|
||||||
@test Groups.flip_autsymbol(2)(deepcopy(D)) == (a, b^-1,c,d)
|
@test Groups.flip(2)(deepcopy(D)) == (a, b^-1,c,d)
|
||||||
@test Groups.flip_autsymbol(3)(deepcopy(D)) == (a, b,c^-1,d)
|
@test Groups.flip(3)(deepcopy(D)) == (a, b,c^-1,d)
|
||||||
@test Groups.flip_autsymbol(4)(deepcopy(D)) == (a, b,c,d^-1)
|
@test Groups.flip(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(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(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(3))(deepcopy(D)) == (a, b,c^-1,d)
|
||||||
@test inv(Groups.flip_autsymbol(4))(deepcopy(D)) == (a, b,c,d^-1)
|
@test inv(Groups.flip(4))(deepcopy(D)) == (a, b,c,d^-1)
|
||||||
end
|
end
|
||||||
|
|
||||||
@testset "perm_autsymbol correctness" begin
|
@testset "perm correctness" begin
|
||||||
σ = Groups.perm_autsymbol([1,2,3,4])
|
σ = Groups.AutSymbol(perm"(4)")
|
||||||
@test σ(deepcopy(D)) == deepcopy(D)
|
@test σ(deepcopy(D)) == deepcopy(D)
|
||||||
@test inv(σ)(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 σ(deepcopy(D)) == (b, c, d, a)
|
||||||
@test inv(σ)(deepcopy(D)) == (d, a, b, c)
|
@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 σ(deepcopy(D)) == (b, a, d, c)
|
||||||
@test inv(σ)(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 σ(deepcopy(D)) == (b, c, a, d)
|
||||||
@test inv(σ)(deepcopy(D)) == (c, a, b, d)
|
@test inv(σ)(deepcopy(D)) == (c, a, b, d)
|
||||||
end
|
end
|
||||||
|
|
||||||
@testset "rmul/lmul_autsymbol correctness" begin
|
@testset "rmul/transvection_R correctness" begin
|
||||||
i,j = 1,2
|
i,j = 1,2
|
||||||
r = Groups.rmul_autsymbol(i,j)
|
r = Groups.transvection_R(i,j)
|
||||||
l = Groups.lmul_autsymbol(i,j)
|
l = Groups.transvection_L(i,j)
|
||||||
@test r(deepcopy(D)) == (a*b, b, c, d)
|
@test r(deepcopy(D)) == (a*b, b, c, d)
|
||||||
@test inv(r)(deepcopy(D)) == (a*b^-1,b, c, d)
|
@test inv(r)(deepcopy(D)) == (a*b^-1,b, c, d)
|
||||||
@test l(deepcopy(D)) == (b*a, b, c, d)
|
@test l(deepcopy(D)) == (b*a, b, c, d)
|
||||||
@test inv(l)(deepcopy(D)) == (b^-1*a,b, c, d)
|
@test inv(l)(deepcopy(D)) == (b^-1*a,b, c, d)
|
||||||
|
|
||||||
i,j = 3,1
|
i,j = 3,1
|
||||||
r = Groups.rmul_autsymbol(i,j)
|
r = Groups.transvection_R(i,j)
|
||||||
l = Groups.lmul_autsymbol(i,j)
|
l = Groups.transvection_L(i,j)
|
||||||
@test r(deepcopy(D)) == (a, b, c*a, d)
|
@test r(deepcopy(D)) == (a, b, c*a, d)
|
||||||
@test inv(r)(deepcopy(D)) == (a, b, c*a^-1,d)
|
@test inv(r)(deepcopy(D)) == (a, b, c*a^-1,d)
|
||||||
@test l(deepcopy(D)) == (a, b, a*c, d)
|
@test l(deepcopy(D)) == (a, b, a*c, d)
|
||||||
@test inv(l)(deepcopy(D)) == (a, b, a^-1*c,d)
|
@test inv(l)(deepcopy(D)) == (a, b, a^-1*c,d)
|
||||||
|
|
||||||
i,j = 4,3
|
i,j = 4,3
|
||||||
r = Groups.rmul_autsymbol(i,j)
|
r = Groups.transvection_R(i,j)
|
||||||
l = Groups.lmul_autsymbol(i,j)
|
l = Groups.transvection_L(i,j)
|
||||||
@test r(deepcopy(D)) == (a, b, c, d*c)
|
@test r(deepcopy(D)) == (a, b, c, d*c)
|
||||||
@test inv(r)(deepcopy(D)) == (a, b, c, d*c^-1)
|
@test inv(r)(deepcopy(D)) == (a, b, c, d*c^-1)
|
||||||
@test l(deepcopy(D)) == (a, b, c, c*d)
|
@test l(deepcopy(D)) == (a, b, c, c*d)
|
||||||
@test inv(l)(deepcopy(D)) == (a, b, c, c^-1*d)
|
@test inv(l)(deepcopy(D)) == (a, b, c, c^-1*d)
|
||||||
|
|
||||||
i,j = 2,4
|
i,j = 2,4
|
||||||
r = Groups.rmul_autsymbol(i,j)
|
r = Groups.transvection_R(i,j)
|
||||||
l = Groups.lmul_autsymbol(i,j)
|
l = Groups.transvection_L(i,j)
|
||||||
@test r(deepcopy(D)) == (a, b*d, c, d)
|
@test r(deepcopy(D)) == (a, b*d, c, d)
|
||||||
@test inv(r)(deepcopy(D)) == (a, b*d^-1,c, d)
|
@test inv(r)(deepcopy(D)) == (a, b*d^-1,c, d)
|
||||||
@test l(deepcopy(D)) == (a, d*b, c, d)
|
@test l(deepcopy(D)) == (a, d*b, c, d)
|
||||||
@ -94,40 +95,40 @@
|
|||||||
@test length(Groups.gens(A)) == 0
|
@test length(Groups.gens(A)) == 0
|
||||||
A = AutGroup(FreeGroup(2))
|
A = AutGroup(FreeGroup(2))
|
||||||
@test length(Groups.gens(A)) == 7
|
@test length(Groups.gens(A)) == 7
|
||||||
gens = Groups.gens(A)
|
Agens = Groups.gens(A)
|
||||||
|
|
||||||
@test isa(A(Groups.rmul_autsymbol(1,2)), Automorphism)
|
@test isa(A(Groups.transvection_R(1,2)), Automorphism)
|
||||||
@test A(Groups.rmul_autsymbol(1,2)) in gens
|
@test A(Groups.transvection_R(1,2)) in Agens
|
||||||
|
|
||||||
@test isa(A(Groups.rmul_autsymbol(2,1)), Automorphism)
|
@test isa(A(Groups.transvection_R(2,1)), Automorphism)
|
||||||
@test A(Groups.rmul_autsymbol(2,1)) in gens
|
@test A(Groups.transvection_R(2,1)) in Agens
|
||||||
|
|
||||||
@test isa(A(Groups.lmul_autsymbol(1,2)), Automorphism)
|
@test isa(A(Groups.transvection_R(1,2)), Automorphism)
|
||||||
@test A(Groups.lmul_autsymbol(1,2)) in gens
|
@test A(Groups.transvection_R(1,2)) in Agens
|
||||||
|
|
||||||
@test isa(A(Groups.lmul_autsymbol(2,1)), Automorphism)
|
@test isa(A(Groups.transvection_R(2,1)), Automorphism)
|
||||||
@test A(Groups.lmul_autsymbol(2,1)) in gens
|
@test A(Groups.transvection_R(2,1)) in Agens
|
||||||
|
|
||||||
@test isa(A(Groups.flip_autsymbol(1)), Automorphism)
|
@test isa(A(Groups.flip(1)), Automorphism)
|
||||||
@test A(Groups.flip_autsymbol(1)) in gens
|
@test A(Groups.flip(1)) in Agens
|
||||||
|
|
||||||
@test isa(A(Groups.flip_autsymbol(2)), Automorphism)
|
@test isa(A(Groups.flip(2)), Automorphism)
|
||||||
@test A(Groups.flip_autsymbol(2)) in gens
|
@test A(Groups.flip(2)) in Agens
|
||||||
|
|
||||||
@test isa(A(Groups.perm_autsymbol([2,1])), Automorphism)
|
@test isa(A(Groups.AutSymbol(perm"(1,2)")), Automorphism)
|
||||||
@test A(Groups.perm_autsymbol([2,1])) in gens
|
@test A(Groups.AutSymbol(perm"(1,2)")) in Agens
|
||||||
end
|
end
|
||||||
|
|
||||||
A = AutGroup(FreeGroup(4))
|
A = AutGroup(FreeGroup(4))
|
||||||
|
|
||||||
@testset "eltary functions" begin
|
@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 (Groups.change_pow(f, -2)).pow == 1
|
@test (Groups.change_pow(f, -2)).pow == 1
|
||||||
@test (inv(f)).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 isa(inv(f), Groups.AutSymbol)
|
||||||
|
|
||||||
@test_throws MethodError f*f
|
@test_throws MethodError f*f
|
||||||
@ -136,14 +137,15 @@
|
|||||||
end
|
end
|
||||||
|
|
||||||
@testset "reductions/arithmetic" begin
|
@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)
|
f² = append!(A(f), [f])
|
||||||
@test Groups.simplifyperms!(f²) == false
|
@test Groups.simplifyperms!(Bool, f²) == false
|
||||||
@test f²^2 == one(A)
|
@test f²^2 == one(A)
|
||||||
|
@test !isone(f²)
|
||||||
|
|
||||||
a = A(Groups.rmul_autsymbol(1,2))*Groups.flip_autsymbol(2)
|
a = A(Groups.transvection_L(1,2))*Groups.flip(2)
|
||||||
b = Groups.flip_autsymbol(2)*A(inv(Groups.rmul_autsymbol(1,2)))
|
b = Groups.flip(2)*A(inv(Groups.transvection_L(1,2)))
|
||||||
@test a*b == b*a
|
@test a*b == b*a
|
||||||
@test a^3 * b^3 == one(A)
|
@test a^3 * b^3 == one(A)
|
||||||
g,h = Groups.gens(A)[[1,8]] # (g, h) = (ϱ₁₂, ϱ₃₂)
|
g,h = Groups.gens(A)[[1,8]] # (g, h) = (ϱ₁₂, ϱ₃₂)
|
||||||
@ -164,27 +166,31 @@
|
|||||||
# Not so simple arithmetic: applying starting on the left:
|
# 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)
|
x1, x2, x3, x4 = Groups.domain(A)
|
||||||
@test g(Groups.domain(A)) == (x1*x2, x2, x3, x4)
|
@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)
|
@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)
|
@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)) == (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))
|
g_im = g(Groups.domain(A))
|
||||||
@test length(g_im[1]) == 5
|
@test length.(g_im) == (1,1,1,1)
|
||||||
@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)
|
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
@testset "specific Aut(F4) tests" begin
|
@testset "specific Aut(F4) tests" begin
|
||||||
@ -208,48 +214,48 @@
|
|||||||
@test length(unique(B_2)) == 1777
|
@test length(unique(B_2)) == 1777
|
||||||
end
|
end
|
||||||
|
|
||||||
@testset "linear_repr tests" begin
|
@testset "abelianization homomorphism" begin
|
||||||
N = 3
|
N = 4
|
||||||
G = AutGroup(FreeGroup(N))
|
G = AutGroup(FreeGroup(N))
|
||||||
S = unique([gens(G); inv.(gens(G))])
|
S = unique([gens(G); inv.(gens(G))])
|
||||||
R = 3
|
R = 3
|
||||||
|
|
||||||
@test Groups.linear_repr(one(G)) isa Matrix{Int}
|
@test Groups.abelianize(one(G)) isa Matrix{Int}
|
||||||
@test Groups.linear_repr(one(G)) == Matrix{Int}(I, N, N)
|
@test Groups.abelianize(one(G)) == Matrix{Int}(I, N, N)
|
||||||
|
|
||||||
M = Matrix{Int}(I, N, N)
|
M = Matrix{Int}(I, N, N)
|
||||||
M[1,2] = 1
|
M[1,2] = 1
|
||||||
ϱ₁₂ = G(Groups.rmul_autsymbol(1,2))
|
ϱ₁₂ = G(Groups.ϱ(1,2))
|
||||||
λ₁₂ = G(Groups.rmul_autsymbol(1,2))
|
λ₁₂ = G(Groups.λ(1,2))
|
||||||
|
|
||||||
@test Groups.linear_repr(ϱ₁₂) == M
|
@test Groups.abelianize(ϱ₁₂) == M
|
||||||
@test Groups.linear_repr(λ₁₂) == M
|
@test Groups.abelianize(λ₁₂) == M
|
||||||
|
|
||||||
M[1,2] = -1
|
M[1,2] = -1
|
||||||
|
|
||||||
@test Groups.linear_repr(ϱ₁₂^-1) == M
|
@test Groups.abelianize(ϱ₁₂^-1) == M
|
||||||
@test Groups.linear_repr(λ₁₂^-1) == M
|
@test Groups.abelianize(λ₁₂^-1) == M
|
||||||
|
|
||||||
@test Groups.linear_repr(ϱ₁₂*λ₁₂^-1) == Matrix{Int}(I, N, N)
|
@test Groups.abelianize(ϱ₁₂*λ₁₂^-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)
|
||||||
|
|
||||||
M = Matrix{Int}(I, N, N)
|
M = Matrix{Int}(I, N, N)
|
||||||
M[2,2] = -1
|
M[2,2] = -1
|
||||||
ε₂ = G(Groups.flip_autsymbol(2))
|
ε₂ = G(Groups.flip(2))
|
||||||
|
|
||||||
@test Groups.linear_repr(ε₂) == M
|
@test Groups.abelianize(ε₂) == M
|
||||||
@test Groups.linear_repr(ε₂^2) == Matrix{Int}(I, N, N)
|
@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]))
|
σ = G(Groups.AutSymbol(perm"(1,2,4)"))
|
||||||
@test Groups.linear_repr(σ) == M
|
@test Groups.abelianize(σ) == M
|
||||||
@test Groups.linear_repr(σ^3) == Matrix{Int}(I, 3, 3)
|
@test Groups.abelianize(σ^3) == Matrix{Int}(I, N, N)
|
||||||
@test Groups.linear_repr(σ)^3 == Matrix{Int}(I, 3, 3)
|
@test Groups.abelianize(σ)^3 == Matrix{Int}(I, N, N)
|
||||||
|
|
||||||
function test_homomorphism(S, r)
|
function test_homomorphism(S, r)
|
||||||
for elts in Iterators.product([[g for g in S] for _ in 1: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
|
end
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
×(a,b) = Groups.DirectPower(a,b)
|
×(a,b) = Groups.DirectPower(a,b)
|
||||||
|
|
||||||
@testset "Constructors" begin
|
@testset "Constructors" begin
|
||||||
G = PermutationGroup(3)
|
G = SymmetricGroup(3)
|
||||||
|
|
||||||
@test Groups.DirectPowerGroup(G,2) isa AbstractAlgebra.Group
|
@test Groups.DirectPowerGroup(G,2) isa AbstractAlgebra.Group
|
||||||
@test G×G 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 == DirectPowerGroup(G, 3)
|
||||||
@test (G×G)×G == (G×G)×G
|
@test (G×G)×G == (G×G)×G
|
||||||
@ -42,7 +42,7 @@
|
|||||||
end
|
end
|
||||||
|
|
||||||
@testset "Basic arithmetic" begin
|
@testset "Basic arithmetic" begin
|
||||||
G = PermutationGroup(3)
|
G = SymmetricGroup(3)
|
||||||
GG = G×G
|
GG = G×G
|
||||||
i = perm"(1,3)"
|
i = perm"(1,3)"
|
||||||
g = perm"(1,2,3)"
|
g = perm"(1,2,3)"
|
||||||
@ -65,7 +65,7 @@
|
|||||||
end
|
end
|
||||||
|
|
||||||
@testset "elem/parent_types" begin
|
@testset "elem/parent_types" begin
|
||||||
G = PermutationGroup(3)
|
G = SymmetricGroup(3)
|
||||||
g = perm"(1,2,3)"
|
g = perm"(1,2,3)"
|
||||||
|
|
||||||
@test elem_type(G×G) == DirectPowerGroupElem{2, elem_type(G)}
|
@test elem_type(G×G) == DirectPowerGroupElem{2, elem_type(G)}
|
||||||
@ -75,7 +75,7 @@
|
|||||||
end
|
end
|
||||||
|
|
||||||
@testset "Misc" begin
|
@testset "Misc" begin
|
||||||
G = PermutationGroup(3)
|
G = SymmetricGroup(3)
|
||||||
GG = Groups.DirectPowerGroup(G,3)
|
GG = Groups.DirectPowerGroup(G,3)
|
||||||
@test order(GG) == 216
|
@test order(GG) == 216
|
||||||
|
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
@testset "FPGroups definitions" begin
|
@testset "FPGroups definitions" begin
|
||||||
F = FreeGroup(["a", "b", "c"])
|
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]
|
R = [a^2, a*b*a, c*b*a]
|
||||||
@test F/R isa FPGroup
|
@test F/R isa FPGroup
|
||||||
@test F isa FreeGroup
|
@test F isa FreeGroup
|
||||||
G = F/R
|
G = F/R
|
||||||
A,B,C = gens(G)
|
A,B,C = Groups.gens(G)
|
||||||
|
|
||||||
@test A^2 == one(G)
|
@test Groups.reduce!(A^2) == one(G)
|
||||||
@test A*B*A*A == A
|
@test Groups.reduce!(A*B*A*A) == A
|
||||||
@test A*A*B*A == B*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
|
@test G/[B^2, C*B*C] isa FPGroup
|
||||||
end
|
end
|
||||||
|
@ -55,23 +55,18 @@ end
|
|||||||
|
|
||||||
@testset "internal arithmetic" begin
|
@testset "internal arithmetic" begin
|
||||||
|
|
||||||
@test Vector{Groups.FreeGroupElem}([s,t]) == [Groups.GroupWord(s), Groups.GroupWord(t)]
|
|
||||||
@test (s*s).symbols == (s^2).symbols
|
@test (s*s).symbols == (s^2).symbols
|
||||||
@test hash([t^1,s^1]) == hash([t^2*inv(t),s*inv(s)*s])
|
@test hash([t^1,s^1]) == hash([t^2*inv(t),s*inv(s)*s])
|
||||||
|
|
||||||
t_symb = Groups.FreeSymbol(:t)
|
t_symb = Groups.FreeSymbol(:t)
|
||||||
tt = deepcopy(t)
|
tt = deepcopy(t)
|
||||||
@test string(Groups.r_multiply!(tt,[inv(t_symb)]; reduced=true)) ==
|
@test string(Groups.rmul!(tt, tt, inv(t_symb))) == "(id)"
|
||||||
"(id)"
|
|
||||||
tt = deepcopy(t)
|
tt = deepcopy(t)
|
||||||
@test string(Groups.r_multiply!(tt,[inv(t_symb)]; reduced=false)) ==
|
@test string(append!(tt, [inv(t_symb)])) == "t*t^-1"
|
||||||
"t*t^-1"
|
|
||||||
tt = deepcopy(t)
|
tt = deepcopy(t)
|
||||||
@test string(Groups.l_multiply!(tt,[inv(t_symb)]; reduced=true)) ==
|
@test string(Groups.lmul!(tt, tt, inv(t_symb))) == "(id)"
|
||||||
"(id)"
|
|
||||||
tt = deepcopy(t)
|
tt = deepcopy(t)
|
||||||
@test string(Groups.l_multiply!(tt,[inv(t_symb)]; reduced=false)) ==
|
@test string(prepend!(tt, [inv(t_symb)])) == "t^-1*t"
|
||||||
"t^-1*t"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@testset "reductions" begin
|
@testset "reductions" begin
|
||||||
@ -79,7 +74,7 @@ end
|
|||||||
@test length((one(G)*one(G)).symbols) == 0
|
@test length((one(G)*one(G)).symbols) == 0
|
||||||
@test one(G) == one(G)*one(G)
|
@test one(G) == one(G)*one(G)
|
||||||
w = deepcopy(s)
|
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))
|
@test Groups.reduce!(w) == one(parent(w))
|
||||||
o = (t*s)^3
|
o = (t*s)^3
|
||||||
@test o == t*s*t*s*t*s
|
@test o == t*s*t*s*t*s
|
||||||
@ -116,23 +111,23 @@ end
|
|||||||
@test Groups.issubsymbol(inv(b), Groups.change_pow(b,-2)) == true
|
@test Groups.issubsymbol(inv(b), Groups.change_pow(b,-2)) == true
|
||||||
|
|
||||||
c = s*t*s^-1*t^-1
|
c = s*t*s^-1*t^-1
|
||||||
@test findfirst(c, s^-1*t^-1) == 3
|
@test findfirst(s^-1*t^-1, c) == 3
|
||||||
@test findnext(c*s^-1, s^-1*t^-1,3) == 3
|
@test findnext(s^-1*t^-1, c*s^-1,3) == 3
|
||||||
@test findnext(c*s^-1*t^-1, s^-1*t^-1,4) == 5
|
@test findnext(s^-1*t^-1, c*s^-1*t^-1,4) == 5
|
||||||
@test findfirst(c*t, c) == 0
|
@test findfirst(c, c*t) === nothing
|
||||||
w = s*t*s^-1
|
w = s*t*s^-1
|
||||||
subst = Dict{FreeGroupElem, FreeGroupElem}(w => s^1, s*t^-1 => t^4)
|
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, s*t=>one(G)) == s^-1*t^-1
|
||||||
@test Groups.replace(c, 1, w, subst[w]) == s*t^-1
|
@test Groups.replace(c, w=>subst[w]) == s*t^-1
|
||||||
@test Groups.replace(s*c*t^-1, 1, w, subst[w]) == s^2*t^-2
|
@test Groups.replace(s*c*t^-1, w=>subst[w]) == s^2*t^-2
|
||||||
@test Groups.replace(t*c*t, 2, w, subst[w]) == t*s
|
@test Groups.replace(t*c*t, 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(s*c*s*c*s, subst) == s*t^4*s*t^4*s
|
||||||
|
|
||||||
G = FreeGroup(["x", "y"])
|
G = FreeGroup(["x", "y"])
|
||||||
x,y = gens(G)
|
x,y = gens(G)
|
||||||
|
|
||||||
@test Groups.replace(x*y^9, 2, y^2, y) == x*y^8
|
@test Groups.replace(x*y^9, y^2=>y) == x*y^5
|
||||||
@test Groups.replace(x^3, 1, x^2, y) == x*y
|
@test Groups.replace(x^3, x^2=>y) == x*y
|
||||||
@test Groups.replace(y*x^3*y, 2, x^2, y) == y*x*y^2
|
@test Groups.replace(y*x^3*y, x^2=>y) == y*x*y^2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
@testset "WreathProducts" begin
|
@testset "WreathProducts" begin
|
||||||
S_3 = PermutationGroup(3)
|
S_3 = SymmetricGroup(3)
|
||||||
S_2 = PermutationGroup(2)
|
S_2 = SymmetricGroup(2)
|
||||||
b = perm"(1,2,3)"
|
b = perm"(1,2,3)"
|
||||||
a = perm"(1,2)"
|
a = perm"(1,2)"
|
||||||
|
|
||||||
@ -8,7 +8,7 @@
|
|||||||
@test Groups.WreathProduct(S_2, S_3) isa AbstractAlgebra.Group
|
@test Groups.WreathProduct(S_2, S_3) isa AbstractAlgebra.Group
|
||||||
B3 = Groups.WreathProduct(S_2, S_3)
|
B3 = Groups.WreathProduct(S_2, S_3)
|
||||||
@test B3 isa Groups.WreathProduct
|
@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))
|
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 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)) == Groups.WreathProduct(S_2,S_3)
|
||||||
@test parent(one(B3)) == B3
|
@test parent(one(B3)) == B3
|
||||||
@ -64,7 +64,7 @@
|
|||||||
end
|
end
|
||||||
|
|
||||||
@testset "Group arithmetic" begin
|
@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)"
|
id, a, b = perm"(3)", perm"(1,2)(3)", perm"(1,2,3)"
|
||||||
|
|
||||||
@ -84,7 +84,7 @@
|
|||||||
end
|
end
|
||||||
|
|
||||||
@testset "Iteration" begin
|
@testset "Iteration" begin
|
||||||
Wr = WreathProduct(PermutationGroup(2),PermutationGroup(4))
|
Wr = WreathProduct(SymmetricGroup(2),SymmetricGroup(4))
|
||||||
|
|
||||||
elts = collect(Wr)
|
elts = collect(Wr)
|
||||||
@test elts isa Vector{Groups.WreathProductElem{4, Generic.Perm{Int}, Generic.Perm{Int}}}
|
@test elts isa Vector{Groups.WreathProductElem{4, Generic.Perm{Int}, Generic.Perm{Int}}}
|
||||||
|
Loading…
Reference in New Issue
Block a user