mirror of
https://github.com/kalmarek/Groups.jl.git
synced 2024-10-15 07:20:35 +02:00
commit
bc23ce0a90
11
.github/workflows/TagBot.yml
vendored
Normal file
11
.github/workflows/TagBot.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
name: TagBot
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: 0 * * * *
|
||||||
|
jobs:
|
||||||
|
TagBot:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: JuliaRegistries/TagBot@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
@ -1,15 +1,15 @@
|
|||||||
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.4.2"
|
version = "0.5.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"
|
ThreadsX = "ac1d9e8a-700a-412c-b207-f0111f4b6c0d"
|
||||||
|
|
||||||
[compat]
|
[compat]
|
||||||
AbstractAlgebra = "^0.9.0"
|
AbstractAlgebra = "^0.10.0"
|
||||||
|
|
||||||
[extras]
|
[extras]
|
||||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||||
|
@ -35,9 +35,7 @@ end
|
|||||||
# https://github.com/JuliaIntervals/ValidatedNumerics.jl/blob/master/LICENSE.md
|
# https://github.com/JuliaIntervals/ValidatedNumerics.jl/blob/master/LICENSE.md
|
||||||
function subscriptify(n::Integer)
|
function subscriptify(n::Integer)
|
||||||
subscript_0 = Int(0x2080) # Char(0x2080) -> subscript 0
|
subscript_0 = Int(0x2080) # Char(0x2080) -> subscript 0
|
||||||
@assert 0 <= n <= 9
|
return join([Char(subscript_0 + i) for i in reverse(digits(n))], "")
|
||||||
return Char(subscript_0 + n)
|
|
||||||
# return [Char(subscript_0 + i) for i in reverse(digits(n))])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function id_autsymbol()
|
function id_autsymbol()
|
||||||
@ -45,18 +43,26 @@ function id_autsymbol()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function transvection_R(i::Integer, j::Integer, pow::Integer=1)
|
function transvection_R(i::Integer, j::Integer, pow::Integer=1)
|
||||||
id = Symbol("ϱ", subscriptify(i), subscriptify(j))
|
if 0 < i < 10 && 0 < j < 10
|
||||||
|
id = Symbol(:ϱ, subscriptify(i), subscriptify(j))
|
||||||
|
else
|
||||||
|
id = Symbol(:ϱ, subscriptify(i), "." ,subscriptify(j))
|
||||||
|
end
|
||||||
return AutSymbol(id, pow, RTransvect(i, j))
|
return AutSymbol(id, pow, RTransvect(i, j))
|
||||||
end
|
end
|
||||||
|
|
||||||
function transvection_L(i::Integer, j::Integer, pow::Integer=1)
|
function transvection_L(i::Integer, j::Integer, pow::Integer=1)
|
||||||
id = Symbol("λ", subscriptify(i), subscriptify(j))
|
if 0 < i < 10 && 0 < j < 10
|
||||||
|
id = Symbol(:λ, subscriptify(i), subscriptify(j))
|
||||||
|
else
|
||||||
|
id = Symbol(:λ, subscriptify(i), "." ,subscriptify(j))
|
||||||
|
end
|
||||||
return AutSymbol(id, pow, LTransvect(i, j))
|
return AutSymbol(id, pow, LTransvect(i, j))
|
||||||
end
|
end
|
||||||
|
|
||||||
function flip(i::Integer, pow::Integer=1)
|
function flip(i::Integer, pow::Integer=1)
|
||||||
iseven(pow) && return id_autsymbol()
|
iseven(pow) && return id_autsymbol()
|
||||||
id = Symbol("ɛ", subscriptify(i))
|
id = Symbol(:ɛ, subscriptify(i))
|
||||||
return AutSymbol(id, 1, FlipAut(i))
|
return AutSymbol(id, 1, FlipAut(i))
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -66,7 +72,7 @@ function AutSymbol(p::Generic.Perm, pow::Integer=1)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if any(p.d[i] != i for i in eachindex(p.d))
|
if any(p.d[i] != i for i in eachindex(p.d))
|
||||||
id = Symbol("σ", "₍", [subscriptify(i) for i in p.d]..., "₎")
|
id = Symbol(:σ, "₍", join([subscriptify(i) for i in p.d],""), "₎")
|
||||||
return AutSymbol(id, 1, PermAut(p))
|
return AutSymbol(id, 1, PermAut(p))
|
||||||
end
|
end
|
||||||
return id_autsymbol()
|
return id_autsymbol()
|
||||||
@ -78,9 +84,8 @@ end
|
|||||||
σ(v::Generic.Perm, pow::Integer=1) = AutSymbol(v, pow)
|
σ(v::Generic.Perm, pow::Integer=1) = AutSymbol(v, pow)
|
||||||
|
|
||||||
function change_pow(s::AutSymbol, n::Integer)
|
function change_pow(s::AutSymbol, n::Integer)
|
||||||
if n == zero(n)
|
iszero(n) && id_autsymbol()
|
||||||
return id_autsymbol()
|
|
||||||
end
|
|
||||||
symbol = s.fn
|
symbol = s.fn
|
||||||
if symbol isa FlipAut
|
if symbol isa FlipAut
|
||||||
return flip(symbol.i, n)
|
return flip(symbol.i, n)
|
||||||
@ -162,14 +167,12 @@ end
|
|||||||
#
|
#
|
||||||
|
|
||||||
function (ϱ::RTransvect)(v, pow::Integer=1)
|
function (ϱ::RTransvect)(v, pow::Integer=1)
|
||||||
append!(v[ϱ.i], v[ϱ.j]^pow)
|
rmul!(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)
|
||||||
prepend!(v[λ.i], v[λ.j]^pow)
|
lmul!(v[λ.i], v[λ.j]^pow)
|
||||||
freereduce!(v[λ.i])
|
|
||||||
return v
|
return v
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -217,41 +220,60 @@ evaluate(f::Automorphism) = f(domain(parent(f)))
|
|||||||
# hashing && equality
|
# hashing && equality
|
||||||
#
|
#
|
||||||
|
|
||||||
function hash_internal(g::Automorphism, images = freereduce!.(evaluate(g)),
|
function hash_internal(
|
||||||
h::UInt = 0x7d28276b01874b19) # hash(Automorphism)
|
g::Automorphism,
|
||||||
return hash(images, hash(parent(g), h))
|
h::UInt = 0x7d28276b01874b19; # hash(Automorphism)
|
||||||
|
# alternatively: 0xcbf29ce484222325 from FNV-1a algorithm
|
||||||
|
images = compute_images(g),
|
||||||
|
prime = 0x00000100000001b3, # prime from FNV-1a algorithm
|
||||||
|
)
|
||||||
|
return foldl((h,x) -> hash(x, h)*prime, images, init = hash(parent(g), h))
|
||||||
end
|
end
|
||||||
|
|
||||||
function compute_images(g::Automorphism)
|
function compute_images(g::Automorphism)
|
||||||
images = reduce!.(evaluate(g))
|
images = evaluate(g)
|
||||||
savehash!(g, hash_internal(g, images))
|
for im in images
|
||||||
unsetmodified!(g)
|
reduce!(im)
|
||||||
|
end
|
||||||
return images
|
return images
|
||||||
end
|
end
|
||||||
|
|
||||||
function (==)(g::Automorphism{N}, h::Automorphism{N}) where N
|
function (==)(g::Automorphism{N}, h::Automorphism{N}) where N
|
||||||
img_c, imh_c = false, false
|
syllables(g) == syllables(h) && return true
|
||||||
|
img_computed, imh_computed = false, false
|
||||||
|
|
||||||
if ismodified(g)
|
if ismodified(g)
|
||||||
img = compute_images(g)
|
img = compute_images(g) # sets modified bit
|
||||||
img_c = true
|
hash(g, images=img)
|
||||||
|
img_computed = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if ismodified(h)
|
if ismodified(h)
|
||||||
imh = compute_images(h)
|
imh = compute_images(h) # sets modified bit
|
||||||
imh_c = true
|
hash(h, images=imh)
|
||||||
|
imh_computed = true
|
||||||
end
|
end
|
||||||
|
|
||||||
@assert !ismodified(g) && !ismodified(h)
|
@assert !ismodified(g) && !ismodified(h)
|
||||||
# cheap
|
# cheap
|
||||||
hash(g) != hash(h) && return false # hashes differ, so images must have differed as well
|
# if hashes differ, images must have differed as well
|
||||||
# equal elements, or possibly hash conflict
|
hash(g) != hash(h) && return false
|
||||||
if !img_c
|
|
||||||
img = compute_images(g)
|
# hashes equal, hence either equal elements, or a hash conflict
|
||||||
|
begin
|
||||||
|
if !img_computed
|
||||||
|
img_task = Threads.@spawn img = compute_images(g)
|
||||||
|
# img = compute_images(g)
|
||||||
end
|
end
|
||||||
if !imh_c
|
if !imh_computed
|
||||||
imh = compute_images(h)
|
imh_task = Threads.@spawn imh = compute_images(h)
|
||||||
|
# imh = compute_images(h)
|
||||||
end
|
end
|
||||||
|
!img_computed && fetch(img_task)
|
||||||
|
!imh_computed && fetch(imh_task)
|
||||||
|
end
|
||||||
|
|
||||||
|
img != imh && @warn "hash collision in == :" g h
|
||||||
return img == imh
|
return img == imh
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@ export DirectPowerGroup, DirectPowerGroupElem
|
|||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
DirectPowerGroup(G::Group, n::Int) <: Group
|
DirectPowerGroup(G::Group, n::Int) <: Group
|
||||||
Implements `n`-fold direct product of `G`. The group operation is
|
Return `n`-fold direct product of `G`. The group operation is
|
||||||
`*` distributed component-wise, with component-wise identity as neutral element.
|
`*` distributed component-wise, with component-wise identity as neutral element.
|
||||||
"""
|
"""
|
||||||
struct DirectPowerGroup{N, T<:Group} <: Group
|
struct DirectPowerGroup{N, T<:Group} <: Group
|
||||||
@ -70,11 +70,11 @@ Base.getindex(g::DirectPowerGroupElem, i::Int) = g.elts[i]
|
|||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
(G::DirectPowerGroup)(a::Vector, check::Bool=true)
|
(G::DirectPowerGroup)(a::Vector, check::Bool=true)
|
||||||
> Constructs element of the $n$-fold direct product group `G` by coercing each
|
Constructs element of the `n`-fold direct product group `G` by coercing each
|
||||||
> element of vector `a` to `G.group`. If `check` flag is set to `false` neither
|
element of vector `a` to `G.group`. If `check` flag is set to `false` neither
|
||||||
> check on the correctness nor coercion is performed.
|
check on the correctness nor coercion is performed.
|
||||||
"""
|
"""
|
||||||
function (G::DirectPowerGroup{N})(a::Vector, check::Bool=true) where N
|
function (G::DirectPowerGroup{N})(a::Vector, check::Bool=true) where N
|
||||||
if check
|
if check
|
||||||
@ -131,20 +131,12 @@ end
|
|||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
@doc doc"""
|
|
||||||
==(g::DirectPowerGroup, h::DirectPowerGroup)
|
|
||||||
> Checks if two direct product groups are the same.
|
|
||||||
"""
|
|
||||||
function (==)(G::DirectPowerGroup{N}, H::DirectPowerGroup{M}) where {N,M}
|
function (==)(G::DirectPowerGroup{N}, H::DirectPowerGroup{M}) where {N,M}
|
||||||
N == M || return false
|
N == M || return false
|
||||||
G.group == H.group || return false
|
G.group == H.group || return false
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc doc"""
|
|
||||||
==(g::DirectPowerGroupElem, h::DirectPowerGroupElem)
|
|
||||||
> Checks if two direct product group elements are the same.
|
|
||||||
"""
|
|
||||||
(==)(g::DirectPowerGroupElem, h::DirectPowerGroupElem) = g.elts == h.elts
|
(==)(g::DirectPowerGroupElem, h::DirectPowerGroupElem) = g.elts == h.elts
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@ -153,11 +145,6 @@ end
|
|||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
@doc doc"""
|
|
||||||
*(g::DirectPowerGroupElem, h::DirectPowerGroupElem)
|
|
||||||
> Return the direct-product group operation of elements, i.e. component-wise
|
|
||||||
> operation as defined by `operations` field of the parent object.
|
|
||||||
"""
|
|
||||||
function *(g::DirectPowerGroupElem{N}, h::DirectPowerGroupElem{N}, check::Bool=true) where N
|
function *(g::DirectPowerGroupElem{N}, h::DirectPowerGroupElem{N}, check::Bool=true) where N
|
||||||
if check
|
if check
|
||||||
parent(g) == parent(h) || throw(DomainError(
|
parent(g) == parent(h) || throw(DomainError(
|
||||||
@ -168,10 +155,6 @@ end
|
|||||||
|
|
||||||
^(g::DirectPowerGroupElem, n::Integer) = Base.power_by_squaring(g, n)
|
^(g::DirectPowerGroupElem, n::Integer) = Base.power_by_squaring(g, n)
|
||||||
|
|
||||||
@doc doc"""
|
|
||||||
inv(g::DirectPowerGroupElem)
|
|
||||||
> Return the inverse of the given element in the direct product group.
|
|
||||||
"""
|
|
||||||
function inv(g::DirectPowerGroupElem{N}) where {N}
|
function inv(g::DirectPowerGroupElem{N}) where {N}
|
||||||
return DirectPowerGroupElem(ntuple(i-> inv(g.elts[i]), N))
|
return DirectPowerGroupElem(ntuple(i-> inv(g.elts[i]), N))
|
||||||
end
|
end
|
||||||
|
120
src/Groups.jl
120
src/Groups.jl
@ -11,7 +11,7 @@ import Base: findfirst, findnext, findlast, findprev, replace
|
|||||||
import Base: deepcopy_internal
|
import Base: deepcopy_internal
|
||||||
|
|
||||||
using LinearAlgebra
|
using LinearAlgebra
|
||||||
using Markdown
|
using ThreadsX
|
||||||
|
|
||||||
export gens, FreeGroup, Aut, SAut
|
export gens, FreeGroup, Aut, SAut
|
||||||
|
|
||||||
@ -32,17 +32,11 @@ include("findreplace.jl")
|
|||||||
include("DirectPower.jl")
|
include("DirectPower.jl")
|
||||||
include("WreathProducts.jl")
|
include("WreathProducts.jl")
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# String I/O
|
# String I/O
|
||||||
#
|
#
|
||||||
|
|
||||||
@doc doc"""
|
|
||||||
show(io::IO, W::GWord)
|
|
||||||
> The actual string produced by show depends on the eltype of `W.symbols`.
|
|
||||||
|
|
||||||
"""
|
|
||||||
function Base.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)")
|
||||||
@ -64,43 +58,109 @@ end
|
|||||||
# Misc
|
# Misc
|
||||||
#
|
#
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
gens(G::AbstractFPGroups)
|
gens(G::AbstractFPGroups)
|
||||||
> returns vector of generators of `G`, as its elements.
|
Return vector of generators of `G`, as its elements.
|
||||||
"""
|
"""
|
||||||
AbstractAlgebra.gens(G::AbstractFPGroup) = G.(G.gens)
|
AbstractAlgebra.gens(G::AbstractFPGroup) = G.(G.gens)
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
metric_ball(S::Vector{GroupElem}, center=Id; radius=2, op=*)
|
wlmetric_ball(S::AbstractVector{<:GroupElem}
|
||||||
|
[, center=one(first(S)); radius=2, op=*])
|
||||||
Compute metric ball as a list of elements of non-decreasing length, given the
|
Compute metric ball as a list of elements of non-decreasing length, given the
|
||||||
word-length metric on group generated by `S`. The ball is centered at `center`
|
word-length metric on the group generated by `S`. The ball is centered at `center`
|
||||||
(by default: the identity element). `radius` and `op` keywords specify the
|
(by default: the identity element). `radius` and `op` keywords specify the
|
||||||
radius and multiplication operation to be used.
|
radius and multiplication operation to be used.
|
||||||
"""
|
"""
|
||||||
function generate_balls(S::AbstractVector{T}, center::T=one(first(S));
|
|
||||||
radius=2, op=*) where T<:Union{GroupElem, NCRingElem}
|
function wlmetric_ball_serial(
|
||||||
sizes = Int[]
|
S::AbstractVector{T};
|
||||||
B = [one(first(S))]
|
radius = 2,
|
||||||
for i in 1:radius
|
op = *,
|
||||||
BB = [op(i,j) for (i,j) in Base.product(B,S)]
|
) where {T<:Union{GroupElem,NCRingElem}}
|
||||||
B = unique([B; vec(BB)])
|
old = unique!([one(first(S)), S...])
|
||||||
push!(sizes, length(B))
|
sizes = [1, length(old)]
|
||||||
|
for i = 2:radius
|
||||||
|
new = collect(
|
||||||
|
op(i, j)
|
||||||
|
for (i, j) in Base.product(@view(old[sizes[end-1]:end]), S)
|
||||||
|
)
|
||||||
|
append!(old, new)
|
||||||
|
resize!(new, 0)
|
||||||
|
old = unique!(old)
|
||||||
|
push!(sizes, length(old))
|
||||||
end
|
end
|
||||||
isone(center) && return B, sizes
|
return old, sizes[2:end]
|
||||||
return c.*B, sizes
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc doc"""
|
function wlmetric_ball_thr(
|
||||||
image(A::GWord, homomorphism; kwargs...)
|
S::AbstractVector{T};
|
||||||
Evaluate homomorphism `homomorphism` on a GWord `A`.
|
radius = 2,
|
||||||
`homomorphism` needs implement
|
op = *,
|
||||||
> `hom(s; kwargs...)`,
|
) where {T<:Union{GroupElem,NCRingElem}}
|
||||||
where `hom(;kwargs...)` evaluates the value at the identity element.
|
old = unique!([one(first(S)), S...])
|
||||||
|
sizes = [1, length(old)]
|
||||||
|
for r = 2:radius
|
||||||
|
begin
|
||||||
|
new = ThreadsX.collect(
|
||||||
|
op(o, s) for o in @view(old[sizes[end-1]:end]) for s in S
|
||||||
|
)
|
||||||
|
ThreadsX.foreach(hash, new)
|
||||||
|
end
|
||||||
|
append!(old, new)
|
||||||
|
resize!(new, 0)
|
||||||
|
old = ThreadsX.unique(old)
|
||||||
|
push!(sizes, length(old))
|
||||||
|
end
|
||||||
|
return old, sizes[2:end]
|
||||||
|
end
|
||||||
|
|
||||||
|
function wlmetric_ball_serial(
|
||||||
|
S::AbstractVector{T},
|
||||||
|
center::T;
|
||||||
|
radius = 2,
|
||||||
|
op = *,
|
||||||
|
) where {T<:Union{GroupElem,NCRingElem}}
|
||||||
|
E, sizes = wlmetric_ball_serial(S, radius = radius, op = op)
|
||||||
|
isone(center) && return E, sizes
|
||||||
|
return c .* E, sizes
|
||||||
|
end
|
||||||
|
|
||||||
|
function wlmetric_ball_thr(
|
||||||
|
S::AbstractVector{T},
|
||||||
|
center::T;
|
||||||
|
radius = 2,
|
||||||
|
op = *,
|
||||||
|
) where {T<:Union{GroupElem,NCRingElem}}
|
||||||
|
E, sizes = wlmetric_ball_thr(S, radius = radius, op = op)
|
||||||
|
isone(center) && return E, sizes
|
||||||
|
return c .* E, sizes
|
||||||
|
end
|
||||||
|
|
||||||
|
function wlmetric_ball(
|
||||||
|
S::AbstractVector{T},
|
||||||
|
center::T = one(first(S));
|
||||||
|
radius = 2,
|
||||||
|
op = *,
|
||||||
|
threading = true,
|
||||||
|
) where {T<:Union{GroupElem,NCRingElem}}
|
||||||
|
threading && return wlmetric_ball_thr(S, center, radius = radius, op = op)
|
||||||
|
return return wlmetric_ball_serial(S, center, radius = radius, op = op)
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
image(w::GWord, homomorphism; kwargs...)
|
||||||
|
Evaluate homomorphism `homomorphism` on a group word (element) `w`.
|
||||||
|
`homomorphism` needs to implement
|
||||||
|
> `hom(w; kwargs...)`,
|
||||||
|
where `hom(;kwargs...)` returns the value at the identity element.
|
||||||
"""
|
"""
|
||||||
function image(w::GWord, hom; kwargs...)
|
function image(w::GWord, hom; kwargs...)
|
||||||
return reduce(*,
|
return reduce(
|
||||||
|
*,
|
||||||
(hom(s; kwargs...) for s in syllables(w)),
|
(hom(s; kwargs...) for s in syllables(w)),
|
||||||
init = hom(;kwargs...))
|
init = hom(; kwargs...),
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
end # of module Groups
|
end # of module Groups
|
||||||
|
@ -8,18 +8,17 @@ import AbstractAlgebra: AbstractPermutationGroup, AbstractPerm
|
|||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
WreathProduct(N, P) <: Group
|
WreathProduct(N, P) <: Group
|
||||||
> Implements Wreath product of a group `N` by permutation group $P = S_n$,
|
Return the wreath product of a group `N` by permutation group `P`, usually
|
||||||
> usually written as $N \wr P$.
|
written as `N ≀ P`. The multiplication inside wreath product is defined as
|
||||||
> The multiplication inside wreath product is defined as
|
> `(n, σ) * (m, τ) = (n*σ(m), στ)`
|
||||||
> > `(n, σ) * (m, τ) = (n*σ(m), στ)`
|
where `σ(m)` denotes the action (from the right) of the permutation group on
|
||||||
> where `σ(m)` denotes the action (from the right) of the permutation group on
|
`n-tuples` of elements from `N`
|
||||||
> `n-tuples` of elements from `N`
|
|
||||||
|
|
||||||
# Arguments:
|
# Arguments:
|
||||||
* `N::Group` : the single factor of the group $N$
|
* `N::Group` : the single factor of the `DirectPower` group `N`
|
||||||
* `P::Generic.PermGroup` : full `PermutationGroup`
|
* `P::AbstractPermutationGroup` acting on `DirectPower` of `N`
|
||||||
"""
|
"""
|
||||||
struct WreathProduct{N, T<:Group, PG<:AbstractPermutationGroup} <: Group
|
struct WreathProduct{N, T<:Group, PG<:AbstractPermutationGroup} <: Group
|
||||||
N::DirectPowerGroup{N, T}
|
N::DirectPowerGroup{N, T}
|
||||||
@ -71,25 +70,25 @@ function (G::WreathProduct{N})(g::WreathProductElem{N}) where {N}
|
|||||||
return WreathProductElem(n, p)
|
return WreathProductElem(n, p)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
(G::WreathProduct)(n::DirectPowerGroupElem, p::Generic.Perm)
|
(G::WreathProduct)(n::DirectPowerGroupElem, p::Generic.Perm)
|
||||||
> Creates an element of wreath product `G` by coercing `n` and `p` to `G.N` and
|
Create an element of wreath product `G` by coercing `n` and `p` to `G.N` and
|
||||||
> `G.P`, respectively.
|
`G.P`, respectively.
|
||||||
"""
|
"""
|
||||||
(G::WreathProduct)(n::DirectPowerGroupElem, p::Generic.Perm) = WreathProductElem(n,p)
|
(G::WreathProduct)(n::DirectPowerGroupElem, p::Generic.Perm) = WreathProductElem(n,p)
|
||||||
|
|
||||||
Base.one(G::WreathProduct) = WreathProductElem(one(G.N), one(G.P), false)
|
Base.one(G::WreathProduct) = WreathProductElem(one(G.N), one(G.P), false)
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
(G::WreathProduct)(p::Generic.Perm)
|
(G::WreathProduct)(p::Generic.Perm)
|
||||||
> Returns the image of permutation `p` in `G` via embedding `p -> (id,p)`.
|
Return the image of permutation `p` in `G` via embedding `p → (id,p)`.
|
||||||
"""
|
"""
|
||||||
(G::WreathProduct)(p::Generic.Perm) = G(one(G.N), p)
|
(G::WreathProduct)(p::Generic.Perm) = G(one(G.N), p)
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
(G::WreathProduct)(n::DirectPowerGroupElem)
|
(G::WreathProduct)(n::DirectPowerGroupElem)
|
||||||
> Returns the image of `n` in `G` via embedding `n -> (n,())`. This is the
|
Return the image of `n` in `G` via embedding `n → (n, ())`. This is the
|
||||||
> embedding that makes the sequence `1 -> N -> G -> P -> 1` exact.
|
embedding that makes the sequence `1 → N → G → P → 1` exact.
|
||||||
"""
|
"""
|
||||||
(G::WreathProduct)(n::DirectPowerGroupElem) = G(n, one(G.P))
|
(G::WreathProduct)(n::DirectPowerGroupElem) = G(n, one(G.P))
|
||||||
|
|
||||||
@ -149,14 +148,12 @@ end
|
|||||||
|
|
||||||
(p::Generic.Perm)(n::DirectPowerGroupElem) = DirectPowerGroupElem(n.elts[p.d])
|
(p::Generic.Perm)(n::DirectPowerGroupElem) = DirectPowerGroupElem(n.elts[p.d])
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
*(g::WreathProductElem, h::WreathProductElem)
|
*(g::WreathProductElem, h::WreathProductElem)
|
||||||
> Return the wreath product group operation of elements, i.e.
|
Return the group operation of wreath product elements, i.e.
|
||||||
>
|
|
||||||
> `g*h = (g.n*g.p(h.n), g.p*h.p)`,
|
> `g*h = (g.n*g.p(h.n), g.p*h.p)`,
|
||||||
>
|
where `g.p(h.n)` denotes the action of `g.p::Generic.Perm` on
|
||||||
> where `g.p(h.n)` denotes the action of `g.p::Generic.Perm` on
|
`h.n::DirectPowerGroupElem` via standard permutation of coordinates.
|
||||||
> `h.n::DirectPowerGroupElem` via standard permutation of coordinates.
|
|
||||||
"""
|
"""
|
||||||
function *(g::WreathProductElem, h::WreathProductElem)
|
function *(g::WreathProductElem, h::WreathProductElem)
|
||||||
return WreathProductElem(g.n*g.p(h.n), g.p*h.p, false)
|
return WreathProductElem(g.n*g.p(h.n), g.p*h.p, false)
|
||||||
@ -164,9 +161,9 @@ end
|
|||||||
|
|
||||||
^(g::WreathProductElem, n::Integer) = Base.power_by_squaring(g, n)
|
^(g::WreathProductElem, n::Integer) = Base.power_by_squaring(g, n)
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
inv(g::WreathProductElem)
|
inv(g::WreathProductElem)
|
||||||
> Returns the inverse of element of a wreath product, according to the formula
|
Return the inverse of element of a wreath product, according to the formula
|
||||||
> `g^-1 = (g.n, g.p)^-1 = (g.p^-1(g.n^-1), g.p^-1)`.
|
> `g^-1 = (g.n, g.p)^-1 = (g.p^-1(g.n^-1), g.p^-1)`.
|
||||||
"""
|
"""
|
||||||
function inv(g::WreathProductElem)
|
function inv(g::WreathProductElem)
|
||||||
|
@ -4,14 +4,18 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
function freereduce!(::Type{Bool}, w::GWord)
|
function freereduce!(::Type{Bool}, w::GWord)
|
||||||
|
if syllablelength(w) == 1
|
||||||
|
filter!(!isone, syllables(w))
|
||||||
|
return syllablelength(w) == 1
|
||||||
|
end
|
||||||
|
|
||||||
reduced = true
|
reduced = true
|
||||||
for i in 1:syllablelength(w)-1
|
@inbounds for i in 1:syllablelength(w)-1
|
||||||
s, ns = syllables(w)[i], syllables(w)[i+1]
|
s, ns = syllables(w)[i], syllables(w)[i+1]
|
||||||
if isone(s)
|
if isone(s)
|
||||||
continue
|
continue
|
||||||
elseif s.id == ns.id
|
elseif s.id === ns.id
|
||||||
reduced = false
|
reduced = false
|
||||||
setmodified!(w)
|
|
||||||
p1 = s.pow
|
p1 = s.pow
|
||||||
p2 = ns.pow
|
p2 = ns.pow
|
||||||
|
|
||||||
@ -19,7 +23,10 @@ function freereduce!(::Type{Bool}, w::GWord)
|
|||||||
syllables(w)[i] = change_pow(s, 0)
|
syllables(w)[i] = change_pow(s, 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if !reduced
|
||||||
filter!(!isone, syllables(w))
|
filter!(!isone, syllables(w))
|
||||||
|
setmodified!(w)
|
||||||
|
end
|
||||||
return reduced
|
return reduced
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -33,11 +40,10 @@ end
|
|||||||
|
|
||||||
reduce!(w::GWord) = freereduce!(w)
|
reduce!(w::GWord) = freereduce!(w)
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
reduce(w::GWord)
|
reduce(w::GWord)
|
||||||
> performs reduction/simplification of a group element (word in generators).
|
performs reduction/simplification of a group element (word in generators).
|
||||||
> The default reduction is the free group reduction
|
The default reduction is the reduction in the free group reduction.
|
||||||
> More specific procedures should be dispatched on `GWord`s type parameter.
|
More specific procedures should be dispatched on `GWord`s type parameter.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
reduce(w::GWord) = reduce!(deepcopy(w))
|
reduce(w::GWord) = reduce!(deepcopy(w))
|
||||||
|
@ -9,9 +9,9 @@ function hash_internal(W::GWord)
|
|||||||
return hash(syllables(W), hash(typeof(W), h))
|
return hash(syllables(W), hash(typeof(W), h))
|
||||||
end
|
end
|
||||||
|
|
||||||
function hash(W::GWord, h::UInt)
|
function hash(W::GWord, h::UInt=UInt(0); kwargs...)
|
||||||
if ismodified(W)
|
if ismodified(W)
|
||||||
savehash!(W, hash_internal(W))
|
savehash!(W, hash_internal(W; kwargs...))
|
||||||
unsetmodified!(W)
|
unsetmodified!(W)
|
||||||
end
|
end
|
||||||
return xor(savedhash(W), h)
|
return xor(savedhash(W), h)
|
||||||
|
36
src/types.jl
36
src/types.jl
@ -1,31 +1,31 @@
|
|||||||
abstract type AbstractFPGroup <: Group end
|
abstract type AbstractFPGroup <: Group end
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
::GSymbol
|
::GSymbol
|
||||||
> Represents a syllable.
|
Represents a syllable. Abstract type which all group symbols of
|
||||||
> Abstract type which all group symbols of AbstractFPGroups should subtype. Each
|
`AbstractFPGroups` should subtype. Each concrete subtype should implement fields:
|
||||||
> concrete subtype should implement fields:
|
* `id` which is the `Symbol` representation/identification of a symbol
|
||||||
> * `id` which is the `Symbol` representation/identification of a symbol
|
* `pow` which is the (multiplicative) exponent of a symbol.
|
||||||
> * `pow` which is the (multiplicative) exponent of a symbol.
|
|
||||||
"""
|
"""
|
||||||
abstract type GSymbol end
|
abstract type GSymbol end
|
||||||
|
|
||||||
abstract type GWord{T<:GSymbol} <: GroupElem end
|
abstract type GWord{T<:GSymbol} <: GroupElem end
|
||||||
|
|
||||||
@doc doc"""
|
"""
|
||||||
W::GroupWord{T} <: GWord{T<:GSymbol} <:GroupElem
|
W::GroupWord{T} <: GWord{T<:GSymbol} <:GroupElem
|
||||||
> Basic representation of element of a finitely presented group. `W.symbols`
|
Basic representation of element of a finitely presented group.
|
||||||
> fieldname contains particular group symbols which multiplied constitute a
|
* `syllables(W)` return particular group syllables which multiplied constitute `W`
|
||||||
> group element, i.e. a word in generators.
|
group as a word in generators.
|
||||||
> As reduction (inside group) of such word may be time consuming we provide
|
* `parent(W)` return the parent group.
|
||||||
> `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`.
|
|
||||||
|
|
||||||
|
As the reduction (inside the parent group) of word to normal form may be time
|
||||||
|
consuming, we provide a shortcut that is useful in practice:
|
||||||
|
`savehash!(W, h)` and `ismodified(W)` functions.
|
||||||
|
When computing `hash(W)`, a reduction to normal form is performed and a
|
||||||
|
persistent hash is stored inside `W`, setting `ismodified(W)` flag to `false`.
|
||||||
|
This hash can be accessed by `savedhash(W)`.
|
||||||
|
Future comparisons of `W` try not to perform reduction and use the stored hash as shortcut. Only when hashes collide reduction is performed. Whenever word `W` is
|
||||||
|
changed, `ismodified(W)` returns `false` and stored hash is invalidated.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
mutable struct GroupWord{T} <: GWord{T}
|
mutable struct GroupWord{T} <: GWord{T}
|
||||||
|
@ -13,7 +13,7 @@ using LinearAlgebra
|
|||||||
s = one(M); s[1,3] = 2; s[3,2] = -1;
|
s = one(M); s[1,3] = 2; s[3,2] = -1;
|
||||||
|
|
||||||
S = [w,r,s]; S = unique([S; inv.(S)]);
|
S = [w,r,s]; S = unique([S; inv.(S)]);
|
||||||
_, sizes = Groups.generate_balls(S, radius=4);
|
_, sizes = Groups.wlmetric_ball(S, radius=4);
|
||||||
@test sizes == [7, 33, 141, 561]
|
@test sizes == [7, 33, 141, 561]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user