diff --git a/Project.toml b/Project.toml index bd6bd42..513fe43 100644 --- a/Project.toml +++ b/Project.toml @@ -5,11 +5,13 @@ version = "0.5.2" [deps] AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" +GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" ThreadsX = "ac1d9e8a-700a-412c-b207-f0111f4b6c0d" [compat] -AbstractAlgebra = "^0.10.0" +AbstractAlgebra = "^0.13.0, ^0.14.0, ^0.15.0" +GroupsCore = "^0.3" ThreadsX = "^0.1.0" julia = "1.3, 1.4, 1.5" diff --git a/src/DirectPower.jl b/src/DirectPower.jl deleted file mode 100644 index 8274465..0000000 --- a/src/DirectPower.jl +++ /dev/null @@ -1,192 +0,0 @@ -export DirectPowerGroup, DirectPowerGroupElem - -############################################################################### -# -# DirectPowerGroup / DirectPowerGroupElem Constructors -# -############################################################################### - -""" - DirectPowerGroup(G::Group, n::Int) <: Group -Return `n`-fold direct product of `G`. The group operation is -`*` distributed component-wise, with component-wise identity as neutral element. -""" -struct DirectPowerGroup{N, T<:Group} <: Group - group::T -end - -DirectPowerGroup(G::Gr, N::Int) where Gr<:Group = DirectPowerGroup{N,Gr}(G) - -function DirectPower(G::Group, H::Group) - G == H || throw(DomainError( - "Direct Powers are defined only for the same groups")) - return DirectPowerGroup(G,2) -end - -DirectPower(H::Group, G::DirectPowerGroup) = DirectPower(G,H) - -function DirectPower(G::DirectPowerGroup{N}, H::Group) where N - G.group == H || throw(DomainError( - "Direct Powers are defined only for the same groups")) - return DirectPowerGroup(G.group, N+1) -end - -struct DirectPowerGroupElem{N, T<:GroupElem} <: GroupElem - elts::NTuple{N,T} -end - -function DirectPowerGroupElem(v::Vector{GrEl}) where GrEl<:GroupElem - return DirectPowerGroupElem(tuple(v...)) -end - -############################################################################### -# -# Type and parent object methods -# -############################################################################### - -elem_type(::Type{DirectPowerGroup{N,T}}) where {N,T} = - DirectPowerGroupElem{N, elem_type(T)} - -parent_type(::Type{DirectPowerGroupElem{N,T}}) where {N,T} = - DirectPowerGroup{N, parent_type(T)} - -parent(g::DirectPowerGroupElem{N, T}) where {N,T} = - DirectPowerGroup(parent(first(g.elts)), N) - -############################################################################### -# -# AbstractVector interface -# -############################################################################### - -Base.size(g::DirectPowerGroupElem{N}) where N = (N,) -Base.IndexStyle(::Type{DirectPowerGroupElem}) = Base.LinearFast() -Base.getindex(g::DirectPowerGroupElem, i::Int) = g.elts[i] - -############################################################################### -# -# Parent object call overloads -# -############################################################################### - -""" - (G::DirectPowerGroup)(a::Vector, check::Bool=true) -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 -check on the correctness nor coercion is performed. -""" -function (G::DirectPowerGroup{N})(a::Vector, check::Bool=true) where N - if check - N == length(a) || throw(DomainError( - "Can not coerce to DirectPowerGroup: lengths differ")) - a = (G.group).(a) - end - return DirectPowerGroupElem(a) -end - -function (G::DirectPowerGroup{N})(a::NTuple{N, GrEl}) where {N, GrEl} - return DirectPowerGroupElem(G.group.(a)) -end - -(G::DirectPowerGroup{N})(a::Vararg{GrEl, N}) where {N, GrEl} = DirectPowerGroupElem(G.group.(a)) - -function Base.one(G::DirectPowerGroup{N}) where N - return DirectPowerGroupElem(ntuple(i->one(G.group),N)) -end - -(G::DirectPowerGroup)(g::DirectPowerGroupElem) = G(g.elts) - -############################################################################### -# -# Basic manipulation -# -############################################################################### - -function hash(G::DirectPowerGroup{N}, h::UInt) where N - return hash(G.group, hash(N, hash(DirectPowerGroup,h))) -end - -function hash(g::DirectPowerGroupElem, h::UInt) - return hash(g.elts, hash(DirectPowerGroupElem, h)) -end - -############################################################################### -# -# String I/O -# -############################################################################### - -function show(io::IO, G::DirectPowerGroup{N}) where N - print(io, "$(N)-fold direct product of $(G.group)") -end - -function show(io::IO, g::DirectPowerGroupElem) - print(io, "[$(join(g.elts,","))]") -end - -############################################################################### -# -# Comparison -# -############################################################################### - -function (==)(G::DirectPowerGroup{N}, H::DirectPowerGroup{M}) where {N,M} - N == M || return false - G.group == H.group || return false - return true -end - -(==)(g::DirectPowerGroupElem, h::DirectPowerGroupElem) = g.elts == h.elts - -############################################################################### -# -# Group operations -# -############################################################################### - -function *(g::DirectPowerGroupElem{N}, h::DirectPowerGroupElem{N}, check::Bool=true) where N - if check - parent(g) == parent(h) || throw(DomainError( - "Can not multiply elements of different groups!")) - end - return DirectPowerGroupElem(ntuple(i-> g.elts[i]*h.elts[i], N)) -end - -^(g::DirectPowerGroupElem, n::Integer) = Base.power_by_squaring(g, n) - -function inv(g::DirectPowerGroupElem{N}) where {N} - return DirectPowerGroupElem(ntuple(i-> inv(g.elts[i]), N)) -end - -############################################################################### -# -# Misc -# -############################################################################### - -order(G::DirectPowerGroup{N}) where N = order(G.group)^N - -function iterate(G::DirectPowerGroup{N}) where N - elts = collect(G.group) - - indices = CartesianIndices(ntuple(i -> order(G.group), N)) - idx, s = iterate(indices) - g = DirectPowerGroupElem(ntuple(i -> elts[idx[i]], N)) - return g, (elts, indices, s) -end - -function iterate(G::DirectPowerGroup{N}, state) where N - elts, indices, s = state - res = iterate(indices, s) - if res == nothing - return nothing - else - idx, s = res - end - g = DirectPowerGroupElem(ntuple(i -> elts[idx[i]], N)) - return g, (elts, indices, s) -end - -eltype(::Type{DirectPowerGroup{N, G}}) where {N, G} = DirectPowerGroupElem{N, elem_type(G)} -Base.length(G::DirectPowerGroup) = order(G) diff --git a/src/Groups.jl b/src/Groups.jl index 7d2d607..7201de7 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -10,6 +10,7 @@ import Base: inv, reduce, *, ^, power_by_squaring import Base: findfirst, findnext, findlast, findprev, replace import Base: deepcopy_internal +using GroupsCore using LinearAlgebra using ThreadsX @@ -29,9 +30,6 @@ include("freereduce.jl") include("arithmetic.jl") include("findreplace.jl") -include("DirectPower.jl") -include("WreathProducts.jl") - ############################################################################### # # String I/O diff --git a/src/WreathProducts.jl b/src/WreathProducts.jl deleted file mode 100644 index a6d4ab2..0000000 --- a/src/WreathProducts.jl +++ /dev/null @@ -1,210 +0,0 @@ -export WreathProduct, WreathProductElem - -import AbstractAlgebra: AbstractPermutationGroup, AbstractPerm - -############################################################################### -# -# WreathProduct / WreathProductElem -# -############################################################################### - -""" - WreathProduct(N, P) <: Group -Return the wreath product of a group `N` by permutation group `P`, usually -written as `N ≀ P`. The multiplication inside wreath product is defined as -> `(n, σ) * (m, τ) = (n*σ(m), στ)` -where `σ(m)` denotes the action (from the right) of the permutation group on -`n-tuples` of elements from `N` - -# Arguments: -* `N::Group` : the single factor of the `DirectPower` group `N` -* `P::AbstractPermutationGroup` acting on `DirectPower` of `N` -""" -struct WreathProduct{N, T<:Group, PG<:AbstractPermutationGroup} <: Group - N::DirectPowerGroup{N, T} - P::PG - - function WreathProduct(G::Gr, P::PG) where - {Gr <: Group, PG <: AbstractPermutationGroup} - N = DirectPowerGroup(G, Int(P.n)) - return new{Int(P.n), Gr, PG}(N, P) - end -end - -struct WreathProductElem{N, T<:GroupElem, P<:AbstractPerm} <: GroupElem - n::DirectPowerGroupElem{N, T} - p::P - - function WreathProductElem(n::DirectPowerGroupElem{N,T}, p::P, - check::Bool=true) where {N, T, P<:AbstractPerm} - if check - N == length(p.d) || throw(DomainError( - "Can't form WreathProductElem: lengths differ")) - end - return new{N, T, P}(n, p) - end -end - -############################################################################### -# -# Type and parent object methods -# -############################################################################### - -elem_type(::Type{WreathProduct{N, T, PG}}) where {N, T, PG} = WreathProductElem{N, elem_type(T), elem_type(PG)} - -parent_type(::Type{WreathProductElem{N, T, P}}) where {N, T, P} = - WreathProduct{N, parent_type(T), parent_type(P)} - -parent(g::WreathProductElem) = WreathProduct(parent(g.n[1]), parent(g.p)) - -############################################################################### -# -# Parent object call overloads -# -############################################################################### - -function (G::WreathProduct{N})(g::WreathProductElem{N}) where {N} - n = G.N(g.n) - p = G.P(g.p) - return WreathProductElem(n, p) -end - -""" - (G::WreathProduct)(n::DirectPowerGroupElem, p::Generic.Perm) -Create an element of wreath product `G` by coercing `n` and `p` to `G.N` and -`G.P`, respectively. -""" -(G::WreathProduct)(n::DirectPowerGroupElem, p::Generic.Perm) = WreathProductElem(n,p) - -Base.one(G::WreathProduct) = WreathProductElem(one(G.N), one(G.P), false) - -""" - (G::WreathProduct)(p::Generic.Perm) -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)(n::DirectPowerGroupElem) -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. -""" -(G::WreathProduct)(n::DirectPowerGroupElem) = G(n, one(G.P)) - -(G::WreathProduct)(n,p) = G(G.N(n), G.P(p)) - -############################################################################### -# -# Basic manipulation -# -############################################################################### - -function hash(G::WreathProduct, h::UInt) - return hash(G.N, hash(G.P, hash(WreathProduct, h))) -end - -function hash(g::WreathProductElem, h::UInt) - return hash(g.n, hash(g.p, hash(WreathProductElem, h))) -end - -############################################################################### -# -# String I/O -# -############################################################################### - -function show(io::IO, G::WreathProduct) - print(io, "Wreath Product of $(G.N.group) by $(G.P)") -end - -function show(io::IO, g::WreathProductElem) - print(io, "($(g.n)≀$(g.p))") -end - -############################################################################### -# -# Comparison -# -############################################################################### - -function (==)(G::WreathProduct, H::WreathProduct) - G.N == H.N || return false - G.P == H.P || return false - return true -end - -function (==)(g::WreathProductElem, h::WreathProductElem) - g.n == h.n || return false - g.p == h.p || return false - return true -end - -############################################################################### -# -# Group operations -# -############################################################################### - -(p::Generic.Perm)(n::DirectPowerGroupElem) = DirectPowerGroupElem(n.elts[p.d]) - -""" - *(g::WreathProductElem, h::WreathProductElem) -Return the group operation of wreath product elements, i.e. -> `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 -`h.n::DirectPowerGroupElem` via standard permutation of coordinates. -""" -function *(g::WreathProductElem, h::WreathProductElem) - return WreathProductElem(g.n*g.p(h.n), g.p*h.p, false) -end - -^(g::WreathProductElem, n::Integer) = Base.power_by_squaring(g, n) - -""" - inv(g::WreathProductElem) -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)`. -""" -function inv(g::WreathProductElem) - pinv = inv(g.p) - return WreathProductElem(pinv(inv(g.n)), pinv, false) -end - -############################################################################### -# -# Misc -# -############################################################################### - -matrix_repr(g::WreathProductElem) = Any[matrix_repr(g.p) g.n] - -function iterate(G::WreathProduct) - n, state_N = iterate(G.N) - p, state_P = iterate(G.P) - return G(n,p), (state_N, p, state_P) -end - -function iterate(G::WreathProduct, state) - state_N, p, state_P = state - res = iterate(G.N, state_N) - - if res == nothing - resP = iterate(G.P, state_P) - if resP == nothing - return nothing - else - n, state_N = iterate(G.N) - p, state_P = resP - end - else - n, state_N = res - end - - return G(n,p), (state_N, p, state_P) -end - -eltype(::Type{WreathProduct{N,G,PG}}) where {N,G,PG} = WreathProductElem{N, elem_type(G), elem_type(PG)} - -order(G::WreathProduct) = order(G.P)*order(G.N) -length(G::WreathProduct) = order(G) diff --git a/test/DirectPower-tests.jl b/test/DirectPower-tests.jl deleted file mode 100644 index 65722f5..0000000 --- a/test/DirectPower-tests.jl +++ /dev/null @@ -1,89 +0,0 @@ -@testset "DirectPowers" begin - - ×(a,b) = Groups.DirectPower(a,b) - - @testset "Constructors" begin - G = SymmetricGroup(3) - - @test Groups.DirectPowerGroup(G,2) isa AbstractAlgebra.Group - @test G×G isa AbstractAlgebra.Group - @test Groups.DirectPowerGroup(G,2) isa Groups.DirectPowerGroup{2, Generic.SymmetricGroup{Int64}} - - @test (G×G)×G == DirectPowerGroup(G, 3) - @test (G×G)×G == (G×G)×G - - GG = DirectPowerGroup(G,2) - @test one(G×G) isa GroupElem - @test (G×G)((one(G), one(G))) isa GroupElem - @test (G×G)([one(G), one(G)]) isa GroupElem - - @test Groups.DirectPowerGroupElem((one(G), one(G))) == one(G×G) - @test GG(one(G), one(G)) == one(G×G) - - g = perm"(1,2,3)" - - @test GG(g, g^2) isa GroupElem - @test GG(g, g^2) isa Groups.DirectPowerGroupElem{2, Generic.Perm{Int64}} - - h = GG(g,g^2) - - @test h == GG(h) - - @test GG(g, g^2) isa GroupElem - @test GG(g, g^2) isa Groups.DirectPowerGroupElem - - @test_throws MethodError GG(g,g,g) - @test GG(g,g^2) == h - - @test h[1] == g - @test h[2] == g^2 - h = GG(g, one(G)) - @test h == GG(g, one(G)) - end - - @testset "Basic arithmetic" begin - G = SymmetricGroup(3) - GG = G×G - i = perm"(1,3)" - g = perm"(1,2,3)" - - h = GG(g,g^2) - k = GG(g^3, g^2) - - @test h^2 == GG(g^2,g) - @test h^6 == one(GG) - - @test h*h == h^2 - @test h*k == GG(g,g) - - @test h*inv(h) == one(G×G) - - w = GG(g,i)*GG(i,g) - @test w == GG(perm"(1,2)(3)", perm"(2,3)") - @test w == inv(w) - @test w^2 == w*w == one(GG) - end - - @testset "elem/parent_types" begin - G = SymmetricGroup(3) - g = perm"(1,2,3)" - - @test elem_type(G×G) == DirectPowerGroupElem{2, elem_type(G)} - @test elem_type(G×G×G) == DirectPowerGroupElem{3, elem_type(G)} - @test parent_type(typeof((G×G)(g,g^2))) == Groups.DirectPowerGroup{2, typeof(G)} - @test parent(DirectPowerGroupElem((g,g^2,g^3))) == DirectPowerGroup(G,3) - end - - @testset "Misc" begin - G = SymmetricGroup(3) - GG = Groups.DirectPowerGroup(G,3) - @test order(GG) == 216 - - @test isa(collect(GG), Vector{Groups.DirectPowerGroupElem{3, elem_type(G)}}) - elts = vec(collect(GG)) - - @test length(elts) == 216 - @test all([g*inv(g) == one(GG) for g in elts]) - @test all(inv(g*h) == inv(h)*inv(g) for g in elts for h in elts) - end -end diff --git a/test/WreathProd-tests.jl b/test/WreathProd-tests.jl deleted file mode 100644 index 158e056..0000000 --- a/test/WreathProd-tests.jl +++ /dev/null @@ -1,98 +0,0 @@ -@testset "WreathProducts" begin - S_3 = SymmetricGroup(3) - S_2 = SymmetricGroup(2) - b = perm"(1,2,3)" - a = perm"(1,2)" - - @testset "Constructors" begin - @test Groups.WreathProduct(S_2, S_3) isa AbstractAlgebra.Group - B3 = Groups.WreathProduct(S_2, S_3) - @test B3 isa Groups.WreathProduct - @test B3 isa WreathProduct{3, Generic.SymmetricGroup{Int}, Generic.SymmetricGroup{Int}} - - aa = Groups.DirectPowerGroupElem((a^0 ,a, a^2)) - - @test Groups.WreathProductElem(aa, b) isa AbstractAlgebra.GroupElem - x = Groups.WreathProductElem(aa, b) - @test x isa Groups.WreathProductElem - @test x isa - Groups.WreathProductElem{3, Generic.Perm{Int}, Generic.Perm{Int}} - - @test B3.N == Groups.DirectPowerGroup(S_2, 3) - @test B3.P == S_3 - - @test B3(aa, b) == Groups.WreathProductElem(aa, b) - w = B3(aa, b) - @test B3(w) == w - @test B3(b) == Groups.WreathProductElem(one(B3.N), b) - @test B3(aa) == Groups.WreathProductElem(aa, one(S_3)) - - @test B3((a^0 ,a, a^2), b) isa WreathProductElem - - @test B3((a^0 ,a, a^2), b) == B3(aa, b) - end - - @testset "Types" begin - B3 = Groups.WreathProduct(S_2, S_3) - - @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.SymmetricGroup{Int}} - - @test parent(one(B3)) == Groups.WreathProduct(S_2,S_3) - @test parent(one(B3)) == B3 - end - - @testset "Basic operations on WreathProductElem" begin - aa = Groups.DirectPowerGroupElem((a^0 ,a, a^2)) - B3 = Groups.WreathProduct(S_2, S_3) - g = B3(aa, b) - - @test g.p == b - @test g.n == DirectPowerGroupElem(aa.elts) - - h = deepcopy(g) - @test h == g - @test !(g === h) - - g = B3(Groups.DirectPowerGroupElem((a ,a, a^2)), g.p) - - @test g.n[1] == parent(g.n[1])(a) - @test g != h - - @test hash(g) != hash(h) - end - - @testset "Group arithmetic" begin - B4 = Groups.WreathProduct(SymmetricGroup(3), SymmetricGroup(4)) - - id, a, b = perm"(3)", perm"(1,2)(3)", perm"(1,2,3)" - - x = B4((id,a,b,id), perm"(1,2,3)(4)") - @test inv(x) == B4((inv(b),id, a,id), perm"(1,3,2)(4)") - - y = B4((a,id,a,b), perm"(1,4)(2,3)") - @test inv(y) == B4((inv(b), a,id, a), perm"(1,4)(2,3)") - - @test x*y == B4((id,id,b*a,b), perm"(1,3,4)(2)") - @test y*x == B4(( a, b, id,b), perm"(1,4,2)(3)") - - @test inv(x)*y == B4((inv(b)*a,a,a,b), perm"(1,2,4)(3)") - @test y*inv(x) == B4((a,a,a,id), perm"(1,4,3)(2)") - - @test (x*y)^6 == ((x*y)^2)^3 - end - - @testset "Iteration" begin - Wr = WreathProduct(SymmetricGroup(2),SymmetricGroup(4)) - - elts = collect(Wr) - @test elts isa Vector{Groups.WreathProductElem{4, Generic.Perm{Int}, Generic.Perm{Int}}} - @test order(Wr) == 2^4*factorial(4) - - @test length(elts) == order(Wr) - @test all((g*inv(g) == one(Wr) for g in elts)) - @test all(inv(g*h) == inv(h)*inv(g) for g in elts for h in elts) - end - -end diff --git a/test/runtests.jl b/test/runtests.jl index 077c3b3..3841aad 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,7 +21,5 @@ using LinearAlgebra include("FreeGroup-tests.jl") include("AutGroup-tests.jl") - include("DirectPower-tests.jl") - include("WreathProd-tests.jl") include("FPGroup-tests.jl") end