Skip to content

Commit

Permalink
add G-set functionality for conjugacy classes (#3268)
Browse files Browse the repository at this point in the history
* add G-set functionality for conjugacy classes

and changed the `show` methods for conjugacy classes

* address comments (introduce `action_range`)

* `GroupConjClass` is a subtype of `GSet`
  • Loading branch information
ThomasBreuer committed Jan 31, 2024
1 parent b2455d0 commit 34c5a22
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 61 deletions.
124 changes: 88 additions & 36 deletions src/Groups/GAPGroups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -530,29 +530,78 @@ end
#
################################################################################

struct GAPGroupConjClass{T<:GAPGroup, S<:Union{GAPGroupElem,GAPGroup}} <: GroupConjClass{T, S}
@attributes mutable struct GAPGroupConjClass{T<:GAPGroup, S<:Union{GAPGroupElem,GAPGroup}} <: GroupConjClass{T, S}
X::T
repr::S
CC::GapObj

function GAPGroupConjClass(G::T, obj::S, C::GapObj) where T<:GAPGroup where S<:Union{GAPGroupElem, GAPGroup}
return new{T, S}(G, obj, C, Dict{Symbol,Any}())
end
end

Base.eltype(::Type{GAPGroupConjClass{T,S}}) where {T,S} = S

Base.hash(x::GAPGroupConjClass, h::UInt) = h # FIXME

function Base.show(io::IO, x::GAPGroupConjClass)
print(io, String(GAPWrap.StringViewObj(x.repr.X)),
" ^ ",
String(GAPWrap.StringViewObj(x.X.X)))
function Base.show(io::IO, ::MIME"text/plain", x::GAPGroupConjClass)
println(io, "Conjugacy class of")
io = pretty(io)
print(io, Indent())
println(io, Lowercase(), x.repr, " in")
print(io, Lowercase(), x.X)
print(io, Dedent())
end

function Base.show(io::IO, x::GAPGroupConjClass{T, S}) where T where S
if get(io, :supercompact, false)
if S <: GAPGroupElem
print(io, "Conjugacy class of group elements")
else
print(io, "Conjugacy class of subgroups")
end
else
print(io, "Conjugacy class of ")
io = pretty(io)
print(IOContext(io, :supercompact => true), Lowercase(), x.repr, " in ", Lowercase(), x.X)
end
end

action_function(C::GAPGroupConjClass) = ^

==(a::GAPGroupConjClass{T, S}, b::GAPGroupConjClass{T, S}) where S where T = a.CC == b.CC

function Base.length(::Type{T}, C::GAPGroupConjClass) where T <: IntegerUnion
return T(GAPWrap.Size(C.CC))
end

Base.length(C::GroupConjClass) = length(ZZRingElem, C)
Base.lastindex(C::GroupConjClass) = length(C)

Base.keys(C::GroupConjClass) = keys(1:length(C))

is_transitive(C::GroupConjClass) = true

orbit(G::GAPGroup, g::T) where T<: Union{GAPGroupElem, GAPGroup} = conjugacy_class(G, g)

orbits(C::GAPGroupConjClass) = [C]

function permutation(C::GAPGroupConjClass, g::GAPGroupElem)
pi = GAP.Globals.Permutation(g.X, C.CC, GAP.Globals.OnPoints)::GapObj
return group_element(action_range(C), pi)
end

@attr GAPGroupHomomorphism{T, PermGroup} function action_homomorphism(C::GAPGroupConjClass{T}) where T
G = C.X
acthom = GAP.Globals.ActionHomomorphism(G.X, C.CC, GAP.Globals.OnPoints)::GapObj

# See the comment about `SetJuliaData` in the `action_homomorphism` method
# for `GSetByElements`.
GAP.Globals.SetJuliaData(acthom, GAP.Obj([C, G]))

return GAPGroupHomomorphism(G, action_range(C), acthom)
end


"""
representative(C::GroupConjClass)
Expand All @@ -564,11 +613,12 @@ Return a representative of the conjugacy class `C`.
julia> G = symmetric_group(4);
julia> C = conjugacy_class(G, G([2, 1, 3, 4]))
(1,2) ^ Sym( [ 1 .. 4 ] )
Conjugacy class of
(1,2) in
Sym(4)
julia> representative(C)
(1,2)
```
"""
representative(C::GroupConjClass) = C.repr
Expand All @@ -583,11 +633,12 @@ Return the acting group of `C`.
julia> G = symmetric_group(4);
julia> C = conjugacy_class(G, G([2, 1, 3, 4]))
(1,2) ^ Sym( [ 1 .. 4 ] )
Conjugacy class of
(1,2) in
Sym(4)
julia> acting_group(C) === G
true
```
"""
acting_group(C::GroupConjClass) = C.X
Expand All @@ -604,8 +655,9 @@ Return the conjugacy class `cc` of `g` in `G`, where `g` = `representative`(`cc`
julia> G = symmetric_group(4);
julia> C = conjugacy_class(G, G([2, 1, 3, 4]))
(1,2) ^ Sym( [ 1 .. 4 ] )
Conjugacy class of
(1,2) in
Sym(4)
```
"""
function conjugacy_class(G::GAPGroup, g::GAPGroupElem)
Expand All @@ -620,6 +672,25 @@ function Base.rand(rng::Random.AbstractRNG, C::GAPGroupConjClass{S,T}) where S w
return group_element(C.X, GAP.Globals.Random(GAP.wrap_rng(rng), C.CC)::GapObj)
end

Base.in(g::GAPGroupElem, C::GAPGroupConjClass) = GapObj(g) in C.CC
Base.in(G::GAPGroup, C::GAPGroupConjClass) = GapObj(G) in C.CC

Base.IteratorSize(::Type{<:GAPGroupConjClass}) = Base.SizeUnknown()

Base.iterate(cc::GAPGroupConjClass) = iterate(cc, GAPWrap.Iterator(cc.CC))

function Base.iterate(cc::GAPGroupConjClass{S,T}, state::GapObj) where {S,T}
if GAPWrap.IsDoneIterator(state)
return nothing
end
i = GAPWrap.NextIterator(state)::GapObj
if T <: GAPGroupElem
return group_element(cc.X, i), state
else
return _as_subgroup(cc.X, i)[1], state
end
end


"""
number_of_conjugacy_classes(G::GAPGroup)
Expand Down Expand Up @@ -704,11 +775,10 @@ Sym(3)
julia> conjugacy_classes_subgroups(G)
4-element Vector{GAPGroupConjClass{PermGroup, PermGroup}}:
Group(()) ^ Sym( [ 1 .. 3 ] )
Group([ (2,3) ]) ^ Sym( [ 1 .. 3 ] )
Group([ (1,2,3) ]) ^ Sym( [ 1 .. 3 ] )
Group([ (1,2,3), (2,3) ]) ^ Sym( [ 1 .. 3 ] )
Conjugacy class of permutation group in Sym(3)
Conjugacy class of permutation group in Sym(3)
Conjugacy class of permutation group in Sym(3)
Conjugacy class of permutation group in Sym(3)
```
"""
function conjugacy_classes_subgroups(G::GAPGroup)
Expand Down Expand Up @@ -759,9 +829,8 @@ julia> G = symmetric_group(3);
julia> conjugacy_classes_maximal_subgroups(G)
2-element Vector{GAPGroupConjClass{PermGroup, PermGroup}}:
Group([ (1,2,3) ]) ^ Sym( [ 1 .. 3 ] )
Group([ (2,3) ]) ^ Sym( [ 1 .. 3 ] )
Conjugacy class of permutation group in Sym(3)
Conjugacy class of permutation group in Sym(3)
```
"""
function conjugacy_classes_maximal_subgroups(G::GAPGroup)
Expand Down Expand Up @@ -1044,23 +1113,6 @@ end
# END subgroups conjugation


# START iterator
Base.IteratorSize(::Type{<:GAPGroupConjClass}) = Base.SizeUnknown()

Base.iterate(cc::GAPGroupConjClass) = iterate(cc, GAPWrap.Iterator(cc.CC))

function Base.iterate(cc::GAPGroupConjClass{S,T}, state::GapObj) where {S,T}
if GAPWrap.IsDoneIterator(state)
return nothing
end
i = GAPWrap.NextIterator(state)::GapObj
if T <: GAPGroupElem
return group_element(cc.X, i), state
else
return _as_subgroup(cc.X, i)[1], state
end
end

################################################################################
#
# Normal Structure
Expand Down
35 changes: 17 additions & 18 deletions src/Groups/gsets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import Hecke.orbit
# - conjugacy classes of group elements
# - conjugacy classes of subgroups
# - block system
abstract type GSet{T} end


# TODO: add lots of concrete subtypes constructors, e.g. for
Expand All @@ -42,7 +41,7 @@ abstract type GSet{T} end
function GSetByElements(G::T, fun::Function, seeds; closed::Bool = false) where T<:GAPGroup
@assert ! isempty(seeds)
Omega = new{T}(G, fun, seeds, Dict{Symbol,Any}())
closed && set_attribute!(Omega, :elements => collect(seeds))
closed && set_attribute!(Omega, :elements => unique!(collect(seeds)))
return Omega
end
end
Expand Down Expand Up @@ -71,6 +70,15 @@ end
acting_group(Omega::GSetByElements) = Omega.group
action_function(Omega::GSetByElements) = Omega.action_function

# The following works for all G-set types that support attributes
# and for which the number of elements is an `Int`.
function action_range(Omega::GSet)
return get_attribute!(Omega, :action_range) do
return symmetric_group(length(Int, Omega))
end
end


#############################################################################
##
## general method with explicit action function
Expand Down Expand Up @@ -443,10 +451,7 @@ function permutation(Omega::GSetByElements{T}, g::GAPGroupElem) where T<:GAPGrou
# whether the given group element 'g' is a group element.
pi = GAP.Globals.PermutationOp(g, omega_list, gfun)

sym = get_attribute!(Omega, :action_range) do
return symmetric_group(length(Omega))
end
return group_element(sym, pi)
return group_element(action_range(Omega), pi)
end


Expand Down Expand Up @@ -502,6 +507,8 @@ function Base.in(omega::GroupCoset, Omega::GSetByRightTransversal)
end

Base.length(Omega::GSetByRightTransversal) = index(Int, Omega.group, Omega.subgroup)
Base.length(::Type{T}, Omega::GSetByRightTransversal) where T <: IntegerUnion = index(T, Omega.group, Omega.subgroup)

Base.lastindex(Omega::GSetByRightTransversal) = length(Omega)

Base.keys(Omega::GSetByRightTransversal) = keys(1:length(Omega))
Expand Down Expand Up @@ -540,10 +547,7 @@ end
function permutation(Omega::GSetByRightTransversal{T, S, E}, g::E) where T <: GAPGroup where S <: GAPGroup where E
# The following works because GAP uses its `PositionCanonical`.
pi = GAP.Globals.PermutationOp(g.X, Omega.transversal.X, GAP.Globals.OnRight)::GapObj
sym = get_attribute!(Omega, :action_range) do
return symmetric_group(length(Omega))
end
return group_element(sym, pi)
return group_element(action_range(Omega), pi)
end

@attr GAPGroupHomomorphism{T, PermGroup} function action_homomorphism(Omega::GSetByRightTransversal{T, S, E}) where T <: GAPGroup where S <: GAPGroup where E
Expand All @@ -556,10 +560,7 @@ end
# for `GSetByElements`.
GAP.Globals.SetJuliaData(acthom, GAP.Obj([Omega, G]))

sym = get_attribute!(Omega, :action_range) do
return symmetric_group(length(Omega))
end
return GAPGroupHomomorphism(G, sym, acthom)
return GAPGroupHomomorphism(G, action_range(Omega), acthom)
end


Expand Down Expand Up @@ -629,10 +630,7 @@ true
# which uses `permutation` or something better for mapping elements.)
GAP.Globals.SetJuliaData(acthom, GAP.Obj([Omega, G]))

sym = get_attribute!(Omega, :action_range) do
return symmetric_group(length(Omega))
end
return GAPGroupHomomorphism(G, sym, acthom)
return GAPGroupHomomorphism(G, action_range(Omega), acthom)
end

# for convenience: create the G-set on the fly
Expand Down Expand Up @@ -718,6 +716,7 @@ end
acting_domain(Omega::GSet) = acting_group(Omega)

Base.length(Omega::GSetByElements) = length(elements(Omega))
Base.length(::Type{T}, Omega::GSetByElements) where T <: IntegerUnion = T(length(elements(Omega)))

representative(Omega::GSetByElements) = first(Omega.seeds)

Expand Down
13 changes: 6 additions & 7 deletions src/Groups/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,11 @@ end
compatible_group(G::T, S::T) where T <: GAPGroup = _oscar_group(G.X, S)


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

abstract type GSet{T} end


################################################################################
#
# Conjugacy Classes
Expand All @@ -320,14 +325,8 @@ compatible_group(G::T, S::T) where T <: GAPGroup = _oscar_group(G.X, 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
abstract type GroupConjClass{T, S} <: GSet{T} end


################################################################################
Expand Down
14 changes: 14 additions & 0 deletions test/Groups/conjugation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,20 @@

end

@testset "Conjugacy classes as G-sets" begin
G = symmetric_group(4)
x = G(cperm([3, 4]))
y = G(cperm([1, 4, 2]))
C = conjugacy_class(G, x)
@test x in C
@test orbits(C) == [C]
@test C == orbit(G, x)
mp = action_homomorphism(C)
@test permutation(C, y) == mp(y)
@test length(C) == 6
@test order(image(mp)[1]) == 24
end

function TestConjCentr(G,x)
Cx = centralizer(G,x)[1]
cc = conjugacy_class(G,x)
Expand Down

0 comments on commit 34c5a22

Please sign in to comment.