Skip to content

Commit

Permalink
introduce SubPcGroup (#3166)
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomasBreuer authored May 15, 2024
1 parent fe84dfc commit c06c1b5
Show file tree
Hide file tree
Showing 30 changed files with 655 additions and 265 deletions.
2 changes: 1 addition & 1 deletion docs/src/Groups/grouphom.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ isomorphism(G::GAPGroup, H::GAPGroup)
```

```@docs
isomorphism(::Type{T}, G::GAPGroup) where T <: Union{PcGroup, PermGroup}
isomorphism(::Type{T}, G::GAPGroup) where T <: Union{SubPcGroup, PermGroup}
isomorphism(::Type{FinGenAbGroup}, G::GAPGroup)
simplified_fp_group(G::FPGroup)
```
Expand Down
2 changes: 1 addition & 1 deletion docs/src/Groups/quotients.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Quotient groups in OSCAR can be defined using the instruction `quo` in two ways.

* Quotients by normal subgroups.
```@docs
quo(G::T, H::T) where T <: GAPGroup
quo(G::GAPGroup, N::GAPGroup)
```

* Quotients by elements.
Expand Down
40 changes: 20 additions & 20 deletions docs/src/Groups/subgroups.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ The following functions are available in OSCAR for subgroup properties:

```@docs
sub(G::GAPGroup, gens::AbstractVector{<:GAPGroupElem}; check::Bool = true)
is_subset(H::T, G::T) where T <: GAPGroup
is_subgroup(H::T, G::T) where T <: GAPGroup
embedding(H::T, G::T) where T <: GAPGroup
index(G::T, H::T) where T <: Union{GAPGroup, FinGenAbGroup}
is_maximal_subgroup(H::T, G::T) where T <: GAPGroup
is_normalized_by(H::T, G::T) where T <: GAPGroup
is_normal_subgroup(H::T, G::T) where T <: GAPGroup
is_characteristic_subgroup(H::T, G::T) where T <: GAPGroup
is_subset(H::GAPGroup, G::GAPGroup)
is_subgroup(H::GAPGroup, G::GAPGroup)
embedding(H::GAPGroup, G::GAPGroup)
index(G::GAPGroup, H::GAPGroup)
is_maximal_subgroup(H::GAPGroup, G::GAPGroup; check::Bool = true)
is_normalized_by(H::GAPGroup , G::GAPGroup)
is_normal_subgroup(H::GAPGroup, G::GAPGroup)
is_characteristic_subgroup(H::GAPGroup, G::GAPGroup; check::Bool = true)
```

## Standard subgroups
Expand Down Expand Up @@ -72,7 +72,7 @@ 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
complements(G::GAPGroup, N::GAPGroup)
hall_subgroups
low_index_subgroups
maximal_subgroups
Expand All @@ -87,11 +87,11 @@ is_conjugate(G::GAPGroup, H::GAPGroup, K::GAPGroup)
is_conjugate_with_data(G::GAPGroup, x::GAPGroupElem, y::GAPGroupElem)
is_conjugate_with_data(G::GAPGroup, H::GAPGroup, K::GAPGroup)
centralizer(G::GAPGroup, x::GAPGroupElem)
centralizer(G::T, H::T) where T <: GAPGroup
centralizer(G::GAPGroup, H::GAPGroup)
normalizer(G::GAPGroup, x::GAPGroupElem)
normalizer(G::T, H::T) where T<:GAPGroup
core(G::T, H::T) where T<:GAPGroup
normal_closure(G::T, H::T) where T<:GAPGroup
normalizer(G::GAPGroup, H::GAPGroup)
core(G::GAPGroup, H::GAPGroup)
normal_closure(G::GAPGroup, H::GAPGroup)
```

```@docs
Expand All @@ -100,7 +100,7 @@ representative(G::GroupConjClass)
acting_group(G::GroupConjClass)
number_of_conjugacy_classes(G::GAPGroup)
conjugacy_class(G::GAPGroup, g::GAPGroupElem)
conjugacy_class(G::T, g::T) where T<:GAPGroup
conjugacy_class(G::GAPGroup, H::GAPGroup)
conjugacy_classes(G::GAPGroup)
complement_classes
hall_subgroup_classes
Expand All @@ -121,13 +121,13 @@ is_left(c::GroupCoset)
is_bicoset(C::GroupCoset)
acting_domain(C::GroupCoset)
representative(C::GroupCoset)
right_cosets(G::T, H::T; check::Bool=true) where T<: GAPGroup
left_cosets(G::T, H::T; check::Bool=true) where T<: GAPGroup
right_transversal(G::T, H::T; check::Bool=true) where T<: GAPGroup
left_transversal(G::T, H::T; check::Bool=true) where T<: GAPGroup
right_cosets(G::GAPGroup, H::GAPGroup; check::Bool=true)
left_cosets(G::GAPGroup, H::GAPGroup; check::Bool=true)
right_transversal(G::T1, H::T2; check::Bool=true) where T1 <: GAPGroup where T2 <: GAPGroup
left_transversal(G::T1, H::T2; check::Bool=true) where T1 <: GAPGroup where T2 <: GAPGroup
GroupDoubleCoset{T <: GAPGroup, S <: GAPGroupElem}
double_coset(G::T, g::GAPGroupElem{T}, H::T) where T<: GAPGroup
double_cosets(G::T, H::T, K::T; check::Bool) where T<: GAPGroup
double_coset(G::GAPGroup, g::GAPGroupElem, H::GAPGroup)
double_cosets(G::T, H::GAPGroup, K::GAPGroup; check::Bool=true) where T <: GAPGroup
left_acting_group(C::GroupDoubleCoset)
right_acting_group(C::GroupDoubleCoset)
representative(C::GroupDoubleCoset)
Expand Down
2 changes: 1 addition & 1 deletion experimental/GModule/Brueckner.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function reps(K, G::Oscar.GAPGroup)
r = R[pos]
F = r.M
@assert group(r) == s
rh = gmodule(group(r), [action(r, preimage(ms, x^h)) for x = gens(s)])
rh = gmodule(group(r), [action(r, preimage(ms, ms(x)^h)) for x = gens(s)])
@hassert :BruecknerSQ 2 Oscar.GrpCoh.is_consistent(rh)
l = Oscar.GModuleFromGap.hom_base(r, rh)
@assert length(l) <= 1
Expand Down
6 changes: 3 additions & 3 deletions experimental/SymmetricIntersections/src/representations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ function is_faithful(chi::Oscar.GAPGroupClassFunction, p::GAPGroupHomomorphism{T
E = group(parent(chi))::T
@req E === domain(p) "Incompatible underlying group of chi and domain of the cover p"
@req is_projective(chi, p) "chi is not afforded by a p-projective representation"
Z = center(chi)[1]::T
Z = center(chi)[1]
Q = kernel(p)[1]
return Q.X == Z.X
end
Expand Down Expand Up @@ -1070,8 +1070,8 @@ function _has_pfr(G::Oscar.GAPGroup, dim::Int)
fff_gap = GG.IsomorphismPermGroup(H_gap)::GAP.GapObj
E_gap = fff_gap(H_gap)::GAP.GapObj
end
E = Oscar._get_type(E_gap)(E_gap)
H = Oscar._get_type(H_gap)(H_gap)
E = Oscar._oscar_group(E_gap)
H = Oscar._oscar_group(H_gap)
fff = inv(GAPGroupHomomorphism(H, E, fff_gap))
f = GAPGroupHomomorphism(H, G, f_gap)
pschur = compose(fff, f)
Expand Down
6 changes: 6 additions & 0 deletions gap/OscarInterface/gap/OscarInterface.gd
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ BindGlobal("IsPcGroupOrPcpGroup", IsGroup and CategoryCollections(IsPcElementOrP

############################################################################

# Use a GAP property for caching whether a fp/pc/pcp group is a full group
# and its stored generators are the generators for the defining presentation.
DeclareProperty( "GroupGeneratorsDefinePresentation", IsGroup );

############################################################################

# Use GAP operations for the serialization of GAP objects.
# (The methods will be Julia functions.)
DeclareOperation( "SerializeInOscar", [ IsObject, IsObject ] );
Expand Down
26 changes: 26 additions & 0 deletions gap/OscarInterface/gap/OscarInterface.gi
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,32 @@ InstallMethod( IsGeneratorsOfMagmaWithInverses,

############################################################################

InstallMethod( GroupGeneratorsDefinePresentation,
[ "IsPcGroup" ],
G -> GeneratorsOfGroup(G) = FamilyPcgs(G) );

InstallMethod( GroupGeneratorsDefinePresentation,
[ "IsPcpGroup" ],
function( G )
local n, Ggens, i, w;

n:= One( G )!.collector![ PC_NUMBER_OF_GENERATORS ];
Ggens:= GeneratorsOfGroup( G );
if Length( Ggens ) <> n then
return false;
fi;
for i in [ 1 .. n ] do
w:= Ggens[i]!.word;
if not ( Length(w) = 2 and w[1] = i and w[2] = 1 ) then
return false;
fi;
od;
return true;
end );

############################################################################


Perform( Oscar._GAP_serializations,
function( entry )
InstallMethod( SerializeInOscar,
Expand Down
2 changes: 2 additions & 0 deletions src/GAP/wrappers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ GAP.@wrap Identity(x::GapObj)::GapObj
GAP.@wrap Image(x::Any)::GapObj
GAP.@wrap Image(x::Any, y::Any)::GapObj
GAP.@wrap ImagesRepresentative(x::GapObj, y::Any)::GAP.Obj
GAP.@wrap ImagesSource(x::GapObj)::GapObj
GAP.@wrap ImmutableMatrix(x::GapObj, y::GapObj, z::Bool)::GapObj
GAP.@wrap IndependentGeneratorExponents(x::Any, y::Any)::GapObj
GAP.@wrap Indeterminate(x::GapObj)::GapObj
Expand Down Expand Up @@ -192,6 +193,7 @@ GAP.@wrap IsomorphismFpGroupByGenerators(x::GapObj, y::GapObj)::GapObj
GAP.@wrap IsomorphismFpGroupByPcgs(x::GapObj, y::GapObj)::GapObj
GAP.@wrap IsOne(x::Any)::Bool
GAP.@wrap IsPcGroup(x::Any)::Bool
GAP.@wrap IsPcpGroup(x::Any)::Bool
GAP.@wrap IsPerfectGroup(x::Any)::Bool
GAP.@wrap IsPermGroup(x::Any)::Bool
GAP.@wrap IsPGroup(x::Any)::Bool
Expand Down
60 changes: 36 additions & 24 deletions src/Groups/GAPGroups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ function Base.rand(rng::Random.AbstractRNG, G::GAPGroup)
return group_element(G, s)
end

function Base.rand(rng::Random.AbstractRNG, rs::Random.SamplerTrivial{Gr}) where Gr<:Oscar.GAPGroup
function Base.rand(rng::Random.AbstractRNG, rs::Random.SamplerTrivial{Gr}) where Gr<:GAPGroup
return rand(rng, rs[])
end

Expand Down Expand Up @@ -234,6 +234,9 @@ end

#We need a lattice of groups to implement this properly
function _prod(x::T, y::T) where T <: GAPGroupElem
#T not nec. same type,
#T and for pc subgroups may need to go to the big group
#T (write tests that model this situation)
G = _common_parent_group(parent(x), parent(y))
return group_element(G, GapObj(x)*GapObj(y))
end
Expand All @@ -242,7 +245,7 @@ Base.:*(x::GAPGroupElem, y::GAPGroupElem) = _prod(x, y)

==(x::GAPGroup, y::GAPGroup) = GapObj(x) == GapObj(y)

==(x::T, y::T) where T <: BasicGAPGroupElem = GapObj(x) == GapObj(y)
==(x::BasicGAPGroupElem, y::BasicGAPGroupElem ) = GapObj(x) == GapObj(y)

"""
one(G::GAPGroup) -> elem_type(G)
Expand Down Expand Up @@ -286,6 +289,7 @@ function Base.show(io::IO, G::FPGroup)
end
else
print(io, "Finitely presented group") # FIXME: actually some of these groups are *not* finitely presented
#T introduce SubFPGroup
if !is_terse(io)
if has_order(G)
if is_finite(G)
Expand Down Expand Up @@ -330,10 +334,11 @@ function Base.show(io::IO, G::PermGroup)
end
end

function Base.show(io::IO, G::PcGroup)
function Base.show(io::IO, G::Union{PcGroup,SubPcGroup})
@show_name(io, G)
@show_special(io, G)
print(io, "Pc group")
T = typeof(G) == PcGroup ? "Pc group" : "Subgroup of pc group"
print(io, T)
if !is_terse(io)
if isfinite(G)
print(io, " of order ", order(G))
Expand Down Expand Up @@ -489,7 +494,7 @@ in general the length of this vector is not minimal.
# Examples
```jldoctest
julia> length(small_generating_set(abelian_group(PcGroup, [2,3,4])))
julia> length(small_generating_set(abelian_group(SubPcGroup, [2,3,4])))
2
julia> length(small_generating_set(abelian_group(PermGroup, [2,3,4])))
Expand All @@ -512,7 +517,7 @@ Return a vector of minimal length of elements in `G` that generate `G`.
# Examples
```jldoctest
julia> length(minimal_generating_set(abelian_group(PcGroup, [2,3,4])))
julia> length(minimal_generating_set(abelian_group(SubPcGroup, [2,3,4])))
2
julia> length(minimal_generating_set(abelian_group(PermGroup, [2,3,4])))
Expand Down Expand Up @@ -760,20 +765,21 @@ end

# START subgroups conjugation
"""
conjugacy_class(G::T, H::T) where T<:Group -> GroupConjClass
conjugacy_class(G::Group, H::Group) -> GroupConjClass
Return the subgroup conjugacy class `cc` of `H` in `G`, where `H` = `representative`(`cc`).
"""
function conjugacy_class(G::T, g::T) where T<:GAPGroup
return GAPGroupConjClass(G, g, GAPWrap.ConjugacyClassSubgroups(GapObj(G),GapObj(g)))
function conjugacy_class(G::GAPGroup, H::GAPGroup)
#T _check_compatible
return GAPGroupConjClass(G, H, GAPWrap.ConjugacyClassSubgroups(GapObj(G),GapObj(H)))
end

function Base.rand(C::GroupConjClass{S,T}) where S where T<:GAPGroup
return Base.rand(Random.GLOBAL_RNG, C)
end

function Base.rand(rng::Random.AbstractRNG, C::GroupConjClass{S,T}) where S where T<:GAPGroup
return _oscar_group(GAP.Globals.Random(GAP.wrap_rng(rng), C.CC), acting_group(C))
return _oscar_subgroup(GAP.Globals.Random(GAP.wrap_rng(rng), C.CC), acting_group(C))
end

"""
Expand Down Expand Up @@ -842,9 +848,10 @@ julia> maximal_subgroup_classes(G)
"""
@gapattribute function maximal_subgroup_classes(G::GAPGroup)
L = Vector{GapObj}(GAP.Globals.ConjugacyClassesMaximalSubgroups(GapObj(G))::GapObj)
T = typeof(G)
TG = typeof(G)
TS = sub_type(TG)
LL = [GAPGroupConjClass(G, _as_subgroup_bare(G, GAPWrap.Representative(cc)), cc) for cc in L]
return Vector{GAPGroupConjClass{T, T}}(LL)
return Vector{GAPGroupConjClass{TG, TS}}(LL)
end

"""
Expand Down Expand Up @@ -921,7 +928,7 @@ Permutation group of degree 4 and order 3
"""
function conjugate_group(G::T, x::GAPGroupElem) where T <: GAPGroup
@req check_parent(G, x) "G and x are not compatible"
return _oscar_group(GAPWrap.ConjugateSubgroup(GapObj(G), GapObj(x)), G)
return _oscar_subgroup(GAPWrap.ConjugateSubgroup(GapObj(G), GapObj(x)), G)
end

Base.:^(H::GAPGroup, y::GAPGroupElem) = conjugate_group(H, y)
Expand Down Expand Up @@ -1152,7 +1159,7 @@ Return `N, f`, where `N` is the normalizer of `H` in `G`,
i.e., the largest subgroup of `G` in which `H` is normal,
and `f` is the embedding morphism of `N` into `G`.
"""
normalizer(G::T, H::T) where T<:GAPGroup = _as_subgroup(G, GAPWrap.Normalizer(GapObj(G), GapObj(H)))
normalizer(G::GAPGroup, H::GAPGroup) = _as_subgroup(G, GAPWrap.Normalizer(GapObj(G), GapObj(H)))

"""
normalizer(G::Group, x::GAPGroupElem)
Expand All @@ -1169,7 +1176,7 @@ Return `C, f`, where `C` is the normal core of `H` in `G`,
that is, the largest normal subgroup of `G` that is contained in `H`,
and `f` is the embedding morphism of `C` into `G`.
"""
core(G::T, H::T) where T<:GAPGroup = _as_subgroup(G, GAPWrap.Core(GapObj(G), GapObj(H)))
core(G::GAPGroup, H::GAPGroup) = _as_subgroup(G, GAPWrap.Core(GapObj(G), GapObj(H)))

"""
normal_closure(G::Group, H::Group)
Expand All @@ -1180,7 +1187,7 @@ and `f` is the embedding morphism of `N` into `G`.
Note that `H` must be a subgroup of `G`.
"""
normal_closure(G::T, H::T) where T<:GAPGroup = _as_subgroup(G, GAPWrap.NormalClosure(GapObj(G), GapObj(H)))
normal_closure(G::GAPGroup, H::GAPGroup) = _as_subgroup(G, GAPWrap.NormalClosure(GapObj(G), GapObj(H)))

# Note:
# GAP admits `NormalClosure` also when `H` is not a subgroup of `G`,
Expand Down Expand Up @@ -1361,7 +1368,7 @@ an exception is thrown if `G` is not solvable.
end

@doc raw"""
complement_classes(G::T, N::T) where T <: GAPGroup
complement_classes(G::GAPGroup, N::GAPGroup)
Return a vector of the conjugacy classes of complements
of the normal subgroup `N` in `G`.
Expand All @@ -1383,20 +1390,20 @@ julia> G = dihedral_group(8)
Pc group of order 8
julia> complement_classes(G, center(G)[1])
GAPGroupConjClass{PcGroup, PcGroup}[]
GAPGroupConjClass{PcGroup, SubPcGroup}[]
```
"""
function complement_classes(G::T, N::T) where T <: GAPGroup
function complement_classes(G::T, N::GAPGroup) where T <: GAPGroup
res_gap = GAP.Globals.ComplementClassesRepresentatives(GapObj(G), GapObj(N))::GapObj
if length(res_gap) == 0
return GAPGroupConjClass{T, T}[]
return GAPGroupConjClass{T, sub_type(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
complements(G::GAPGroup, N::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.
Expand All @@ -1409,7 +1416,7 @@ julia> describe(first(complements(G, derived_subgroup(G)[1])))
"C2"
```
"""
complements(G::T, N::T) where T <: GAPGroup = Iterators.flatten(complement_classes(G, N))
complements(G::GAPGroup, N::GAPGroup) = Iterators.flatten(complement_classes(G, N))

@doc raw"""
complement_system(G::Group)
Expand Down Expand Up @@ -1910,7 +1917,7 @@ function map_word(g::PcGroupElem, genimgs::Vector; genimgs_inv::Vector = Vector(
end
gX = GapObj(g)

if GAP.Globals.IsPcGroup(GapObj(G))
if GAPWrap.IsPcGroup(GapObj(G))
l = GAP.Globals.ExponentsOfPcElement(GAP.Globals.FamilyPcgs(GapObj(G)), gX)
else # GAP.Globals.IsPcpGroup(GapObj(G))
l = GAP.Globals.Exponents(gX)
Expand Down Expand Up @@ -2269,7 +2276,12 @@ function describe(G::FPGroup)

if !has_is_finite(G)
# try to obtain an isomorphic permutation group, but don't try too hard
iso = GAP.Globals.IsomorphismPermGroupOrFailFpGroup(GapObj(G), 100000)::GapObj
#TODO: With GAP 4.13.0, the prescribed bound 100000 will cause a test failure.
# This regression will hopefully be fixed in GAP 4.13.1,
# see https://github.com/gap-system/gap/issues/5697
# and https://github.com/gap-system/gap/pull/5698.
# iso = GAP.Globals.IsomorphismPermGroupOrFailFpGroup(GapObj(G), 100000)::GapObj
iso = GAP.Globals.IsomorphismPermGroupOrFailFpGroup(GapObj(G))::GapObj
iso != GAP.Globals.fail && return describe(PermGroup(GAPWrap.Range(iso)))
elseif is_finite(G)
return describe(PermGroup(G))
Expand Down
Loading

0 comments on commit c06c1b5

Please sign in to comment.