Skip to content

Commit

Permalink
Improve describe(G::GAPGroup)
Browse files Browse the repository at this point in the history
  • Loading branch information
fingolfin committed Mar 2, 2022
1 parent 695b9fe commit 88e0006
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 38 deletions.
145 changes: 107 additions & 38 deletions src/Groups/GAPGroups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -966,45 +966,33 @@ end
Return a string that describes some aspects of the structure of `G`.
This works well if `G` is an abelian group or a finite simple group
or a group in one of the following series:
symmetric, dihedral, quasidihedral, generalized quaternion,
general linear, special linear.
For other groups, the function tries to decompose `G` as a direct product
or a semidirect product,
and if this is not possible as a non-splitting extension of
a normal subgroup $N$ with the factor group `G`$/N$,
where $N$ is the center or the derived subgroup or the Frattini subgroup
of `G`.
Note that
- there is in general no "nice" decomposition of `G`,
- there may be several decompositions of `G`,
- nonisomorphic groups may yield the same `describe` result,
- isomorphic groups may yield different `describe` results,
- the computations can take a long time (for example in the case of
large $p$-groups), and the results are still often not very helpful.
For finite groups, the function works well if `G` is an abelian group or a
finite simple group or a group in one of the following series: symmetric,
dihedral, quasidihedral, generalized quaternion, general linear, special
linear.
For other finite groups, the function tries to decompose `G` as a direct
product or a semidirect product, and if this is not possible as a
non-splitting extension of a normal subgroup $N$ with the factor group
`G`$/N$, where $N$ is the center or the derived subgroup or the Frattini
subgroup of `G`.
For infinite groups or groups for which finiteness is not (yet) known (e.g.
finitely presented groups), it may not be possible to print much useful
information. We make a best-effort attempt, but in general this is a hard
problem.
# Examples
```jldoctest
julia> g = symmetric_group(6);
julia> describe(g)
"S6"
julia> describe(sylow_subgroup(g,2)[1])
"C2 x D8"
julia> describe(sylow_subgroup(g, 3)[1])
"C3 x C3"
julia> g = symmetric_group(10);
julia> describe(sylow_subgroup(g,2)[1])
"C2 x ((((C2 x C2 x C2 x C2) : C2) : C2) : C2)"
!!! note
- for finitely presented groups, even deciding if the group is trivial
is impossible in general; the same holds for most other properties,
like whether the group is finite, abelian, etc.
- there is in general no "nice" decomposition of `G`,
- there may be several decompositions of `G`,
- nonisomorphic groups may yield the same `describe` result,
- isomorphic groups may yield different `describe` results,
- the computations can take a long time (for example in the case of
large $p$-groups), and the results are still often not very helpful.
```
The following notation is used in the returned string.
| Description | Syntax |
Expand Down Expand Up @@ -1035,5 +1023,86 @@ The following notation is used in the returned string.
| direct product | A x B |
| semidirect product | N : H |
| non-split extension | Z(G) . G/Z(G) = G' . G/G', Phi(G) . G/Phi(G) |
# Examples
```jldoctest
julia> g = symmetric_group(6);
julia> describe(g)
"S6"
julia> describe(sylow_subgroup(g,2)[1])
"C2 x D8"
julia> describe(sylow_subgroup(g, 3)[1])
"C3 x C3"
julia> g = symmetric_group(10);
julia> describe(sylow_subgroup(g,2)[1])
"C2 x ((((C2 x C2 x C2 x C2) : C2) : C2) : C2)"
```
"""
describe(G::GAPGroup) = String(GAP.Globals.StructureDescription(G.X))
function describe(G::GAPGroup)
isfinitelygenerated(G) || return "a non-finitely generated group"

# handle groups whose finiteness is known
if hasisfinite(G)
# finite groups: pass them to GAP
if isfinite(G)
return String(GAP.Globals.StructureDescription(G.X)::GapObj)
end

# infinite groups known to be abelian can still be dealt with by GAP
if hasisabelian(G) && isabelian(G)
return String(GAP.Globals.StructureDescription(G.X)::GapObj)
end

return "an infinite group"
end

return "a group"
end

function describe(G::FPGroup)
# despite the name, there are non-finitely generated (and hence non-finitely presented) FPGroup instances
isfinitelygenerated(G) || return "a non-finitely generated group"

if GAP.Globals.IsFreeGroup(G.X)::Bool
r = GAP.Globals.RankOfFreeGroup(G.X)::GapInt
r >= 2 && return "a free group of rank $(r)"
r == 1 && return "Z"
r == 0 && return "1"
end

# check for free groups in disguise
isempty(relators(G)) && return describe(free_group(G))

# attempt to simplify presentation
H = simplified_fp_group(G)[1]
ngens(H) < ngens(G) && return describe(H)

# abelian groups can be dealt with by GAP
extra = ""
if !hasisabelian(G)
# TODO: perform some cheap tests???
elseif isabelian(G)
return String(GAP.Globals.StructureDescription(G.X)::GapObj)
else
extra *= " non-abelian"
end

if !hasisfinite(G)
# try to obtain an isomorphic permutation group, but don't try too hard
iso = GAP.Globals.IsomorphismPermGroupOrFailFpGroup(G.X, 100000)::GapObj
iso != GAP.Globals.fail && return describe(PermGroup(GAP.Globals.Range(iso)))
elseif isfinite(G)
return describe(isomorphic_perm_group(G)[1])
else
extra *= " infinite"
end

return "a finitely presented$(extra) group"

end
82 changes: 82 additions & 0 deletions test/Groups/describe.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
@testset "describe() for permutation groups" begin
@test describe(symmetric_group(1)) == "1"
@test describe(symmetric_group(2)) == "C2"
@test describe(symmetric_group(3)) == "S3"
@test describe(symmetric_group(4)) == "S4"
@test describe(symmetric_group(5)) == "S5"

@test describe(alternating_group(1)) == "1"
@test describe(alternating_group(2)) == "1"
@test describe(alternating_group(3)) == "C3"
@test describe(alternating_group(4)) == "A4"
@test describe(alternating_group(5)) == "A5"
end

@testset "describe() for pc groups" begin
@test describe(dihedral_group(2)) == "C2"
@test describe(dihedral_group(4)) == "C2 x C2"
@test describe(dihedral_group(6)) == "S3"
@test describe(dihedral_group(8)) == "D8"
@test describe(dihedral_group(10)) == "D10"
end

@testset "describe() for matrix groups" begin
@test describe(general_linear_group(2,2)) == "S3" # FIXME: correct but not ideal
@test describe(general_linear_group(2,3)) == "GL(2,3)"
@test describe(general_linear_group(2,4)) == "GL(2,4)"
@test describe(general_linear_group(3,2)) == "PSL(3,2)" # FIXME: correct but not ideal

@test describe(special_linear_group(2,2)) == "S3" # FIXME: correct but not ideal
@test describe(special_linear_group(2,3)) == "SL(2,3)"
@test describe(special_linear_group(2,4)) == "A5" # FIXME: correct but not ideal
@test describe(special_linear_group(3,2)) == "PSL(3,2)" # FIXME: correct but not ideal
@test describe(special_linear_group(3,3)) == "PSL(3,3)" # FIXME: correct but not ideal
@test describe(special_linear_group(4,2)) == "A8" # FIXME: correct but not ideal
end

@testset "describe() for projective groups" begin
# TODO: PGL / projective_general_linear_group constructor is missing
# TODO: PSL / projective_general_linear_group constructor is missing
end

@testset "describe() for free groups" begin
@test describe(free_group(0)) == "1"
@test describe(free_group(1)) == "Z"
@test describe(free_group(2)) == "a free group of rank 2"
@test describe(free_group(3)) == "a free group of rank 3"

F = free_group(4)
subs = [sub(F, gens(F)[1:n])[1] for n in 0:4]
@test describe(subs[1]) == "1"
@test describe(subs[2]) == "Z"
@test describe(subs[3]) == "a free group of rank 2"
@test describe(subs[4]) == "a free group of rank 3"
@test describe(subs[5]) == "a free group of rank 4"
end

@testset "describe() for finitely presented groups" begin

F = free_group(1)
@test describe(direct_product(F, F)) == "Z x Z"

F = free_group(2)
G = quo(F, [gen(F,2)])[1]
@test describe(G) == "Z"
G = quo(F, [gen(F,2)/gen(F,1)])[1]
@test describe(G) == "Z"

# TODO: add test for infinitely generated groups
@test describe(derived_subgroup(free_group(2))[1]) == "a non-finitely generated group"

end


@testset "describe() for some real world examples" begin

P = SimplicialComplex([[1,2,4,9],[1,2,4,15],[1,2,6,14],[1,2,6,15],[1,2,9,14],[1,3,4,12],[1,3,4,15],[1,3,7,10],[1,3,7,12],[1,3,10,15],[1,4,9,12],[1,5,6,13],[1,5,6,14],[1,5,8,11],[1,5,8,13],[1,5,11,14],[1,6,13,15],[1,7,8,10],[1,7,8,11],[1,7,11,12],[1,8,10,13],[1,9,11,12],[1,9,11,14],[1,10,13,15],[2,3,5,10],[2,3,5,11],[2,3,7,10],[2,3,7,13],[2,3,11,13],[2,4,9,13],[2,4,11,13],[2,4,11,15],[2,5,8,11],[2,5,8,12],[2,5,10,12],[2,6,10,12],[2,6,10,14],[2,6,12,15],[2,7,9,13],[2,7,9,14],[2,7,10,14],[2,8,11,15],[2,8,12,15],[3,4,5,14],[3,4,5,15],[3,4,12,14],[3,5,10,15],[3,5,11,14],[3,7,12,13],[3,11,13,14],[3,12,13,14],[4,5,6,7],[4,5,6,14],[4,5,7,15],[4,6,7,11],[4,6,10,11],[4,6,10,14],[4,7,11,15],[4,8,9,12],[4,8,9,13],[4,8,10,13],[4,8,10,14],[4,8,12,14],[4,10,11,13],[5,6,7,13],[5,7,9,13],[5,7,9,15],[5,8,9,12],[5,8,9,13],[5,9,10,12],[5,9,10,15],[6,7,11,12],[6,7,12,13],[6,10,11,12],[6,12,13,15],[7,8,10,14],[7,8,11,15],[7,8,14,15],[7,9,14,15],[8,12,14,15],[9,10,11,12],[9,10,11,16],[9,10,15,16],[9,11,14,16],[9,14,15,16],[10,11,13,16],[10,13,15,16],[11,13,14,16],[12,13,14,15],[13,14,15,16]])
pi_1 = fundamental_group(P)
@test describe(pi_1) == "SL(2,5)"

@test describe(fundamental_group(torus())) == "a finitely presented infinite group"

end
1 change: 1 addition & 0 deletions test/Groups/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ using Oscar
using Test

include("abelian_aut.jl")
include("describe.jl")
include("spinor_norms.jl")
include("conformance.jl")
include("constructors.jl")
Expand Down

0 comments on commit 88e0006

Please sign in to comment.