diff --git a/docs/src/Groups/subgroups.md b/docs/src/Groups/subgroups.md index f132fa331c6e..b46096f38cba 100644 --- a/docs/src/Groups/subgroups.md +++ b/docs/src/Groups/subgroups.md @@ -43,17 +43,13 @@ intersect(::T, V::T...) where T<:GAPGroup The following functions return a vector of subgroups. ```@docs -subgroups(G::GAPGroup) normal_subgroups -maximal_subgroups maximal_normal_subgroups minimal_normal_subgroups characteristic_subgroups derived_series sylow_system -hall_subgroup_reps hall_system -complement_class_reps complement_system chief_series composition_series @@ -71,6 +67,18 @@ upper_central_series one can type `embedding(G,H)`. +The following functions return an iterator of subgroups. +Usually it is more efficient to work with (representatives of) the +underlying conjugacy classes of subgroups instead. + +```@docs +complements(G::T, N::T) where T <: GAPGroup +hall_subgroups +low_index_subgroups +maximal_subgroups +subgroups(G::GAPGroup) +``` + ## Conjugation action of elements and subgroups ```@docs @@ -94,8 +102,11 @@ number_of_conjugacy_classes(G::GAPGroup) conjugacy_class(G::GAPGroup, g::GAPGroupElem) conjugacy_class(G::T, g::T) where T<:GAPGroup conjugacy_classes(G::GAPGroup) -conjugacy_classes_subgroups(G::GAPGroup) -conjugacy_classes_maximal_subgroups(G::GAPGroup) +complement_classes +hall_subgroup_classes +low_index_subgroup_classes +maximal_subgroup_classes(G::GAPGroup) +subgroup_classes(G::GAPGroup) ``` diff --git a/src/Groups/GAPGroups.jl b/src/Groups/GAPGroups.jl index 7276ae2dca2c..f0c6ab20a73f 100644 --- a/src/Groups/GAPGroups.jl +++ b/src/Groups/GAPGroups.jl @@ -304,9 +304,11 @@ function Base.show(io::IO, G::PermGroup) # Treat groups specially which know that they are nat. symmetric/alternating. io = pretty(io) - if has_is_natural_symmetric_group(G) && is_natural_symmetric_group(G) + if has_is_natural_symmetric_group(G) && is_natural_symmetric_group(G) && + number_of_moved_points(G) == degree(G) print(io, LowercaseOff(), "Sym(", degree(G), ")") - elseif has_is_natural_alternating_group(G) && is_natural_alternating_group(G) + elseif has_is_natural_alternating_group(G) && is_natural_alternating_group(G) && + number_of_moved_points(G) == degree(G) print(io, LowercaseOff(), "Alt(", degree(G), ")") else print(io, "Permutation group") @@ -696,7 +698,7 @@ number_of_conjugacy_classes(::Type{T}, G::GAPGroup) where T <: IntegerUnion = T( """ conjugacy_classes(G::Group) -Return the vector of all conjugacy classes of elements in `G`. +Return a vector of all conjugacy classes of elements in `G`. It is guaranteed that the class of the identity is in the first position. """ function conjugacy_classes(G::GAPGroup) @@ -756,123 +758,131 @@ function Base.rand(rng::Random.AbstractRNG, C::GroupConjClass{S,T}) where S wher end """ - conjugacy_classes_subgroups(G::Group) + subgroup_classes(G::GAPGroup; order::T = ZZRingElem(-1)) where T <: IntegerUnion -Return the vector of all conjugacy classes of subgroups of G. +Return a vector of all conjugacy classes of subgroups of `G` or, +if `order` is positive, the classes of subgroups of this order. # Examples ```jldoctest julia> G = symmetric_group(3) Sym(3) -julia> conjugacy_classes_subgroups(G) +julia> subgroup_classes(G) 4-element Vector{GAPGroupConjClass{PermGroup, PermGroup}}: Conjugacy class of permutation group in G Conjugacy class of permutation group in G Conjugacy class of permutation group in G Conjugacy class of permutation group in G + +julia> subgroup_classes(G, order = ZZRingElem(2)) +1-element Vector{GAPGroupConjClass{PermGroup, PermGroup}}: + Conjugacy class of permutation group in G ``` """ -function conjugacy_classes_subgroups(G::GAPGroup) +function subgroup_classes(G::GAPGroup; order::T = ZZRingElem(-1)) where T <: IntegerUnion L = Vector{GapObj}(GAPWrap.ConjugacyClassesSubgroups(G.X)) - return [GAPGroupConjClass(G, _as_subgroup_bare(G, GAPWrap.Representative(cc)), cc) for cc in L] + res = [GAPGroupConjClass(G, _as_subgroup_bare(G, GAPWrap.Representative(cc)), cc) for cc in L] + if order != -1 + filter!(x -> AbstractAlgebra.order(representative(x)) == order, res) + end + return res end """ - subgroup_reps(G::GAPGroup; order::ZZRingElem = ZZRingElem(-1)) + subgroups(G::GAPGroup) -Return a vector of representatives (under conjugation) for all subgroups of `G`. -If given, only subgroups of a certain order are returned. +Return an iterator over all subgroups in `G`. +Very likely it is better to use [`subgroup_classes`](@ref) instead. # Examples ```jldoctest -julia> G = symmetric_group(3); - -julia> subgroup_reps(G) -4-element Vector{PermGroup}: - Permutation group of degree 3 and order 1 - Permutation group of degree 3 and order 2 - Permutation group of degree 3 and order 3 - Permutation group of degree 3 and order 6 - -julia> subgroup_reps(G, order = ZZRingElem(2)) -1-element Vector{PermGroup}: - Permutation group of degree 3 and order 2 +julia> println([order(H) for H in subgroups(symmetric_group(3))]) +ZZRingElem[1, 2, 2, 2, 3, 6] +julia> println([order(H) for H in subgroups(quaternion_group(8))]) +ZZRingElem[1, 2, 4, 4, 4, 8] ``` """ -function subgroup_reps(G::GAPGroup; order::ZZRingElem = ZZRingElem(-1)) - C = GAPWrap.ConjugacyClassesSubgroups(G.X) - C = map(GAPWrap.Representative, C) - if order != -1 - C = [x for x = C if GAPWrap.Order(x) == order] - end - return [Oscar._as_subgroup(G, x)[1] for x = C] -end +subgroups(G::GAPGroup) = Iterators.flatten(subgroup_classes(G)) """ - conjugacy_classes_maximal_subgroups(G::Group) + maximal_subgroup_classes(G::Group) -Return the vector of all conjugacy classes of maximal subgroups of G. +Return a vector of all conjugacy classes of maximal subgroups of `G`. # Examples ```jldoctest julia> G = symmetric_group(3); -julia> conjugacy_classes_maximal_subgroups(G) +julia> maximal_subgroup_classes(G) 2-element Vector{GAPGroupConjClass{PermGroup, PermGroup}}: Conjugacy class of permutation group in G Conjugacy class of permutation group in G ``` """ -function conjugacy_classes_maximal_subgroups(G::GAPGroup) - L = Vector{GapObj}(GAPWrap.ConjugacyClassesMaximalSubgroups(G.X)) - return [GAPGroupConjClass(G, _as_subgroup_bare(G, GAPWrap.Representative(cc)), cc) for cc in L] +@gapattribute function maximal_subgroup_classes(G::GAPGroup) + L = Vector{GapObj}(GAP.Globals.ConjugacyClassesMaximalSubgroups(G.X)::GapObj) + T = typeof(G) + LL = [GAPGroupConjClass(G, _as_subgroup_bare(G, GAPWrap.Representative(cc)), cc) for cc in L] + return Vector{GAPGroupConjClass{T, T}}(LL) end """ - maximal_subgroup_reps(G::GAPGroup) + maximal_subgroups(G::Group) -Return a vector of representatives (under conjugation) for all maximal -subgroups of `G`. +Return an iterator over the maximal subgroups in `G`. +Very likely it is better to use [`maximal_subgroup_classes`](@ref) instead. # Examples ```jldoctest -julia> maximal_subgroup_reps(symmetric_group(4)) -3-element Vector{PermGroup}: - Permutation group of degree 4 - Permutation group of degree 4 and order 8 - Permutation group of degree 4 and order 6 +julia> println([order(H) for H in maximal_subgroups(symmetric_group(3))]) +ZZRingElem[3, 2, 2, 2] +julia> println([order(H) for H in maximal_subgroups(quaternion_group(8))]) +ZZRingElem[4, 4, 4] ``` """ -function maximal_subgroup_reps(G::GAPGroup) - return Oscar._as_subgroups(G, GAP.Globals.MaximalSubgroupClassReps(G.X)) -end +maximal_subgroups(G::T) where T <: Union{GAPGroup, FinGenAbGroup} = Iterators.flatten(maximal_subgroup_classes(G)) """ - low_index_subgroup_reps(G::GAPGroup, n::Int) + low_index_subgroup_classes(G::GAPGroup, n::Int) -Return a vector of representatives (under conjugation) for all subgroups -of index at most `n` in `G`. +Return a vector of conjugacy classes of subgroups of index at most `n` in `G`. # Examples ```jldoctest julia> G = symmetric_group(5); -julia> low_index_subgroup_reps(G, 5) -3-element Vector{PermGroup}: - Sym(5) - Alt(5) - Sym(5) - +julia> low_index_subgroup_classes(G, 5) +3-element Vector{GAPGroupConjClass{PermGroup, PermGroup}}: + Conjugacy class of Sym(5) in G + Conjugacy class of Alt(5) in G + Conjugacy class of permutation group in G ``` """ -function low_index_subgroup_reps(G::GAPGroup, n::Int) - ll = GAP.Globals.LowIndexSubgroups(G.X, n) - return [Oscar._as_subgroup(G, x)[1] for x = ll] +function low_index_subgroup_classes(G::GAPGroup, n::Int) + @req (n > 0) "index must be positive" + ll = GAP.Globals.LowIndexSubgroups(G.X, n)::GapObj + return [conjugacy_class(G, H) for H in _as_subgroups(G, ll)] end +""" + low_index_subgroups(G::Group, n::Int) + +Return an iterator over the subgroups of index at most `n` in `G`. +Very likely it is better to use [`low_index_subgroup_classes`](@ref) instead. + +# Examples +```jldoctest +julia> G = alternating_group(6); + +julia> length(collect(low_index_subgroups(G, 6))) +13 +``` +""" +low_index_subgroups(G::T, n::Int) where T <: Union{GAPGroup, FinGenAbGroup} = Iterators.flatten(low_index_subgroup_classes(G, n)) + """ conjugate_group(G::T, x::GAPGroupElem) where T <: GAPGroup @@ -1248,18 +1258,10 @@ function sylow_subgroup(G::GAPGroup, p::IntegerUnion) return _as_subgroup(G, GAPWrap.SylowSubgroup(G.X, GAP.Obj(p))) end -# no longer documented, better use `hall_subgroup_reps` -function hall_subgroup(G::GAPGroup, P::AbstractVector{<:IntegerUnion}) - P = unique(P) - @req all(is_prime, P) "The integers must be prime" - @req is_solvable(G) "The group is not solvable" - return _as_subgroup(G,GAP.Globals.HallSubgroup(G.X,GAP.Obj(P, recursive=true))::GapObj) -end - """ - hall_subgroup_reps(G::Group, P::AbstractVector{<:IntegerUnion}) + hall_subgroup_classes(G::Group, P::AbstractVector{<:IntegerUnion}) -Return a vector that contains representatives of conjugacy classes of +Return a vector that contains the conjugacy classes of Hall `P`-subgroups of the finite group `G`, for a vector `P` of primes. A Hall `P`-subgroup of `G` is a subgroup the order of which is only divisible by primes in `P` and whose index in `G` is coprime to all primes in `P`. @@ -1272,37 +1274,53 @@ up to conjugacy. ```jldoctest julia> g = dihedral_group(30); -julia> h = hall_subgroup_reps(g, [2, 3]); +julia> h = hall_subgroup_classes(g, [2, 3]); -julia> (length(h), order(h[1])) +julia> (length(h), order(representative(h[1]))) (1, 6) julia> g = GL(3, 2) GL(3,2) -julia> h = hall_subgroup_reps(g, [2, 3]); +julia> h = hall_subgroup_classes(g, [2, 3]); -julia> (length(h), order(h[1])) +julia> (length(h), order(representative(h[1]))) (2, 24) -julia> h = hall_subgroup_reps(g, [2, 7]); length(h) +julia> h = hall_subgroup_classes(g, [2, 7]); length(h) 0 - ``` """ -function hall_subgroup_reps(G::GAPGroup, P::AbstractVector{<:IntegerUnion}) +function hall_subgroup_classes(G::GAPGroup, P::AbstractVector{<:IntegerUnion}) P = unique(P) @req all(is_prime, P) "The integers must be prime" res_gap = GAP.Globals.HallSubgroup(G.X, GAP.Obj(P, recursive = true))::GapObj if res_gap == GAP.Globals.fail - return typeof(G)[] + T = typeof(G) + return GAPGroupConjClass{T, T}[] elseif GAPWrap.IsList(res_gap) - return _as_subgroups(G, res_gap) + return [conjugacy_class(G, H) for H in _as_subgroups(G, res_gap)] else - return [_as_subgroup_bare(G, res_gap)] + return [conjugacy_class(G, _as_subgroup_bare(G, res_gap))] end end +""" + hall_subgroups(G::Group, P::AbstractVector{<:IntegerUnion}) + +Return an iterator over the Hall `P`-subgroups in `G`. +Very likely it is better to use [`hall_subgroup_classes`](@ref) instead. + +# Examples +```jldoctest +julia> g = GL(3, 2); + +julia> describe(first(hall_subgroups(g, [2, 3]))) +"S4" +``` +""" +hall_subgroups(G::T, P::AbstractVector{<:IntegerUnion}) where T <: Union{GAPGroup, FinGenAbGroup} = Iterators.flatten(hall_subgroup_classes(G, P)) + @doc raw""" sylow_system(G::Group) @@ -1319,9 +1337,9 @@ an exception is thrown if `G` is not solvable. end @doc raw""" - complement_class_reps(G::T, N::T) where T <: GAPGroup + complement_classes(G::T, N::T) where T <: GAPGroup -Return a vector of representatives of the conjugacy classes of complements +Return a vector of the conjugacy classes of complements of the normal subgroup `N` in `G`. This function may throw an error exception if both `N` and `G/N` are nonsolvable. @@ -1333,21 +1351,42 @@ together with `N` generates `G`. ```jldoctest julia> G = symmetric_group(3); -julia> complement_class_reps(G, derived_subgroup(G)[1]) -1-element Vector{PermGroup}: - Permutation group of degree 3 +julia> complement_classes(G, derived_subgroup(G)[1]) +1-element Vector{GAPGroupConjClass{PermGroup, PermGroup}}: + Conjugacy class of permutation group in G julia> G = dihedral_group(8) Pc group of order 8 -julia> complement_class_reps(G, center(G)[1]) -PcGroup[] +julia> complement_classes(G, center(G)[1]) +GAPGroupConjClass{PcGroup, PcGroup}[] ``` """ -function complement_class_reps(G::T, N::T) where T <: GAPGroup - return _as_subgroups(G, GAP.Globals.ComplementClassesRepresentatives(G.X, N.X)) +function complement_classes(G::T, N::T) where T <: GAPGroup + res_gap = GAP.Globals.ComplementClassesRepresentatives(G.X, N.X)::GapObj + if length(res_gap) == 0 + return GAPGroupConjClass{T, T}[] + else + return [conjugacy_class(G, H) for H in _as_subgroups(G, res_gap)] + end end +@doc raw""" + complements(G::T, N::T) where T <: GAPGroup + +Return an iterator over the complements of the normal subgroup `N` in `G`. +Very likely it is better to use [`complement_classes`](@ref) instead. + +# Examples +```jldoctest +julia> G = symmetric_group(3); + +julia> describe(first(complements(G, derived_subgroup(G)[1]))) +"C2" +``` +""" +complements(G::T, N::T) where T <: GAPGroup = Iterators.flatten(complement_classes(G, N)) + @doc raw""" complement_system(G::Group) diff --git a/src/Groups/GrpAb.jl b/src/Groups/GrpAb.jl index 9a3ed48fc759..2114a32a4f9d 100644 --- a/src/Groups/GrpAb.jl +++ b/src/Groups/GrpAb.jl @@ -71,24 +71,24 @@ end # ################################################################################ -struct GrpAbFinGenConjClass{T<:FinGenAbGroup, S<:Union{FinGenAbGroupElem,FinGenAbGroup}} <: GroupConjClass{T, S} +struct FinGenAbGroupConjClass{T<:FinGenAbGroup, S<:Union{FinGenAbGroupElem,FinGenAbGroup}} <: GroupConjClass{T, S} X::T repr::S end -function Base.hash(C::GrpAbFinGenConjClass, h::UInt) +function Base.hash(C::FinGenAbGroupConjClass, h::UInt) return Base.hash(Representative(C), h) end -function Base.show(io::IO, C::GrpAbFinGenConjClass) +function Base.show(io::IO, C::FinGenAbGroupConjClass) print(io, string(representative(C)), " ^ ", string(C.X)) end -==(a::GrpAbFinGenConjClass, b::GrpAbFinGenConjClass) = representative(a) == representative(b) +==(a::FinGenAbGroupConjClass, b::FinGenAbGroupConjClass) = representative(a) == representative(b) -Base.length(::Type{T}, C::GrpAbFinGenConjClass) where T <: IntegerUnion = T(1) +Base.length(::Type{T}, C::FinGenAbGroupConjClass) where T <: IntegerUnion = T(1) -Base.length(C::GrpAbFinGenConjClass) = ZZRingElem(1) +Base.length(C::FinGenAbGroupConjClass) = ZZRingElem(1) ################################################################################ @@ -97,19 +97,19 @@ Base.length(C::GrpAbFinGenConjClass) = ZZRingElem(1) # ################################################################################ -conjugacy_class(G::FinGenAbGroup, g::FinGenAbGroupElem) = GrpAbFinGenConjClass(G, g) +conjugacy_class(G::FinGenAbGroup, g::FinGenAbGroupElem) = FinGenAbGroupConjClass(G, g) -Base.eltype(::Type{GrpAbFinGenConjClass{T,S}}) where {T,S} = S +Base.eltype(::Type{FinGenAbGroupConjClass{T,S}}) where {T,S} = S -Base.rand(C::GrpAbFinGenConjClass) = representative(C) +Base.rand(C::FinGenAbGroupConjClass) = representative(C) -Base.rand(rng::Random.AbstractRNG, C::GrpAbFinGenConjClass) = representative(C) +Base.rand(rng::Random.AbstractRNG, C::FinGenAbGroupConjClass) = representative(C) number_of_conjugacy_classes(G::FinGenAbGroup) = order(ZZRingElem, G) number_of_conjugacy_classes(::Type{T}, G::FinGenAbGroup) where T <: IntegerUnion = order(T, G) -conjugacy_classes(G::FinGenAbGroup) = [GrpAbFinGenConjClass(G, x) for x in G] +conjugacy_classes(G::FinGenAbGroup) = [FinGenAbGroupConjClass(G, x) for x in G] is_conjugate(G::FinGenAbGroup, x::FinGenAbGroupElem, y::FinGenAbGroupElem) = (x == y) @@ -123,45 +123,37 @@ end # ################################################################################ -conjugacy_class(G::T, H::T) where T <: FinGenAbGroup = GrpAbFinGenConjClass(G, H) +conjugacy_class(G::T, H::T) where T <: FinGenAbGroup = FinGenAbGroupConjClass(G, H) -function conjugacy_classes_subgroups(G::FinGenAbGroup) +function subgroup_classes(G::FinGenAbGroup; order::T = ZZRingElem(-1)) where T <: IntegerUnion @req is_finite(G) "G is not finite" - return [conjugacy_class(G, H) for (H, mp) in subgroups(G)] -end - -function subgroup_reps(G::FinGenAbGroup; order::ZZRingElem = ZZRingElem(-1)) if order > 0 && mod(Hecke.order(G), order) != 0 # `subgroups` would throw an error - return FinGenAbGroup[] + return FinGenAbGroupConjClass{FinGenAbGroup, FinGenAbGroup}[] end - return [H for (H, mp) in subgroups(G, order = order)] + return [conjugacy_class(G, H) for (H, mp) in Hecke.subgroups(G, order = order)] end -function low_index_subgroup_reps(G::FinGenAbGroup, n::Int) +function low_index_subgroup_classes(G::FinGenAbGroup, n::Int) @req (n > 0) "index must be positive" - res = [G] + res = [conjugacy_class(G, G)] ord = order(G) for i in 2:n if mod(ord, i) == 0 - append!(res, [H for (H, mp) in subgroups(G, index = i)]) + append!(res, [conjugacy_class(G, H) for (H, mp) in Hecke.subgroups(G, index = i)]) end end return res end -function maximal_subgroup_reps(G::FinGenAbGroup) +function maximal_subgroup_classes(G::FinGenAbGroup) @req is_finite(G) "G is not finite" primes = [p for (p, e) in factor(order(G))] res = typeof(G)[] for p in primes - append!(res, [H for (H, mp) in subgroups(G, index = p)]) + append!(res, [H for (H, mp) in Hecke.subgroups(G, index = p)]) end - return res -end - -function conjugacy_classes_maximal_subgroups(G::FinGenAbGroup) - return [conjugacy_class(G, H) for H in maximal_subgroup_reps(G)] + return [conjugacy_class(G, H) for H in res] end conjugate_group(G::FinGenAbGroup, x::FinGenAbGroupElem) = G @@ -184,11 +176,11 @@ end is_conjugate_subgroup(G::T, U::T, V::T) where T <: FinGenAbGroup = is_subgroup(V, U)[1] is_conjugate_subgroup_with_data(G::T, U::T, V::T) where T <: FinGenAbGroup = is_subgroup(V, U)[1], zero(G) -Base.IteratorSize(::Type{<:GrpAbFinGenConjClass}) = Base.HasLength() +Base.IteratorSize(::Type{<:FinGenAbGroupConjClass}) = Base.HasLength() -Base.iterate(C::GrpAbFinGenConjClass) = iterate(C, 0) +Base.iterate(C::FinGenAbGroupConjClass) = iterate(C, 0) -function Base.iterate(C::GrpAbFinGenConjClass, state::Int) +function Base.iterate(C::FinGenAbGroupConjClass, state::Int) if state == 0 return representative(C), 1 else @@ -279,7 +271,7 @@ solvable_radical(G::FinGenAbGroup) = (G, identity_map(G)) ################################################################################ #TODO: how to compute complements? -# complement_class_reps(G::T, N::T) where T <: FinGenAbGroup +# complement_classes(G::T, N::T) where T <: FinGenAbGroup # complement_system(G::FinGenAbGroup) function sylow_system(G::FinGenAbGroup) @@ -291,10 +283,7 @@ function sylow_system(G::FinGenAbGroup) return result end -# no longer documented, better use `hall_subgroup_reps` -hall_subgroup(G::FinGenAbGroup, P::AbstractVector{<:IntegerUnion}) = hall_subgroup_reps(G, P)[1] - -function hall_subgroup_reps(G::FinGenAbGroup, P::AbstractVector{<:IntegerUnion}) +function hall_subgroup_classes(G::FinGenAbGroup, P::AbstractVector{<:IntegerUnion}) @req is_finite(G) "G is not finite" P = unique(P) @req all(is_prime, P) "The integers must be prime" @@ -312,7 +301,7 @@ function hall_subgroup_reps(G::FinGenAbGroup, P::AbstractVector{<:IntegerUnion}) end end end - return [sub(G, subgens)[1]] + return [conjugacy_class(G, sub(G, subgens)[1])] end function hall_system(G::FinGenAbGroup) @@ -320,7 +309,7 @@ function hall_system(G::FinGenAbGroup) primes = [p for (p, e) in factor(order(G))] result = FinGenAbGroup[] for P in subsets(Set(primes)) - push!(result, hall_subgroup_reps(G, collect(P))[1]) + push!(result, representative(hall_subgroup_classes(G, collect(P))[1])) end return result end diff --git a/src/Groups/cosets.jl b/src/Groups/cosets.jl index bee4731cfc9f..eb51fdddb554 100644 --- a/src/Groups/cosets.jl +++ b/src/Groups/cosets.jl @@ -525,7 +525,7 @@ Base.:*(H::GAPGroup, g::GAPGroupElem, K::GAPGroup) = double_coset(H,g,K) """ double_cosets(G::T, H::T, K::T; check::Bool=true) where T<: GAPGroup -Return the vector of all the double cosets `HxK` for `x` in `G`. +Return a vector of all the double cosets `HxK` for `x` in `G`. If `check == false`, do not check whether `H` and `K` are subgroups of `G`. # Examples diff --git a/src/Groups/gsets.jl b/src/Groups/gsets.jl index f1b9555bad43..4c67812fb99c 100644 --- a/src/Groups/gsets.jl +++ b/src/Groups/gsets.jl @@ -1016,7 +1016,7 @@ the action is transitive and the point stabilizers are maximal in `G`. ```jldoctest julia> G = alternating_group(6); -julia> mx = filter(is_transitive, maximal_subgroup_reps(G)) +julia> mx = filter(is_transitive, map(representative, maximal_subgroup_classes(G))) 3-element Vector{PermGroup}: Permutation group of degree 6 and order 24 Permutation group of degree 6 and order 36 diff --git a/src/Groups/sub.jl b/src/Groups/sub.jl index 2932195d1a50..bc05e667d226 100644 --- a/src/Groups/sub.jl +++ b/src/Groups/sub.jl @@ -187,62 +187,6 @@ julia> normal_subgroups(quaternion_group(8)) @gapattribute normal_subgroups(G::GAPGroup) = _as_subgroups(G, GAP.Globals.NormalSubgroups(G.X)) -""" - subgroups(G::Group) - -Return all subgroups of `G`. - -# Examples -```jldoctest -julia> subgroups(symmetric_group(3)) -6-element Vector{PermGroup}: - Permutation group of degree 3 and order 1 - Permutation group of degree 3 and order 2 - Permutation group of degree 3 and order 2 - Permutation group of degree 3 and order 2 - Permutation group of degree 3 and order 3 - Permutation group of degree 3 and order 6 - -julia> subgroups(quaternion_group(8)) -6-element Vector{PcGroup}: - Pc group of order 1 - Pc group of order 2 - Pc group of order 4 - Pc group of order 4 - Pc group of order 4 - Pc group of order 8 -``` -""" -function subgroups(G::GAPGroup) - # TODO: this is super inefficient. Slightly better would be to return an iterator - # which iterates over the (elements of) the conjugacy classes of subgroups - return _as_subgroups(G, GAP.Globals.AllSubgroups(G.X)) -end - -""" - maximal_subgroups(G::Group) - -Return all maximal subgroups of `G`. - -# Examples -```jldoctest -julia> maximal_subgroups(symmetric_group(3)) -4-element Vector{PermGroup}: - Permutation group of degree 3 and order 3 - Permutation group of degree 3 and order 2 - Permutation group of degree 3 and order 2 - Permutation group of degree 3 and order 2 - -julia> maximal_subgroups(quaternion_group(8)) -3-element Vector{PcGroup}: - Pc group of order 4 - Pc group of order 4 - Pc group of order 4 -``` -""" -@gapattribute maximal_subgroups(G::GAPGroup) = - _as_subgroups(G, GAP.Globals.MaximalSubgroups(G.X)) - """ maximal_normal_subgroups(G::Group) @@ -295,14 +239,11 @@ i.e., those subgroups that are invariant under all automorphisms of `G`. # Examples ```jldoctest -julia> subgroups(symmetric_group(3)) -6-element Vector{PermGroup}: - Permutation group of degree 3 and order 1 - Permutation group of degree 3 and order 2 - Permutation group of degree 3 and order 2 - Permutation group of degree 3 and order 2 +julia> characteristic_subgroups(symmetric_group(3)) +3-element Vector{PermGroup}: + Sym(3) Permutation group of degree 3 and order 3 - Permutation group of degree 3 and order 6 + Permutation group of degree 3 and order 1 julia> characteristic_subgroups(quaternion_group(8)) 3-element Vector{PcGroup}: @@ -512,7 +453,7 @@ julia> lower_central_series(symmetric_group(4)) upper_central_series(G::GAPGroup) Return the vector $[ G_1, G_2, \ldots ]$ where the last entry is the -trivial group, and $G_i$ is defined as the overgroup of $G_{i+1} +trivial group, and $G_i$ is defined as the overgroup of $G_{i+1}$ satisfying $G_i / G_{i+1} = Z(G/G_{i+1})$. The series ends as soon as it is repeating (e.g. when the whole group $G$ is reached, which happens if and only if $G$ is nilpotent). @@ -611,11 +552,14 @@ function is_maximal_subgroup(H::T, G::T; check::Bool = true) where T <: GAPGroup if check @req is_subset(H, G) "H is not a subgroup of G" end - if order(G) // order(H) < 100 - t = right_transversal(G, H)[2:end] #drop the identity - return all(x -> order(sub(G, vcat(gens(H), [x]))[1]) == order(G), t) + ind = index(G, H) + is_prime(ind) && return true + if ind < 100 + # Do not unpack the right transversal object. + t = right_transversal(G, H) + return all(i -> order(sub(G, vcat(gens(H), [t[i]]))[1]) == order(G), 2:Int(ind)) end - return any(M -> is_conjugate(G, M, H), maximal_subgroup_reps(G)) + return any(C -> H in C, maximal_subgroup_classes(G)) end """ diff --git a/src/NumberTheory/GaloisGrp/GaloisGrp.jl b/src/NumberTheory/GaloisGrp/GaloisGrp.jl index 6bdf9fcf5cb4..ffc3222cf3aa 100644 --- a/src/NumberTheory/GaloisGrp/GaloisGrp.jl +++ b/src/NumberTheory/GaloisGrp/GaloisGrp.jl @@ -894,7 +894,7 @@ function set_orbit(G::PermGroup, H::PermGroup) # http://dblp.uni-trier.de/db/journals/jsc/jsc79.html#Elsenhans17 # https://doi.org/10.1016/j.jsc.2016.02.005 - l = low_index_subgroup_reps(H, 2*degree(G)^2) + l = representative.(low_index_subgroup_classes(H, 2*degree(G)^2)) S, g = slpoly_ring(ZZ, degree(G), cached = false) sort!(l, lt = (a,b) -> isless(order(b), order(a))) @@ -1227,7 +1227,7 @@ mutable struct DescentEnv #a more select choice of group.... function DescentEnv(G::PermGroup, f::GroupFilter = GroupFilter()) - s = maximal_subgroup_reps(G) + s = map(representative, maximal_subgroup_classes(G)) r = new() r.G = G @vprint :GaloisGroup 1 "starting with $(length(s)) maximal subgroup classes\n" @@ -2374,7 +2374,7 @@ function galois_quotient(C::GaloisCtx, Q::PermGroup) if order(G) % degree(Q) != 0 return [] end - s = subgroup_reps(G, order = divexact(order(G), degree(Q))) + s = map(representative, subgroup_classes(G, order = divexact(order(G), degree(Q)))) res = [] for U = s phi = right_coset_action(G, U) @@ -2413,7 +2413,7 @@ function galois_quotient(C::GaloisCtx, d::Int) if order(G) % d != 0 return [] end - s = subgroup_reps(G, order = divexact(order(G), d)) + s = map(representative, subgroup_classes(G, order = divexact(order(G), d))) res = [] for U = s phi = right_coset_action(G, U) diff --git a/src/NumberTheory/GaloisGrp/Group.jl b/src/NumberTheory/GaloisGrp/Group.jl index dd50d7e76e9d..a441deac9639 100644 --- a/src/NumberTheory/GaloisGrp/Group.jl +++ b/src/NumberTheory/GaloisGrp/Group.jl @@ -7,7 +7,7 @@ minimal supergroups, ie. it fails in ``C_4``. function maximal_subgroup_chain(G::PermGroup, U::PermGroup) l = [G] while order(l[end]) > order(U) - m = maximal_subgroups(l[end]) + m = collect(maximal_subgroups(l[end])) push!(l, m[findfirst(x -> is_subset(U, x), m)]) end return reverse(l) diff --git a/src/deprecations.jl b/src/deprecations.jl index 9c7b6a37fbb1..eaaee4742b52 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -73,3 +73,21 @@ Base.@deprecate_binding jacobi_ideal jacobian_ideal @deprecate factorisations factorizations @deprecate centraliser centralizer + +@deprecate hall_subgroup_reps(G::T, P::AbstractVector{<:IntegerUnion}) where T <: Union{GAPGroup, FinGenAbGroup} map(representative, hall_subgroup_classes(G, P)) +@deprecate hall_subgroups_representatives(G::GAPGroup, P::AbstractVector{<:IntegerUnion}) map(representative, hall_subgroup_classes(G, P)) + +function hall_subgroup(G::T, P::AbstractVector{<:IntegerUnion}) where T <: Union{GAPGroup, FinGenAbGroup} + Base.depwarn("The function hall_subgroup is deprecated. Please use hall_subgroup_classes.", :hall_subgroup) + @req is_solvable(G) "The group is not solvable" + return representative(hall_subgroup_classes(G, P)[1]) +end + +@deprecate low_index_subgroup_reps(G::T, n::Int) where T <: Union{GAPGroup, FinGenAbGroup} map(representative, low_index_subgroup_classes(G, n)) + +@deprecate complement_class_reps(G::T, N::T) where T <: GAPGroup map(representative, complement_classes(G, N)) + +@deprecate maximal_subgroup_reps(G::T) where T <: Union{GAPGroup, FinGenAbGroup} map(representative, maximal_subgroup_classes(G)) +@deprecate subgroup_reps(G::T) where T <: Union{GAPGroup, FinGenAbGroup} map(representative, subgroup_classes(G)) +@deprecate conjugacy_classes_maximal_subgroups(G::T) where T <: Union{GAPGroup, FinGenAbGroup} maximal_subgroup_classes(G) +@deprecate conjugacy_classes_subgroups(G::T) where T <: Union{GAPGroup, FinGenAbGroup} subgroup_classes(G) diff --git a/src/exports.jl b/src/exports.jl index 00e174ec89f7..6c764fdbddb7 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -348,7 +348,7 @@ export comm! export common_components export common_refinement export complement -export complement_class_reps +export complement_classes export complement_equation export complement_equations export complement_ideal @@ -356,6 +356,7 @@ export complement_of_point_ideal export complement_of_prime_ideal export complement_scheme export complement_system, has_complement_system, set_complement_system +export complements export complete_bipartite_graph export complete_graph export complete_intersection_germ @@ -369,7 +370,6 @@ export cone_from_inequalities export cones export conjugacy_class export conjugacy_classes -export conjugacy_classes_maximal_subgroups export conjugacy_classes_subgroups export conjugate export conjugate_group @@ -608,7 +608,8 @@ export h_vector export halfspace export halfspace_matrix_pair export hall_subgroup -export hall_subgroup_reps +export hall_subgroup_classes +export hall_subgroups export hall_system, has_hall_system, set_hall_system export has_du_val_singularities export has_edge @@ -943,7 +944,8 @@ export load_mps export localization export localized_ring export loops -export low_index_subgroup_reps +export low_index_subgroup_classes +export low_index_subgroups export lower_central_series, has_lower_central_series, set_lower_central_series export lower_triangular_matrix export map @@ -981,8 +983,8 @@ export maximal_extension export maximal_groebner_cone export maximal_normal_subgroups, has_maximal_normal_subgroups, set_maximal_normal_subgroups export maximal_polyhedra, maximal_polyhedra_and_multiplicities -export maximal_subgroup_reps -export maximal_subgroups, has_maximal_subgroups, set_maximal_subgroups +export maximal_subgroup_classes, has_maximal_subgroup_classes, set_maximal_subgroup_classes +export maximal_subgroups export metadata export milnor_algebra export milnor_number @@ -1394,7 +1396,7 @@ export structure_sheaf export sub export subalgebra_membership export subalgebra_membership_homogeneous -export subgroup_reps +export subgroup_classes export subquo_type export subquotient export subscheme diff --git a/test/Groups/GrpAb.jl b/test/Groups/GrpAb.jl index 21566828931b..7e3d2b30cd1e 100644 --- a/test/Groups/GrpAb.jl +++ b/test/Groups/GrpAb.jl @@ -83,8 +83,9 @@ end end # conjugacy classes of subgroups - CC = conjugacy_classes_subgroups(G1) - @test length(CC) == length(conjugacy_classes_subgroups(G2)) + CC = subgroup_classes(G1) + @test length(CC) == length(subgroup_classes(G2)) + @test length(CC) == length(collect(subgroups(G1))) @test all(C -> length(C) == 1, CC) @test rand(CC[1]) == representative(CC[1]) @test acting_group(CC[1]) == G1 @@ -105,22 +106,24 @@ end for H in C @test H == representative(C) end - S1 = subgroup_reps(G1) - S2 = subgroup_reps(G2) - @test sort!([length(x) for x in S1]) == sort!([length(x) for x in S2]) + S1 = map(representative, subgroup_classes(G1)) + S2 = map(representative, subgroup_classes(G2)) + @test sort!([order(x) for x in S1]) == sort!([order(x) for x in S2]) for n in 2:4 - S1 = subgroup_reps(G1, order = ZZ(n)) - S2 = subgroup_reps(G2, order = ZZ(n)) + S1 = subgroup_classes(G1, order = n) + S2 = subgroup_classes(G2, order = n) @test length(S1) == length(S2) end for n in 1:4 - S1 = low_index_subgroup_reps(G1, n) - S2 = low_index_subgroup_reps(G2, n) + S1 = low_index_subgroup_classes(G1, n) + S2 = low_index_subgroup_classes(G2, n) @test length(S1) == length(S2) + @test length(S1) == length(collect(low_index_subgroups(G1, n))) end - S1 = conjugacy_classes_maximal_subgroups(G1) - S2 = conjugacy_classes_maximal_subgroups(G2) + S1 = maximal_subgroup_classes(G1) + S2 = maximal_subgroup_classes(G2) @test sort!([length(x) for x in S1]) == sort!([length(x) for x in S2]) + @test length(S1) == length(collect(maximal_subgroups(G1))) # operations x = representative(rand(cc)) @@ -138,8 +141,10 @@ end # operations depending on sets of primes for P in subsets(Set(primes)) - @test [images(iso, S)[1] for S in hall_subgroup_reps(G1, collect(P))] == - hall_subgroup_reps(G2, collect(P)) + @test [images(iso, representative(C))[1] for C in hall_subgroup_classes(G1, collect(P))] == + map(representative, hall_subgroup_classes(G2, collect(P))) + @test [images(iso, C)[1] for C in hall_subgroups(G1, collect(P))] == + collect(hall_subgroups(G2, collect(P))) end @test sort!([order(images(iso, S)[1]) for S in hall_system(G1)]) == sort!([order(S) for S in hall_system(G2)]) diff --git a/test/Groups/conjugation.jl b/test/Groups/conjugation.jl index a355849a804b..6b9e045e7228 100644 --- a/test/Groups/conjugation.jl +++ b/test/Groups/conjugation.jl @@ -70,7 +70,9 @@ @test !is_conjugate_with_data(G,x,y)[1] end - CC = @inferred conjugacy_classes_subgroups(G) + CC5 = @inferred subgroup_classes(G, order = 5) + @test length(CC5) == 0 + CC = @inferred subgroup_classes(G) @test length(CC)==11 @test all(cc -> acting_group(cc) === G, CC) @testset for C in CC @@ -78,8 +80,8 @@ @test length(C) == index(G, normalizer(G, representative(C))[1]) @test degree(representative(C)) == degree(G) end - H=rand(subgroups(G)) - @test sum([length(c) for c in CC]) == length(subgroups(G)) + H = rand(rand(CC)) + @test sum([length(c) for c in CC]) == length(collect(Iterators.flatten(CC))) @test count(c -> H in c, CC) == 1 # H belongs to a unique conjugacy class @testset for i in 1:length(CC) c = CC[i] @@ -98,7 +100,7 @@ @test !is_conjugate_with_data(G,x,y)[1] end - CC = @inferred conjugacy_classes_maximal_subgroups(G) + CC = @inferred maximal_subgroup_classes(G) @test length(CC)==3 @test Set([order(Int, representative(l)) for l in CC])==Set([6,8,12]) @@ -107,8 +109,10 @@ @test normalizer(G,H)==normalizer(G,x) G = symmetric_group(5) - CC = @inferred conjugacy_classes_maximal_subgroups(G) - all(H -> degree(H) == degree(G), map(representative, CC)) + CC = @inferred maximal_subgroup_classes(G) + @test all(H -> degree(H) == degree(G), map(representative, CC)) + @test all(H -> is_maximal_subgroup(H, G), map(representative, CC)) + @test !is_maximal_subgroup(trivial_subgroup(G)[1], G) G = symmetric_group(10) x = rand(G) diff --git a/test/Groups/elements.jl b/test/Groups/elements.jl index c08788d96fee..e72549e4bff0 100644 --- a/test/Groups/elements.jl +++ b/test/Groups/elements.jl @@ -80,7 +80,7 @@ end FPGroup(small_group(2, 1)) GL(2,2) ] - H = first(subgroups(G)) + H = rand(rand(subgroup_classes(G))) @test parent(one(H)) === H @test parent(G(one(H))) === G end diff --git a/test/Groups/matrixgroups.jl b/test/Groups/matrixgroups.jl index dbba1fd97ccf..320606ddc62d 100644 --- a/test/Groups/matrixgroups.jl +++ b/test/Groups/matrixgroups.jl @@ -579,8 +579,8 @@ end G = GL(2,3) @test length(conjugacy_classes(G))==8 - @test length(@inferred conjugacy_classes_subgroups(G))==16 - @test length(@inferred conjugacy_classes_maximal_subgroups(G))==3 + @test length(@inferred subgroup_classes(G))==16 + @test length(@inferred maximal_subgroup_classes(G))==3 end @testset "Jordan structure" begin diff --git a/test/Groups/subgroups_and_cosets.jl b/test/Groups/subgroups_and_cosets.jl index be907ec70a18..9bd878678c68 100644 --- a/test/Groups/subgroups_and_cosets.jl +++ b/test/Groups/subgroups_and_cosets.jl @@ -35,7 +35,7 @@ G = symmetric_group(4) A = alternating_group(4) - L = subgroups(G) + L = collect(subgroups(G)) @test length(L)==30 @test L[1] isa PermGroup L1 = [x for x in L if is_normal_subgroup(x, G)] @@ -44,8 +44,9 @@ for H in L1 @test H in K end - @test length(maximal_subgroups(G))==8 - @test A in maximal_subgroups(G) + @test length(maximal_subgroup_classes(G)) == 3 + @test sum(map(length, maximal_subgroup_classes(G))) == 8 + @test any(C -> A in C, maximal_subgroup_classes(G)) @test maximal_normal_subgroups(G)==[A] H = sub(G,[G([3,4,1,2]), G([2,1,4,3])])[1] @test minimal_normal_subgroups(G)==[H] @@ -327,15 +328,15 @@ end end L = [[2],[3],[5],[7],[2,3],[2,5],[2,7],[3,5],[3,7],[5,7],[2,3,5],[2,3,7],[2,5,7],[3,5,7],[2,3,5,7]] @testset for l in L - h = hall_subgroup_reps(G, l) + h = hall_subgroup_classes(G, l) @test length(h) == 1 - @test h[1] == sub(G,[g^(210÷lcm(l))])[1] + @test representative(h[1]) == sub(G,[g^(210÷lcm(l))])[1] end - h = hall_subgroup_reps(G, Int64[]) + h = hall_subgroup_classes(G, Int64[]) @test length(h) == 1 - @test h[1] == sub(G, [one(G)])[1] - @test length(hall_subgroup_reps(symmetric_group(5), [2, 5])) == 0 - @test_throws ArgumentError hall_subgroup_reps(G, [4]) + @test representative(h[1]) == sub(G, [one(G)])[1] + @test length(hall_subgroup_classes(symmetric_group(5), [2, 5])) == 0 + @test_throws ArgumentError hall_subgroup_classes(G, [4]) L = sylow_system(G) Lo = [order(l) for l in L] @@ -359,26 +360,26 @@ end # solvable group G = symmetric_group(4) N = pcore(G, 2)[1] - @test length(complement_class_reps(G, N)) == 1 + @test length(complement_classes(G, N)) == 1 # nonsolvable factor group G = special_linear_group(2, 5) N = center(G)[1] - @test length(complement_class_reps(G, N)) == 0 + @test length(complement_classes(G, N)) == 0 # nonsolvable normal subgroup G = symmetric_group(6) N = derived_subgroup(G)[1] - @test length(complement_class_reps(G, N)) == 2 + @test length(complement_classes(G, N)) == 2 # both normal subgroup and factor group nonsolvable: # check that GAP throws an error # (if not then perhaps a statement in the documentation of - # `complement_class_reps` can be changed) + # `complement_classes` can be changed) G = alternating_group(5) W = wreath_product(G, G) N = kernel(canonical_projection(W))[1] - @test_throws ErrorException complement_class_reps(W, N) + @test_throws ErrorException complement_classes(W, N) end @testset "Some specific subgroups" begin @@ -388,7 +389,7 @@ end @test order(fitting_subgroup(G)[1])==8 @test fitting_subgroup(S)==sub(S,[S([3,4,1,2]), S([4,3,2,1])]) @test frattini_subgroup(S)==sub(S,[one(S)]) - @test frattini_subgroup(G)[1]==intersect(maximal_subgroups(G))[1] + @test frattini_subgroup(G)[1]==intersect(collect(maximal_subgroups(G)))[1] @test frattini_subgroup(G)==center(G) @test is_characteristic_subgroup(center(G)[1], G) @test socle(G)==frattini_subgroup(G)