1
0
mirror of https://github.com/kalmarek/Groups.jl.git synced 2024-11-19 06:30:29 +01:00

add Constructions module with Direct/Wreath product

This commit is contained in:
Marek Kaluba 2022-04-02 14:43:52 +02:00
parent dd6588f018
commit e78520f90b
No known key found for this signature in database
GPG Key ID: 8BF1A3855328FC15
12 changed files with 424 additions and 12 deletions

View File

@ -8,6 +8,7 @@ Folds = "41a02a25-b8f0-4f67-bc48-60067656b558"
GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120"
KnuthBendix = "c2604015-7b3d-4a30-8a26-9074551ec60a" KnuthBendix = "c2604015-7b3d-4a30-8a26-9074551ec60a"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
[compat] [compat]

View File

@ -15,6 +15,10 @@ export Alphabet, AutomorphismGroup, FreeGroup, FreeGroup, FPGroup, FPGroupElemen
export alphabet, evaluate, word, gens export alphabet, evaluate, word, gens
# general constructions
include(joinpath("constructions", "constructions.jl"))
using .Constructions
include("types.jl") include("types.jl")
include("hashing.jl") include("hashing.jl")
include("normalform.jl") include("normalform.jl")

View File

@ -0,0 +1,10 @@
module Constructions
using GroupsCore
using Random
include("direct_product.jl")
include("direct_power.jl")
include("wreath_product.jl")
end # of module Constructions

View File

@ -0,0 +1,112 @@
struct DirectPower{Gr,N,GEl<:GroupsCore.GroupElement} <: GroupsCore.Group
group::Gr
function DirectPower{N}(G::GroupsCore.Group) where {N}
@assert N > 1
return new{typeof(G),N,eltype(G)}(G)
end
end
struct DirectPowerElement{GEl,N,Gr<:GroupsCore.Group} <: GroupsCore.GroupElement
elts::NTuple{N,GEl}
parent::DirectPower{Gr,N,GEl}
end
DirectPowerElement(
elts::AbstractVector{<:GroupsCore.GroupElement},
G::DirectPower,
) = DirectPowerElement(ntuple(i -> elts[i], _nfold(G)), G)
_nfold(::DirectPower{Gr,N}) where {Gr,N} = N
Base.one(G::DirectPower) =
DirectPowerElement(ntuple(_ -> one(G.group), _nfold(G)), G)
Base.eltype(::Type{<:DirectPower{Gr,N,GEl}}) where {Gr,N,GEl} =
DirectPowerElement{GEl,N,Gr}
function Base.iterate(G::DirectPower)
itr = Iterators.ProductIterator(ntuple(i -> G.group, _nfold(G)))
res = iterate(itr)
@assert res !== nothing
elt = DirectPowerElement(first(res), G)
return elt, (iterator = itr, state = last(res))
end
function Base.iterate(G::DirectPower, state)
itr, st = state.iterator, state.state
res = iterate(itr, st)
res === nothing && return nothing
elt = DirectPowerElement(first(res), G)
return elt, (iterator = itr, state = last(res))
end
function Base.IteratorSize(::Type{<:DirectPower{Gr,N}}) where {Gr,N}
Base.IteratorSize(Gr) isa Base.HasLength && return Base.HasShape{N}()
Base.IteratorSize(Gr) isa Base.HasShape && return Base.HasShape{N}()
return Base.IteratorSize(Gr)
end
Base.size(G::DirectPower) = ntuple(_ -> length(G.group), _nfold(G))
GroupsCore.order(::Type{I}, G::DirectPower) where {I<:Integer} =
convert(I, order(I, G.group)^_nfold(G))
GroupsCore.ngens(G::DirectPower) = _nfold(G)*ngens(G.group)
function GroupsCore.gens(G::DirectPower, i::Integer)
k = ngens(G.group)
ci = CartesianIndices((k, _nfold(G)))
@boundscheck checkbounds(ci, i)
r, c = Tuple(ci[i])
tup = ntuple(j -> j == c ? gens(G.group, r) : one(G.group), _nfold(G))
return DirectPowerElement(tup, G)
end
function GroupsCore.gens(G::DirectPower)
N = _nfold(G)
S = gens(G.group)
tups = [ntuple(j->(i == j ? s : one(s)), N) for i in 1:N for s in S]
return [DirectPowerElement(elts, G) for elts in tups]
end
Base.isfinite(G::DirectPower) = isfinite(G.group)
function Base.rand(
rng::Random.AbstractRNG,
rs::Random.SamplerTrivial{<:DirectPower},
)
G = rs[]
return DirectPowerElement(rand(rng, G.group, _nfold(G)), G)
end
GroupsCore.parent(g::DirectPowerElement) = g.parent
Base.:(==)(g::DirectPowerElement, h::DirectPowerElement) =
(parent(g) === parent(h) && g.elts == h.elts)
Base.hash(g::DirectPowerElement, h::UInt) = hash(g.elts, hash(parent(g), h))
Base.deepcopy_internal(g::DirectPowerElement, stackdict::IdDict) =
DirectPowerElement(Base.deepcopy_internal(g.elts, stackdict), parent(g))
Base.inv(g::DirectPowerElement) = DirectPowerElement(inv.(g.elts), parent(g))
function Base.:(*)(g::DirectPowerElement, h::DirectPowerElement)
@assert parent(g) === parent(h)
return DirectPowerElement(g.elts .* h.elts, parent(g))
end
GroupsCore.order(::Type{I}, g::DirectPowerElement) where {I<:Integer} =
convert(I, reduce(lcm, (order(I, h) for h in g.elts), init = one(I)))
Base.isone(g::DirectPowerElement) = all(isone, g.elts)
function Base.show(io::IO, G::DirectPower)
n = _nfold(G)
nn = n == 1 ? "1-st" : n == 2 ? "2-nd" : n == 3 ? "3-rd" : "$n-th"
print(io, "Direct $(nn) power of $(G.group)")
end
Base.show(io::IO, g::DirectPowerElement) =
print(io, "( ", join(g.elts, ", "), " )")

