Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce SubPcGroup #3166

Merged
merged 18 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading