Skip to content

Commit

Permalink
rearrange and document cycle structures (oscar-system#2651)
Browse files Browse the repository at this point in the history
- move code to `src/Groups`
- add docstrings and tests
- add to the manual
- (and fixed a wrong `@req` condition in `cperm`)
  • Loading branch information
ThomasBreuer authored Aug 15, 2023
1 parent 39d2ffd commit e2dfa57
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 53 deletions.
14 changes: 14 additions & 0 deletions docs/src/Groups/permgroup.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ julia> x(6)
## Operations for permutation groups

```@docs
cycle_structures(G::PermGroup)
is_transitive(G::PermGroup, L::AbstractVector{Int} = 1:degree(G))
transitivity(G::PermGroup, L::AbstractVector{Int} = 1:degree(G))
is_primitive(G::PermGroup, L::AbstractVector{Int} = 1:degree(G))
Expand All @@ -91,3 +92,16 @@ maximal_blocks(G::PermGroup, L::AbstractVector{Int} = moved_points(G))
minimal_block_reps(G::PermGroup, L::AbstractVector{Int} = moved_points(G))
all_blocks(G::PermGroup)
```

## Cycle structures

For a permutation, its cycle structure [`cycle_structure`](@ref)
determines the degree, order, number of moved points, sign.

```@docs
degree(::CycleType)
iseven(::CycleType)
isodd(::CycleType)
order(::Type{T}, c::CycleType) where T
sign(::CycleType)
```
6 changes: 6 additions & 0 deletions src/GAP/wrappers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ GAP.@wrap ClassNames(x::GapObj)::GapObj
GAP.@wrap ClassMultiplicationCoefficient(x::GapObj, y::Int, z::Int, t::Int)::GAP.Obj
GAP.@wrap ClassPositionsOfCentre(x::GapObj)::GapObj
GAP.@wrap ClassPositionsOfPCore(x::GapObj, y::GAP.Obj)::GapObj
GAP.@wrap ClassPositionsOfSolvableResiduum(x::GapObj)::GapObj
GAP.@wrap ClassParameters(x::GapObj)::GapObj
GAP.@wrap Coefficients(x::Any, y::Any)::GapObj
GAP.@wrap CoefficientsFamily(x::GapObj)::GapObj
Expand All @@ -44,6 +45,8 @@ GAP.@wrap CoeffsCyc(x::GAP.Obj, y::Int)::GapObj
GAP.@wrap ComputedPowerMaps(x::GapObj)::GapObj
GAP.@wrap Conductor(x::Any)::GapInt
GAP.@wrap ConjugacyClasses(x::GapObj)::GapObj
GAP.@wrap CycleFromList(x::GapObj)::GapObj
GAP.@wrap CycleStructurePerm(x::GapObj)::GapObj
GAP.@wrap CycList(x::GapObj)::GapInt
GAP.@wrap CyclotomicPol(x::Int)::GapObj
GAP.@wrap Decomposition(x::GapObj, y::GapObj, z::GAP.Obj)::GapObj
Expand Down Expand Up @@ -228,6 +231,7 @@ GAP.@wrap OnTuples(x::GapObj, y::GapObj)::GapObj
GAP.@wrap Order(x::Any)::GapInt
GAP.@wrap OrthogonalComponents(x::GapObj, y::GapObj, z::GapInt)::GapObj
GAP.@wrap OrthogonalDiscriminants(x::GapObj)::GapObj
GAP.@wrap PermList(x::GapObj)::GapObj
GAP.@wrap Permuted(x::GapObj, y::GapObj)::GapObj
GAP.@wrap PolynomialByExtRep(x::GapObj, y::GapObj)::GapObj
GAP.@wrap PolynomialRing(x::GapObj)::GapObj
Expand All @@ -243,6 +247,8 @@ GAP.@wrap ScalarProduct(x::GapObj, y::GapObj, z::GapObj)::GAP.Obj
GAP.@wrap SchurIndexByCharacter(x::GapObj, y::GapObj, z::GapObj)::GAP.Obj
GAP.@wrap SetMaximalAbelianQuotient(x::Any, y::Any)::Nothing
GAP.@wrap SetSize(x::Any, y::Any)::Nothing
GAP.@wrap SignPerm(x::GapObj)::Int
GAP.@wrap SignPermGroup(x::GapObj)::Int
GAP.@wrap Size(x::Any)::GapInt
GAP.@wrap SizeOfFieldOfDefinition(x::GapObj, y::GapInt)::GapInt
GAP.@wrap SizesCentralizers(x::GapObj)::GapObj
Expand Down
14 changes: 0 additions & 14 deletions src/Groups/GAPGroups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -442,20 +442,6 @@ end
#
################################################################################

"""
GroupConjClass{T, S}
It can be either the conjugacy class of an element or of a subgroup of type `S`
in a group `G` of type `T`.
It is displayed as
```
cc = x ^ G
```
where `G` is a group and `x` = `representative`(`cc`) is either an element
or a subgroup of `G`.
"""
abstract type GroupConjClass{T, S} end

struct GAPGroupConjClass{T<:GAPGroup, S<:Union{GAPGroupElem,GAPGroup}} <: GroupConjClass{T, S}
X::T
repr::S
Expand Down
155 changes: 131 additions & 24 deletions src/Groups/perm.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Base.:<(x::PermGroupElem, y::PermGroupElem) = x.X < y.X
Base.isless(x::PermGroupElem, y::PermGroupElem) = x<y


"""
@doc raw"""
degree(G::PermGroup) -> Int
Return the degree of `G` as a permutation group, that is,
Expand Down Expand Up @@ -53,15 +53,16 @@ julia> show(Vector(gen(symmetric_group(5), 2)))
"""
degree(x::PermGroup) = x.deg

"""
@doc raw"""
degree(g::PermGroupElem) -> Int
Return the degree of the parent of `g`. This value is always greater or equal number_moved_points
Return the degree of the parent of `g`.
This value is always greater or equal `number_moved_points(g)`
"""
degree(g::PermGroupElem) = degree(parent(g))

"""
@doc raw"""
moved_points(x::PermGroupElem) -> Vector{Int}
moved_points(G::PermGroup) -> Vector{Int}
Expand All @@ -77,12 +78,11 @@ julia> length(moved_points(s))
julia> length(moved_points(gen(s, 1)))
3
```
"""
@gapattribute moved_points(x::Union{PermGroupElem,PermGroup}) = Vector{Int}(GAP.Globals.MovedPoints(x.X))

"""
@doc raw"""
number_moved_points(x::PermGroupElem) -> Int
number_moved_points(G::PermGroup) -> Int
Expand All @@ -98,7 +98,6 @@ julia> number_moved_points(s)
julia> number_moved_points(gen(s, 1))
3
```
"""
@gapattribute number_moved_points(x::Union{PermGroupElem,PermGroup}) = GAP.Globals.NrMovedPoints(x.X)::Int
Expand All @@ -124,7 +123,7 @@ Sym( [ 1 .. 6 ] )
```
"""
function perm(L::AbstractVector{<:IntegerUnion})
return PermGroupElem(symmetric_group(length(L)), GAP.Globals.PermList(GAP.GapObj(L;recursive=true)))
return PermGroupElem(symmetric_group(length(L)), GAPWrap.PermList(GAP.GapObj(L;recursive=true)))
end


Expand Down Expand Up @@ -162,7 +161,7 @@ true
```
"""
function perm(g::PermGroup, L::AbstractVector{<:IntegerUnion})
x = GAP.Globals.PermList(GAP.GapObj(L;recursive=true))
x = GAPWrap.PermList(GAP.GapObj(L;recursive=true))
@req x !== GAP.Globals.fail "the list does not describe a permutation"
@req (length(L) <= degree(g) && x in g.X) "the element does not embed in the group"
return PermGroupElem(g, x)
Expand All @@ -171,7 +170,7 @@ end
perm(g::PermGroup, L::AbstractVector{<:ZZRingElem}) = perm(g, [Int(y) for y in L])

function (g::PermGroup)(L::AbstractVector{<:IntegerUnion})
x = GAP.Globals.PermList(GAP.GapObj(L;recursive=true))
x = GAPWrap.PermList(GAP.GapObj(L;recursive=true))
@req (length(L) <= degree(g) && x in g.X) "the element does not embed in the group"
return PermGroupElem(g, x)
end
Expand Down Expand Up @@ -286,7 +285,7 @@ function cperm(L::AbstractVector{T}...) where T <: IntegerUnion
if length(L)==0
return one(symmetric_group(1))
else
return prod([PermGroupElem(symmetric_group(maximum(y)), GAP.Globals.CycleFromList(GAP.Obj([Int(k) for k in y]))) for y in L])
return prod([PermGroupElem(symmetric_group(maximum(y)), GAPWrap.CycleFromList(GAP.Obj([Int(k) for k in y]))) for y in L])
#TODO: better create the product of GAP permutations?
end
end
Expand All @@ -298,8 +297,8 @@ function cperm(g::PermGroup,L::AbstractVector{T}...) where T <: IntegerUnion
if length(L)==0
return one(g)
else
x=prod(y -> GAP.Globals.CycleFromList(GAP.Obj([Int(k) for k in y])), L)
@req (length(L) <= degree(g) && x in g.X) "the element does not embed in the group"
x=prod(y -> GAPWrap.CycleFromList(GAP.Obj([Int(k) for k in y])), L)
@req x in g.X "the element does not embed in the group"
return PermGroupElem(g, x)
end
end
Expand All @@ -312,7 +311,7 @@ function cperm(g::PermGroup,L::Vector{Vector{T}}) where T <: IntegerUnion
return cperm(g,L...)
end

"""
@doc raw"""
Vector{T}(x::PermGroupElem, n::Int = x.parent.deg) where T <: IntegerUnion
Vector(x::PermGroupElem, n::Int = x.parent.deg)
Expand Down Expand Up @@ -341,7 +340,6 @@ julia> Vector{ZZRingElem}(pi, 2)
2-element Vector{ZZRingElem}:
2
3
```
"""
Base.Vector{T}(x::PermGroupElem, n::Int = x.parent.deg) where T <: IntegerUnion = T[x(i) for i in 1:n]
Expand All @@ -355,7 +353,7 @@ Base.Vector(x::PermGroupElem, n::Int = x.parent.deg) = Vector{Int}(x,n)
^(n::Int, x::PermGroupElem) = (n^x.X)::Int


"""
@doc raw"""
sign(g::PermGroupElem) -> Int
Return the sign of the permutation `g`.
Expand All @@ -372,13 +370,13 @@ julia> sign(cperm(1:3))
1
```
"""
Base.sign(g::PermGroupElem) = GAP.Globals.SignPerm(g.X)::Int
Base.sign(g::PermGroupElem) = GAPWrap.SignPerm(g.X)

# TODO: document the following?
Base.sign(G::PermGroup) = GAP.Globals.SignPermGroup(G.X)
Base.sign(G::PermGroup) = GAPWrap.SignPermGroup(G.X)


"""
@doc raw"""
isodd(g::PermGroupElem)
Return `true` if the permutation `g` is odd, `false` otherwise.
Expand All @@ -400,7 +398,7 @@ false
"""
Base.isodd(g::PermGroupElem) = sign(g) == -1

"""
@doc raw"""
iseven(g::PermGroupElem)
Return `true` if the permutation `g` is even, `false` otherwise.
Expand Down Expand Up @@ -428,7 +426,7 @@ Base.isodd(G::PermGroup) = sign(G) == -1
Base.iseven(n::PermGroup) = !isodd(n)

##
# cycle-types and support
# cycle types and support
##
struct CycleType <: AbstractVector{Pair{Int64, Int64}}
# pairs 'cycle length => number of times it occurs'
Expand All @@ -447,6 +445,7 @@ struct CycleType <: AbstractVector{Pair{Int64, Int64}}
function CycleType(v::Vector{Pair{Int, Int}}; sorted::Bool = false)
sorted && return new(v)
return new(sort(v, by = x -> x[1]))
#TODO: check that each cycle length is specified at most once?
end
end

Expand Down Expand Up @@ -486,13 +485,96 @@ function ^(c::CycleType, e::Int)
return CycleType(t; sorted=true)
end


@doc raw"""
order(::Type{T} = ZZRingElem, c::CycleType) where T <: IntegerUnion
Return the order of the permutations with cycle structure `c`.
# Examples
```jldoctest
julia> g = symmetric_group(3);
julia> all(x -> order(cycle_structure(x)) == order(x), gens(g))
true
```
"""
order(::Type{T}, c::CycleType) where T = mapreduce(x->T(x[1]), lcm, c.s, init = T(1))
order(c::CycleType) = order(ZZRingElem, c)


@doc raw"""
degree(c::CycleType) -> Int
Return the degree of the permutations with cycle structure `c`.
# Examples
```jldoctest
julia> g = symmetric_group(3);
julia> all(x -> degree(cycle_structure(x)) == degree(g), gens(g))
true
```
"""
degree(c::CycleType) = mapreduce(x->x[1]*x[2], +, c.s, init = 0)


@doc raw"""
sign(c::CycleType) -> Int
Return the sign of the permutations with cycle structure `c`.
# Examples
```jldoctest
julia> g = symmetric_group(3);
julia> all(x -> sign(cycle_structure(x)) == sign(x), gens(g))
true
```
"""
function Base.sign(c::CycleType)
res = 1
for (a, b) in c.s
if iseven(a) && isodd(b)
res = - res
end
end
return res
end

@doc raw"""
isodd(c::CycleType) -> Bool
Return whether the permutations with cycle structure `c` are odd.
# Examples
```jldoctest
julia> g = symmetric_group(3);
julia> all(x -> isodd(cycle_structure(x)) == isodd(x), gens(g))
true
```
"""
Base.isodd(c::CycleType) = sign(c) == -1


@doc raw"""
iseven(c::CycleType) -> Bool
Return whether the permutations with cycle structure `c` are even.
# Examples
```jldoctest
julia> g = symmetric_group(3);
julia> all(x -> iseven(cycle_structure(x)) == iseven(x), gens(g))
true
```
"""
Base.iseven(c::CycleType) = !isodd(c)


@doc raw"""
cycle_structure(g::PermGroupElem) -> CycleType
Return the cycle structure of the permutation `g` as a cycle type.
Expand All @@ -510,16 +592,16 @@ julia> cycle_structure(g)
3 => 2
5 => 1
julia> cperm()
julia> g = cperm()
()
julia> cycle_structure(ans)
julia> cycle_structure(g)
1-element Oscar.CycleType:
1 => 1
```
"""
function cycle_structure(g::PermGroupElem)
c = GAP.Globals.CycleStructurePerm(GAP.GapObj(g))::GAP.GapObj
c = GAPWrap.CycleStructurePerm(g.X)
# TODO: use SortedDict from DataStructures.jl ?
ct = Pair{Int, Int}[ i+1 => c[i] for i in 1:length(c) if GAP.Globals.ISB_LIST(c, i) ]
s = degree(CycleType(ct, sorted = true))
Expand All @@ -530,8 +612,33 @@ function cycle_structure(g::PermGroupElem)
return CycleType(ct, sorted = true)
end

function cycle_structure(x::GroupConjClass{PermGroup, PermGroupElem})
return cycle_structure(representative(x))
end


@doc raw"""
cycle_structures(G::PermGroup) -> Set{CycleType}
Return the set of cycle structures of elements in `G`,
see [`cycle_structure`](@ref).
# Examples
```jldoctest
julia> g = symmetric_group(3);
julia> sort!(collect(cycle_structures(g)))
3-element Vector{Oscar.CycleType}:
[1 => 1, 2 => 1]
[1 => 3]
[3 => 1]
```
"""
function cycle_structures(G::PermGroup)
r = conjugacy_classes(G)
return Set(cycle_structure(x) for x in r)
end


################################################################################
#
Expand Down
Loading

0 comments on commit e2dfa57

Please sign in to comment.