View File

@ -0,0 +1,105 @@
using Random
using GroupsCore
struct DirectProduct{Gt,Ht,GEl,HEl} <: GroupsCore.Group
first::Gt
last::Ht
function DirectProduct(G::GroupsCore.Group, H::GroupsCore.Group)
return new{typeof(G),typeof(H),eltype(G),eltype(H)}(G, H)
end
end
struct DirectProductElement{GEl,HEl,Gt,Ht} <: GroupsCore.GroupElement
elts::Tuple{GEl,HEl}
parent::DirectProduct{Gt,Ht,GEl,HEl}
end
DirectProductElement(g, h, G::DirectProduct) = DirectProduct((g, h), G)
Base.one(G::DirectProduct) =
DirectProductElement((one(G.first), one(G.last)), G)
Base.eltype(::Type{<:DirectProduct{Gt,Ht,GEl,HEl}}) where {Gt,Ht,GEl,HEl} =
DirectProductElement{GEl,HEl,Gt,Ht}
function Base.iterate(G::DirectProduct)
itr = Iterators.product(G.first, G.last)
res = iterate(itr)
@assert res !== nothing
elt = DirectProductElement(first(res), G)
return elt, (iterator = itr, state = last(res))
end
function Base.iterate(G::DirectProduct, state)
itr, st = state.iterator, state.state
res = iterate(itr, st)
res === nothing && return nothing
elt = DirectProductElement(first(res), G)
return elt, (iterator = itr, state = last(res))
end
function Base.IteratorSize(::Type{<:DirectProduct{Gt,Ht}}) where {Gt,Ht}
Gi = Base.IteratorSize(Gt)
Hi = Base.IteratorSize(Ht)
if Gi isa Base.IsInfinite || Hi isa Base.IsInfinite
return Base.IsInfinite()
elseif Gi isa Base.SizeUnknown || Hi isa Base.SizeUnknown
return Base.SizeUnknown()
else
return Base.HasShape{2}()
end
end
Base.size(G::DirectProduct) = (length(G.first), length(G.last))
GroupsCore.order(::Type{I}, G::DirectProduct) where {I<:Integer} =
convert(I, order(I, G.first) * order(I, G.last))
GroupsCore.ngens(G::DirectProduct) = ngens(G.first) + ngens(G.last)
function GroupsCore.gens(G::DirectProduct)
gens_first = [DirectProductElement((g, one(G.last)), G) for g in gens(G.first)]
gens_last = [DirectProductElement((one(G.first), g), G) for g in gens(G.last)]
return [gens_first; gens_last]
end
Base.isfinite(G::DirectProduct) = isfinite(G.first) && isfinite(G.last)
function Base.rand(
rng::Random.AbstractRNG,
rs::Random.SamplerTrivial{<:DirectProduct},
)
G = rs[]
return DirectProductElement((rand(rng, G.first), rand(rng, G.last)), G)
end
GroupsCore.parent(g::DirectProductElement) = g.parent
Base.:(==)(g::DirectProductElement, h::DirectProductElement) =
(parent(g) === parent(h) && g.elts == h.elts)
Base.hash(g::DirectProductElement, h::UInt) = hash(g.elts, hash(parent(g), h))
Base.deepcopy_internal(g::DirectProductElement, stackdict::IdDict) =
DirectProductElement(Base.deepcopy_internal(g.elts, stackdict), parent(g))
Base.inv(g::DirectProductElement) =
DirectProductElement(inv.(g.elts), parent(g))
function Base.:(*)(g::DirectProductElement, h::DirectProductElement)
@assert parent(g) === parent(h)
return DirectProductElement(g.elts .* h.elts, parent(g))
end
GroupsCore.order(::Type{I}, g::DirectProductElement) where {I<:Integer} =
convert(I, lcm(order(I, first(g.elts)), order(I, last(g.elts))))
Base.isone(g::DirectProductElement) = all(isone, g.elts)
Base.show(io::IO, G::DirectProduct) =
print(io, "Direct product of $(G.first) and $(G.last)")
Base.show(io::IO, g::DirectProductElement) =
print(io, "( $(join(g.elts, ",")) )")

