Skip to content

Commit

Permalink
Groups: add minimal_generating_set
Browse files Browse the repository at this point in the history
Also ensure `RankOfFreeGroup` is set for free groups at creation time.

Also add disabled `rank` and `is_free` methods for groups, to be enabled
(or removed) in future updates
  • Loading branch information
fingolfin committed Sep 26, 2023
1 parent add39f2 commit fbc0e3f
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 10 deletions.
90 changes: 86 additions & 4 deletions src/Groups/GAPGroups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,34 @@ julia> length(small_generating_set(abelian_group(PermGroup, [2,3,4])))
return res
end

"""
minimal_generating_set(G::GAPGroup)
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])))
2
julia> length(minimal_generating_set(abelian_group(PermGroup, [2,3,4])))
2
julia> minimal_generating_set(symmetric_group(5))
2-element Vector{PermGroupElem}:
(1,2,3,4,5)
(1,2)
```
"""
@gapattribute function minimal_generating_set(G::GAPGroup)
L = GAP.Globals.MinimalGeneratingSet(G.X)::GapObj
res = Vector{elem_type(G)}(undef, length(L))
for i = 1:length(res)
res[i] = group_element(G, L[i]::GapObj)
end
return res
end


################################################################################
#
Expand Down Expand Up @@ -1424,9 +1452,36 @@ function set_prime_of_pgroup(G::GAPGroup, p::IntegerUnion)
set__prime_of_pgroup(G, GAP.Obj(p))
end


"""
is_finitelygenerated(G)
# TODO/FIXME: the rank method below is disabled because it conflicts
# with semantics of the `rank` method for GrpAbGen. We'll have
# to resolve this first; afterwards we can uncomment this code,
# and possibly rename it to whatever we agreed on (if it is different from `rank`)
#"""
# rank(G::GAPGroup)
#
#Return the rank of the group `G`, i.e., the minimal size of a generating set.
#
## Examples
#```jldoctest
#julia> rank(symmetric_group(5))
#2
#
#julia> rank(free_group(5))
#5
#```
#`"""
#function rank(G::GAPGroup)
# is_trivial(G) && return 0
# is_cyclic(G) && return 1
# if is_free(G) || (has_is_finite(G) && is_finite(G) && is_pgroup(G))
# return GAP.Globals.Rank(G.X)::Int
# end
# has_is_finite(G) && is_finite(G) && return length(minimal_generating_set(G))
# error("not yet supported")
#end

"""
is_finitelygenerated(G::GAPGroup)
Return whether `G` is a finitely generated group.
Expand All @@ -1448,6 +1503,33 @@ false
@gapattribute is_finitelygenerated(G::GAPGroup) = GAP.Globals.IsFinitelyGeneratedGroup(G.X)::Bool


# TODO/FIXME: is_free is disabled for now as it is not universal; it only
# really works for fp groups, and then also only for those without relators;
# it returns `false` for a the quotient of the free group on x,y by y, which
# is mathematically a free group, but maybe not in a pure "technical" sense
#@doc raw"""
# is_free(G::GAPGroup)
#
#Return whether `G` is a free group.
#
## Examples
#```jldoctest
#julia> F = free_group(2)
#<free group on the generators [ f1, f2 ]>
#
#julia> is_free(F)
#true
#
#julia> H = derived_subgroup(F)[1]
#Group(<free, no generators known>)
#
#julia> is_free(H)
#true
#```
#"""
#@gapattribute is_free(G::GAPGroup) = GAP.Globals.IsFreeGroup(G.X)::Bool


@doc raw"""
is_full_fp_group(G::FPGroup)
Expand Down Expand Up @@ -1761,7 +1843,7 @@ function (G::FPGroup)(pairs::AbstractVector{Pair{T, S}}) where {T <: IntegerUnio
end
end
famG = GAP.Globals.ElementsFamily(GAP.Globals.FamilyObj(G.X))
if GAP.Globals.IsFreeGroup(G.X)
if GAPWrap.IsFreeGroup(G.X)
w = GAPWrap.ObjByExtRep(famG, GapObj(ll))::GapObj
else
# For quotients of free groups, `GAPWrap.ObjByExtRep` is not defined.
Expand Down
14 changes: 10 additions & 4 deletions src/Groups/group_constructors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -475,20 +475,26 @@ where the `i`-th generator is printed as `L[i]`.
function free_group(n::Int, s::VarName = :f; eltype::Symbol = :letter)
@req n >= 0 "n must be a non-negative integer"
if eltype == :syllable
return FPGroup(GAP.Globals.FreeGroup(n, GAP.GapObj(s); FreeGroupFamilyType = GapObj("syllable"))::GapObj)
G = FPGroup(GAP.Globals.FreeGroup(n, GAP.GapObj(s); FreeGroupFamilyType = GapObj("syllable"))::GapObj)
else
return FPGroup(GAP.Globals.FreeGroup(n, GAP.GapObj(s))::GapObj)
G = FPGroup(GAP.Globals.FreeGroup(n, GAP.GapObj(s))::GapObj)
end
GAP.Globals.SetRankOfFreeGroup(G.X, n)
return G
end

function free_group(L::Vector{<:VarName})
J = GAP.GapObj(L, recursive = true)
return FPGroup(GAP.Globals.FreeGroup(J)::GapObj)
G = FPGroup(GAP.Globals.FreeGroup(J)::GapObj)
GAP.Globals.SetRankOfFreeGroup(G.X, length(J))
return G
end

function free_group(L::VarName...)
J = GAP.GapObj(L, recursive = true)
return FPGroup(GAP.Globals.FreeGroup(J)::GapObj)
G = FPGroup(GAP.Globals.FreeGroup(J)::GapObj)
GAP.Globals.SetRankOfFreeGroup(G.X, length(J))
return G
end

# FIXME: a function `free_abelian_group` with the same signature is
Expand Down
3 changes: 1 addition & 2 deletions src/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@ export minimal_betti_table
export minimal_block_reps
export minimal_denominators
export minimal_faces
export minimal_generating_set
export minimal_generating_set, has_minimal_generating_set, set_minimal_generating_set
export minimal_generators
export minimal_nonfaces
export minimal_normal_subgroups, has_minimal_normal_subgroups, set_minimal_normal_subgroups
Expand Down Expand Up @@ -1307,7 +1307,6 @@ export singular_locus
export singular_locus_reduced
export singular_poly_ring
export slpoly_ring
export small_generating_set
export small_generating_set, has_small_generating_set, set_small_generating_set
export small_group
export small_group_identification, has_small_group_identification
Expand Down

0 comments on commit fbc0e3f

Please sign in to comment.