diff --git a/docs/src/Groups/subgroups.md b/docs/src/Groups/subgroups.md index 29df3e9bb26c..7310b4ceec69 100644 --- a/docs/src/Groups/subgroups.md +++ b/docs/src/Groups/subgroups.md @@ -67,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 diff --git a/src/Groups/GAPGroups.jl b/src/Groups/GAPGroups.jl index ea97d8d44793..7a0e9b4858ca 100644 --- a/src/Groups/GAPGroups.jl +++ b/src/Groups/GAPGroups.jl @@ -788,6 +788,23 @@ function subgroup_classes(G::GAPGroup; order::T = ZZRingElem(-1)) where T <: Int return [GAPGroupConjClass(G, _as_subgroup_bare(G, GAPWrap.Representative(cc)), cc) for cc in L] end +""" + subgroups(G::GAPGroup) + +Return an iterator of all subgroups in `G`. +Very likely it is better to use [`subgroup_classes`](@ref) instead. + +# Examples +```jldoctest +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] +``` +""" +subgroups(G::GAPGroup) = Iterators.flatten(subgroup_classes(G)) + """ maximal_subgroup_classes(G::Group) @@ -810,6 +827,23 @@ julia> maximal_subgroup_classes(G) return Vector{GAPGroupConjClass{T, T}}(LL) end +""" + maximal_subgroups(G::Group) + +Return an iterator of the maximal subgroups in `G`. +Very likely it is better to use [`maximal_subgroup_classes`](@ref) instead. + +# Examples +```jldoctest +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] +``` +""" +maximal_subgroups(G::T) where T <: Union{GAPGroup, FinGenAbGroup} = Iterators.flatten(maximal_subgroup_classes(G)) + """ low_index_subgroup_classes(G::GAPGroup, n::Int) @@ -832,6 +866,22 @@ function low_index_subgroup_classes(G::GAPGroup, n::Int) return [conjugacy_class(G, H) for H in _as_subgroups(G, ll)] end +""" + low_index_subgroups(G::Group, n::Int) + +Return an iterator of 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 @@ -1254,6 +1304,22 @@ function hall_subgroup_classes(G::GAPGroup, P::AbstractVector{<:IntegerUnion}) end end +""" + hall_subgroups(G::Group, P::AbstractVector{<:IntegerUnion}) + +Return an iterator of 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) @@ -1304,6 +1370,22 @@ function complement_classes(G::T, N::T) where T <: GAPGroup end end +@doc raw""" + complements(G::T, N::T) where T <: GAPGroup + +Return an iterator of 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/sub.jl b/src/Groups/sub.jl index 9588a8657854..fdd1549b772e 100644 --- a/src/Groups/sub.jl +++ b/src/Groups/sub.jl @@ -455,7 +455,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). @@ -554,9 +554,12 @@ 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 + ind = index(G, H) + is_prime(ind) && return true + if ind < 100 + # Do not unpack the right transversal object. t = right_transversal(G, H)[2:end] #drop the identity - return all(x -> order(sub(G, vcat(gens(H), [x]))[1]) == order(G), t) + return all(i -> order(sub(G, vcat(gens(H), [t[i]]))[1]) == order(G), 2:ind) end return any(C -> H in C, maximal_subgroup_classes(G)) end diff --git a/src/exports.jl b/src/exports.jl index 935d8dba8e5c..7438e9d199d7 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -357,6 +357,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 @@ -611,6 +612,7 @@ export halfspace export halfspace_matrix_pair export hall_subgroup export hall_subgroup_classes +export hall_subgroups export hall_system, has_hall_system, set_hall_system export has_du_val_singularities export has_edge @@ -944,6 +946,7 @@ export localization export localized_ring export loops 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 @@ -982,6 +985,7 @@ 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_classes, has_maximal_subgroup_classes, set_maximal_subgroup_classes +export maximal_subgroups export metadata export milnor_algebra export milnor_number diff --git a/test/Groups/GrpAb.jl b/test/Groups/GrpAb.jl index 289e9cbc616b..7e3d2b30cd1e 100644 --- a/test/Groups/GrpAb.jl +++ b/test/Groups/GrpAb.jl @@ -85,6 +85,7 @@ end # conjugacy classes of subgroups 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 @@ -117,10 +118,12 @@ end 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 = 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)) @@ -140,6 +143,8 @@ end for P in subsets(Set(primes)) @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 40dd0a661799..7dafd1b0450b 100644 --- a/test/Groups/conjugation.jl +++ b/test/Groups/conjugation.jl @@ -108,7 +108,9 @@ G = symmetric_group(5) CC = @inferred maximal_subgroup_classes(G) - all(H -> degree(H) == degree(G), map(representative, CC)) + @test all(H -> degree(H) == degree(G), map(representative, CC)) + @test all(H -> is_maximal_subgroup(G, H), map(representative, CC)) + @test !is_maximal_subgroup(G, trivial_subgroup(G)[1]) G = symmetric_group(10) x = rand(G)