View File

@ -0,0 +1,136 @@
import PermutationGroups: AbstractPermutationGroup, AbstractPerm, degree, SymmetricGroup
"""
WreathProduct(G::Group, P::AbstractPermutationGroup) <: Group
Return the wreath product of a group `G` by permutation group `P`, usually
written as `G ≀ P`.
As set `G ≀ P` is the same as `Gᵈ × P` and the group can be understood as a
semi-direct product of `P` acting on `d`-fold cartesian product of `G` by
permuting coordinates. To be more precise, the multiplication inside wreath
product is defined as
> `(n, σ) * (m, τ) = (n*(m^σ), σ*τ)`
where `m^σ` denotes the action (from the right) of the permutation `σ` on
`d`-tuples of elements from `G`.
"""
struct WreathProduct{DP<:DirectPower,PGr<:AbstractPermutationGroup} <:
GroupsCore.Group
N::DP
P::PGr
function WreathProduct(G::Group, P::AbstractPermutationGroup)
N = DirectPower{degree(P)}(G)
return new{typeof(N),typeof(P)}(N, P)
end
end
struct WreathProductElement{
DPEl<:DirectPowerElement,
PEl<:AbstractPerm,
Wr<:WreathProduct,
} <: GroupsCore.GroupElement
n::DPEl
p::PEl
parent::Wr
function WreathProductElement(
n::DirectPowerElement,
p::AbstractPerm,
W::WreathProduct,
)
new{typeof(n),typeof(p),typeof(W)}(n, p, W)
end
end
Base.one(W::WreathProduct) = WreathProductElement(one(W.N), one(W.P), W)
Base.eltype(::Type{<:WreathProduct{DP,PGr}}) where {DP,PGr} =
WreathProductElement{eltype(DP),eltype(PGr),WreathProduct{DP,PGr}}
function Base.iterate(G::WreathProduct)
itr = Iterators.product(G.N, G.P)
res = iterate(itr)
@assert res !== nothing
elt = WreathProductElement(first(res)..., G)
return elt, (iterator = itr, state = last(res))
end
function Base.iterate(G::WreathProduct, state)
itr, st = state.iterator, state.state
res = iterate(itr, st)
res === nothing && return nothing
elt = WreathProductElement(first(res)..., G)
return elt, (iterator = itr, state = last(res))
end
function Base.IteratorSize(::Type{<:WreathProduct{DP,PGr}}) where {DP,PGr}
dpI = Base.IteratorSize(DP)
pgI = Base.IteratorSize(PGr)
if dpI isa Base.IsInfinite || pgI isa Base.IsInfinite
return Base.IsInfinite()
elseif dpI isa Base.SizeUnknown || pgI isa Base.SizeUnknown
return Base.SizeUnknown()
else
return Base.HasShape{2}()
end
end
Base.size(G::WreathProduct) = (length(G.N), length(G.P))
GroupsCore.order(::Type{I}, G::WreathProduct) where {I<:Integer} =
convert(I, order(I, G.N) * order(I, G.P))
function GroupsCore.gens(G::WreathProduct)
N_gens = [WreathProductElement(n, one(G.P), G) for n in gens(G.N)]
P_gens = [WreathProductElement(one(G.N), p, G) for p in gens(G.P)]
return [N_gens; P_gens]
end
Base.isfinite(G::WreathProduct) = isfinite(G.N) && isfinite(G.P)
function Base.rand(
rng::Random.AbstractRNG,
rs::Random.SamplerTrivial{<:WreathProduct},
)
G = rs[]
return WreathProductElement(rand(rng, G.N), rand(rng, G.P), G)
end
GroupsCore.parent(g::WreathProductElement) = g.parent
Base.:(==)(g::WreathProductElement, h::WreathProductElement) =
parent(g) === parent(h) && g.n == h.n && g.p == h.p
Base.hash(g::WreathProductElement, h::UInt) =
hash(g.n, hash(g.p, hash(g.parent, h)))
function Base.deepcopy_internal(g::WreathProductElement, stackdict::IdDict)
return WreathProductElement(
Base.deepcopy_internal(g.n, stackdict),
Base.deepcopy_internal(g.p, stackdict),
parent(g),
)
end
_act(p::AbstractPerm, n::DirectPowerElement) =
DirectPowerElement(n.elts^p, parent(n))
function Base.inv(g::WreathProductElement)
pinv = inv(g.p)
return WreathProductElement(_act(pinv, inv(g.n)), pinv, parent(g))
end
function Base.:(*)(g::WreathProductElement, h::WreathProductElement)
@assert parent(g) === parent(h)
return WreathProductElement(g.n * _act(g.p, h.n), g.p * h.p, parent(g))
end
Base.isone(g::WreathProductElement) = isone(g.n) && isone(g.p)
Base.show(io::IO, G::WreathProduct) =
print(io, "Wreath product of $(G.N.group) by $(G.P)")
Base.show(io::IO, g::WreathProductElement) = print(io, "( $(g.n)$(g.p) )")
Base.copy(g::WreathProductElement) = WreathProductElement(g.n, g.p, parent(g))

View File

@ -1,11 +1,13 @@
module MatrixGroups module MatrixGroups
using StaticArrays
using GroupsCore using GroupsCore
using Groups using Groups
using KnuthBendix using KnuthBendix
using LinearAlgebra # Identity matrix import LinearAlgebra # Identity matrix
using Random # GroupsCore rand import Random # GroupsCore rand
export SpecialLinearGroup, SymplecticGroup export SpecialLinearGroup, SymplecticGroup

View File

@ -1,7 +1,8 @@
abstract type MatrixGroup{N, T} <: Groups.AbstractFPGroup end abstract type MatrixGroup{N, T} <: Groups.AbstractFPGroup end
const MatrixGroupElement{N, T} = Groups.AbstractFPGroupElement{<:MatrixGroup{N, T}} const MatrixGroupElement{N, T} = Groups.AbstractFPGroupElement{<:MatrixGroup{N, T}}
Base.isone(g::MatrixGroupElement{N, T}) where {N, T} = matrix_repr(g) == I Base.isone(g::MatrixGroupElement{N, T}) where {N, T} =
isone(word(g)) || matrix_repr(g) == LinearAlgebra.I
function Base.:(==)(m1::M1, m2::M2) where {M1<:MatrixGroupElement, M2<:MatrixGroupElement} function Base.:(==)(m1::M1, m2::M2) where {M1<:MatrixGroupElement, M2<:MatrixGroupElement}
parent(m1) === parent(m2) || return false parent(m1) === parent(m2) || return false
@ -26,7 +27,9 @@ Base.getindex(sl::MatrixGroupElement, i, j) = matrix_repr(sl)[i,j]
# Base.iterate(sl::MatrixGroupElement, state) = iterate(sl.elts, state) # Base.iterate(sl::MatrixGroupElement, state) = iterate(sl.elts, state)
function matrix_repr(m::MatrixGroupElement{N, T}) where {N, T} function matrix_repr(m::MatrixGroupElement{N, T}) where {N, T}
isempty(word(m)) && return StaticArrays.SMatrix{N, N, T}(I) if isone(word(m))
return StaticArrays.SMatrix{N, N, T}(LinearAlgebra.I)
end
A = alphabet(parent(m)) A = alphabet(parent(m))
return prod(matrix_repr(A[l]) for l in word(m)) return prod(matrix_repr(A[l]) for l in word(m))
end end

View File

@ -1,6 +1,3 @@
using Groups
using StaticArrays
struct ElementaryMatrix{N, T} <: Groups.GSymbol struct ElementaryMatrix{N, T} <: Groups.GSymbol
i::Int i::Int
j::Int j::Int
@ -24,7 +21,7 @@ Base.inv(e::ElementaryMatrix{N}) where N =
ElementaryMatrix{N}(e.i, e.j, -e.val) ElementaryMatrix{N}(e.i, e.j, -e.val)
function matrix_repr(e::ElementaryMatrix{N, T}) where {N, T} function matrix_repr(e::ElementaryMatrix{N, T}) where {N, T}
m = StaticArrays.MMatrix{N, N, T}(I) m = StaticArrays.MMatrix{N, N, T}(LinearAlgebra.I)
m[e.i, e.j] = e.val m[e.i, e.j] = e.val
x = StaticArrays.SMatrix{N, N}(m) x = StaticArrays.SMatrix{N, N}(m)
return x return x

View File

@ -1,6 +1,3 @@
using Groups
using StaticArrays
struct ElementarySymplectic{N, T} <: Groups.GSymbol struct ElementarySymplectic{N, T} <: Groups.GSymbol
symbol::Symbol symbol::Symbol
i::Int i::Int
@ -69,7 +66,7 @@ Base.inv(s::ElementarySymplectic{N}) where N =
function matrix_repr(s::ElementarySymplectic{N, T}) where {N, T} function matrix_repr(s::ElementarySymplectic{N, T}) where {N, T}
@assert iseven(N) @assert iseven(N)
n = div(N, 2) n = div(N, 2)
m = StaticArrays.MMatrix{N, N, T}(I) m = StaticArrays.MMatrix{N, N, T}(LinearAlgebra.I)
i,j = _ind(s) i,j = _ind(s)
m[i,j] = s.val m[i,j] = s.val
if s.symbol === :A if s.symbol === :A

View File

@ -0,0 +1,43 @@
@testset "GroupConstructions" begin
@testset "DirectProduct" begin
GH =
let G = PermutationGroups.SymmetricGroup(3),
H = PermutationGroups.SymmetricGroup(4)
Groups.Constructions.DirectProduct(G, H)
end
test_Group_interface(GH)
test_GroupElement_interface(rand(GH, 2)...)
@test collect(GH) isa Array{eltype(GH), 2}
@test contains(sprint(print, GH), "Direct product")
@test sprint(print, rand(GH)) isa String
end
@testset "DirectPower" begin
GGG = Groups.Constructions.DirectPower{3}(
PermutationGroups.SymmetricGroup(3),
)
test_Group_interface(GGG)
test_GroupElement_interface(rand(GGG, 2)...)
@test collect(GGG) isa Array{eltype(GGG), 3}
@test contains(sprint(print, GGG), "Direct 3-rd power")
@test sprint(print, rand(GGG)) isa String
end
@testset "WreathProduct" begin
W =
let G = PermutationGroups.SymmetricGroup(2),
P = PermutationGroups.SymmetricGroup(4)
Groups.Constructions.WreathProduct(G, P)
end
test_Group_interface(W)
test_GroupElement_interface(rand(W, 2)...)
@test collect(W) isa Array{eltype(W), 2}
@test contains(sprint(print, W), "Wreath product")
@test sprint(print, rand(W)) isa String
end
end

View File

@ -33,6 +33,8 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl"))
include("AutSigma_41.jl") include("AutSigma_41.jl")
include("AutSigma3.jl") include("AutSigma3.jl")
include("group_constructions.jl")
# if !haskey(ENV, "CI") # if !haskey(ENV, "CI")
# include("benchmarks.jl") # include("benchmarks.jl")
# end # end