Skip to content

Commit

Permalink
Add smaller_degree_permutation_representation, lazy double cosets (o…
Browse files Browse the repository at this point in the history
…scar-system#3899)

Co-authored-by: ThomasBreuer <sam@math.rwth-aachen.de>
Co-authored-by: Max Horn <max@quendi.de>
  • Loading branch information
3 people committed Jul 28, 2024
1 parent 19b4b2d commit bfcfee3
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 54 deletions.
1 change: 1 addition & 0 deletions docs/src/Groups/permgroup.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ In OSCAR, every permutation group has a degree `n`, that corresponds to the size

```@docs
degree(x::PermGroup)
smaller_degree_permutation_representation(G::PermGroup)
```


Expand Down
8 changes: 7 additions & 1 deletion src/GAP/wrappers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ GAP.@wrap DescriptionOfRootOfUnity(x::Any)::GapObj
GAP.@wrap DeterminantOfCharacter(x::GapObj)::GapObj
GAP.@wrap Dimension(x::GapObj)::Int
GAP.@wrap DimensionOfHighestWeightModule(x::GapObj, y::GapObj)::GapInt
GAP.@wrap DoubleCoset(x::GapObj, y::GapObj, z::GapObj)::GapObj
GAP.@wrap DoubleCosetRepsAndSizes(x::GapObj, y::GapObj, z::GapObj)::GapObj
GAP.@wrap DominantCharacter(x::GapObj, y::GapObj)::GapObj
GAP.@wrap E(x::Any)::GapInt
GAP.@wrap EigenvaluesChar(x::GapObj, y::GAP.Obj)::GapObj
Expand Down Expand Up @@ -138,9 +140,10 @@ GAP.@wrap Indicator(x::GapObj, y::GapObj, z::GapInt)::GapObj
GAP.@wrap InducedClassFunction(x::GapObj, y::GapObj)::GapObj
GAP.@wrap InducedClassFunctionsByFusionMap(x::GapObj, y::GapObj, z::GapObj, t::GapObj)::GapObj
GAP.@wrap InducedCyclic(x::GapObj)::GapObj
GAP.@wrap InitFusion(x::GapObj, y::GapObj)::GapObj
GAP.@wrap InducedCyclic(x::GapObj, y::GapObj)::GapObj
GAP.@wrap InitFusion(x::GapObj, y::GapObj)::GapObj
GAP.@wrap INT_FFE_DEFAULT(x::Any)::GapInt
GAP.@wrap Intersection(x::GapObj)::GapObj
GAP.@wrap IntFFE(x::Any)::GapInt
GAP.@wrap Inverse(x::GapObj)::GapObj
GAP.@wrap Irr(x::GapObj)::GapObj
Expand Down Expand Up @@ -312,6 +315,7 @@ GAP.@wrap PrimePGroup(x::GapObj)::GapInt
GAP.@wrap PrimitiveElement(x::GapObj)::GapObj
GAP.@wrap Projection(x::GapObj)::GapObj
GAP.@wrap Projection(x::GapObj, i::Int)::GapObj
GAP.@wrap Random(x::GapObj, y::GapObj)::GAP.Obj
GAP.@wrap Range(x::GapObj)::GapObj
GAP.@wrap RecognizeGroup(x::GapObj)::GapObj
GAP.@wrap ReduceCoeffs(x::GapObj, y::GapObj)
Expand All @@ -320,6 +324,8 @@ GAP.@wrap Representative(x::GapObj)::GAP.Obj
GAP.@wrap RepresentativeTom(x::GapObj, y::Int)::GapObj
GAP.@wrap RepresentativeAction(x::GapObj, y::GapObj, z::GapObj)::GapObj
GAP.@wrap RestrictedMapping(x::GapObj, y::GapObj)::GapObj
GAP.@wrap RightCoset(x::GapObj, y::GapObj)::GapObj
GAP.@wrap RightTransversal(x::GapObj, y::GapObj)::GapObj
GAP.@wrap RootSystem(x::GapObj)::GapObj
GAP.@wrap SLPforElement(x::GapObj, y::GapObj)::GAP.Obj
GAP.@wrap ScalarProduct(x::GapObj, y::GapObj, z::GapObj)::GAP.Obj
Expand Down
120 changes: 73 additions & 47 deletions src/Groups/cosets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ function _group_coset(G::GAPGroup, H::GAPGroup, repr::GAPGroupElem, side::Symbol
end

function ==(x::GroupCoset, y::GroupCoset)
return x.X == y.X && x.side == y.side
return GapObj(x) == GapObj(y) && x.side == y.side
end

function Base.show(io::IO, ::MIME"text/plain", x::GroupCoset)
side = x.side === :left ? "Left" : "Right"
io = pretty(io)
println(io, "$side coset of ", Lowercase(), x.H)
print(io, Indent())
println(io, "with representative ", x.repr)
println(io, "with representative ", representative(x))
print(io, "in ", Lowercase(), x.G)
print(io, Dedent())
end
Expand All @@ -44,7 +44,7 @@ function Base.show(io::IO, x::GroupCoset)
else
print(io, "$side coset of ")
io = pretty(io)
print(terse(io), Lowercase(), x.H, " with representative ", x.repr)
print(terse(io), Lowercase(), x.H, " with representative ", representative(x))
end
end

Expand Down Expand Up @@ -73,8 +73,8 @@ Right coset of Sym(3)
```
"""
function right_coset(H::GAPGroup, g::GAPGroupElem)
@req GAPWrap.IsSubset(parent(g).X, H.X) "H is not a subgroup of parent(g)"
return _group_coset(parent(g), H, g, :right, GAP.Globals.RightCoset(H.X,g.X))
@req GAPWrap.IsSubset(GapObj(parent(g)), GapObj(H)) "H is not a subgroup of parent(g)"
return _group_coset(parent(g), H, g, :right, GAPWrap.RightCoset(GapObj(H), GapObj(g)))
end

"""
Expand All @@ -101,8 +101,8 @@ Left coset of Sym(3)
```
"""
function left_coset(H::GAPGroup, g::GAPGroupElem)
@req GAPWrap.IsSubset(parent(g).X, H.X) "H is not a subgroup of parent(g)"
return _group_coset(parent(g), H, g, :left, GAP.Globals.RightCoset(GAP.Globals.ConjugateSubgroup(H.X,GAP.Globals.Inverse(g.X)),g.X))
@req GAPWrap.IsSubset(GapObj(parent(g)), GapObj(H)) "H is not a subgroup of parent(g)"
return _group_coset(parent(g), H, g, :left, GAPWrap.RightCoset(GAPWrap.ConjugateSubgroup(GapObj(H), GAPWrap.Inverse(GapObj(g))), GapObj(g)))
end


Expand All @@ -126,24 +126,24 @@ Base.:*(g::GAPGroupElem, H::GAPGroup) = left_coset(H,g)
function Base.:*(c::GroupCoset, y::GAPGroupElem)
@assert y in c.G "element not in the group"
if c.side == :right
return right_coset(c.H, c.repr*y)
return right_coset(c.H, representative(c)*y)
else
return left_coset(c.H^y, c.repr*y)
return left_coset(c.H^y, representative(c)*y)
end
end

function Base.:*(y::GAPGroupElem, c::GroupCoset)
@assert y in c.G "element not in the group"
if c.side == :left
return left_coset(c.H, y*c.repr)
return left_coset(c.H, y*representative(c))
else
return right_coset(c.H^(y^-1), y*c.repr)
return right_coset(c.H^(y^-1), y*representative(c))
end
end

function Base.:*(c::GroupCoset, d::GroupCoset)
@req (c.side == :right && d.side == :left) "Wrong input"
return double_coset(c.H, c.repr*d.repr, d.H)
return double_coset(c.H, representative(c)*representative(d), d.H)
end

"""
Expand Down Expand Up @@ -238,7 +238,7 @@ julia> is_bicoset(fH)
true
```
"""
is_bicoset(C::GroupCoset) = GAPWrap.IsBiCoset(C.X)
is_bicoset(C::GroupCoset) = GAPWrap.IsBiCoset(GapObj(C))

"""
right_cosets(G::GAPGroup, H::GAPGroup; check::Bool=true)
Expand Down Expand Up @@ -317,6 +317,8 @@ struct SubgroupTransversal{T<: GAPGroup, S<: GAPGroup, E<: GAPGroupElem} <: Abst
X::GapObj # underlying *right* transversal in GAP
end

GAP.julia_to_gap(T::SubgroupTransversal) = T.X

function Base.show(io::IO, ::MIME"text/plain", x::SubgroupTransversal)
side = x.side === :left ? "Left" : "Right"
println(io, "$side transversal of length $(length(x)) of")
Expand All @@ -343,7 +345,7 @@ Base.hash(x::SubgroupTransversal, h::UInt) = h # FIXME
Base.length(T::SubgroupTransversal) = index(Int, T.G, T.H)

function Base.getindex(T::SubgroupTransversal, i::Int)
res = group_element(T.G, T.X[i])
res = group_element(T.G, GapObj(T)[i])
if T.side === :left
res = inv(res)
end
Expand Down Expand Up @@ -397,7 +399,7 @@ function right_transversal(G::T1, H::T2; check::Bool=true) where T1 <: GAPGroup
_check_compatible(G, H)
end
return SubgroupTransversal{T1, T2, eltype(T1)}(G, H, :right,
GAP.Globals.RightTransversal(G.X, H.X))
GAPWrap.RightTransversal(GapObj(G), GapObj(H)))
end

"""
Expand Down Expand Up @@ -437,11 +439,11 @@ function left_transversal(G::T1, H::T2; check::Bool=true) where T1 <: GAPGroup w
_check_compatible(G, H)
end
return SubgroupTransversal{T1, T2, eltype(T1)}(G, H, :left,
GAP.Globals.RightTransversal(G.X, H.X))
GAPWrap.RightTransversal(GapObj(G), GapObj(H)))
end

Base.IteratorSize(::Type{<:GroupCoset}) = Base.SizeUnknown()
Base.iterate(G::GroupCoset) = iterate(G, GAPWrap.Iterator(G.X))
Base.iterate(G::GroupCoset) = iterate(G, GAPWrap.Iterator(GapObj(G)))

function Base.iterate(G::GroupCoset, state)
GAPWrap.IsDoneIterator(state) && return nothing
Expand All @@ -463,24 +465,36 @@ struct GroupDoubleCoset{T <: GAPGroup, S <: GAPGroupElem}
H::GAPGroup
K::GAPGroup
repr::S
X::GapObj
X::Ref{GapObj}
size::Ref{ZZRingElem}

function GroupDoubleCoset(G::T, H::GAPGroup, K::GAPGroup, representative::S) where {T<: GAPGroup, S<:GAPGroupElem}
return new{T, S}(G, H, K, representative, Ref{GapObj}(), Ref{ZZRingElem}())
end
end

GAP.julia_to_gap(obj::GroupDoubleCoset) = obj.X
function GAP.julia_to_gap(C::GroupDoubleCoset)
if !isassigned(C.X)
C.X[] = GAPWrap.DoubleCoset(GapObj(C.H), GapObj(representative(C)), GapObj(C.K))
end
return C.X[]
end

Base.hash(x::GroupDoubleCoset, h::UInt) = h # FIXME
Base.eltype(::Type{GroupDoubleCoset{T,S}}) where {T,S} = S

function ==(x::GroupDoubleCoset, y::GroupDoubleCoset)
return x.X == y.X
# Avoid creating a GAP object if the result is obviously `false`.
isassigned(x.size) && isassigned(y.size) && order(x) != order(y) && return false
return GapObj(x) == GapObj(y)
end

function Base.show(io::IO, ::MIME"text/plain", x::GroupDoubleCoset)
io = pretty(io)
println(io, "Double coset of ", Lowercase(), x.H)
print(io, Indent())
println(io, "and ", Lowercase(), x.K)
println(io, "with representative ", x.repr)
println(io, "with representative ", representative(x))
print(io, "in ", Lowercase(), x.G)
print(io, Dedent())
end
Expand All @@ -492,7 +506,7 @@ function Base.show(io::IO, x::GroupDoubleCoset)
print(io, "Double coset of ")
io = pretty(io)
print(terse(io), Lowercase(), x.H,
" and ", Lowercase(), x.K, " with representative ", x.repr)
" and ", Lowercase(), x.K, " with representative ", representative(x))
end
end

Expand Down Expand Up @@ -526,9 +540,9 @@ Double coset of Sym(3)
"""
function double_coset(G::GAPGroup, g::GAPGroupElem, H::GAPGroup)
#T what if g is in some subgroup of a group of which G, H are also a subgroup?
@req GAPWrap.IsSubset(parent(g).X,G.X) "G is not a subgroup of parent(g)"
@req GAPWrap.IsSubset(parent(g).X,H.X) "H is not a subgroup of parent(g)"
return GroupDoubleCoset(parent(g),G,H,g,GAP.Globals.DoubleCoset(G.X,g.X,H.X))
@req GAPWrap.IsSubset(GapObj(parent(g)), GapObj(G)) "G is not a subgroup of parent(g)"
@req GAPWrap.IsSubset(GapObj(parent(g)), GapObj(H)) "H is not a subgroup of parent(g)"
return GroupDoubleCoset(parent(g), G, H, g)
end

Base.:*(H::GAPGroup, g::GAPGroupElem, K::GAPGroup) = double_coset(H,g,K)
Expand Down Expand Up @@ -558,31 +572,42 @@ julia> double_cosets(G,H,K)
```
"""
function double_cosets(G::T, H::GAPGroup, K::GAPGroup; check::Bool=true) where T <: GAPGroup
if !check
dcs = GAP.Globals.DoubleCosetsNC(G.X,H.X,K.X)
else
if check
@assert is_subset(H, G) "H is not a subgroup of G"
@assert is_subset(K, G) "K is not a subgroup of G"
dcs = GAP.Globals.DoubleCosets(G.X,H.X,K.X)
end
res = Vector{GroupDoubleCoset{T,elem_type(T)}}(undef, length(dcs))
for i = 1:length(res)
dc = dcs[i]
g = group_element(G, GAPWrap.Representative(dc))
res[i] = GroupDoubleCoset(G,H,K,g,dc)
dcs = GAPWrap.DoubleCosetRepsAndSizes(GapObj(G), GapObj(H), GapObj(K))
res = Vector{GroupDoubleCoset{T, elem_type(T)}}(undef, length(dcs))
for i in 1:length(res)
g = group_element(G, dcs[i][1])
C = GroupDoubleCoset(G, H, K, g)
n = dcs[i][2]
C.size[] = ZZRingElem(n)
res[i] = C
end
return res
#return [GroupDoubleCoset(G,H,K,group_element(G.X,GAPWrap.Representative(dc)),dc) for dc in dcs]
return res
end


"""
order(C::Union{GroupCoset,GroupDoubleCoset})
order(::Type{T} = ZZRingElem, C::Union{GroupCoset,GroupDoubleCoset})
Return the cardinality of the (double) coset `C`.
Return the cardinality of the (double) coset `C`,
as an instance of the type `T`.
"""
order(C::Union{GroupCoset,GroupDoubleCoset}) = GAPWrap.Size(C.X)
Base.length(C::Union{GroupCoset,GroupDoubleCoset}) = GAPWrap.Size(C.X)
order(C::Union{GroupCoset,GroupDoubleCoset}) = order(ZZRingElem, C)

function order(::Type{T}, C::GroupCoset) where T <: IntegerUnion
return T(GAPWrap.Size(GapObj(C)))
end

function order(::Type{T}, C::GroupDoubleCoset) where T <: IntegerUnion
if !isassigned(C.size)
C.size[] = ZZRingElem(GAPWrap.Size(GapObj(C)))
end
return T(C.size[])
end

Base.length(C::Union{GroupCoset,GroupDoubleCoset}) = order(C)

"""
rand(rng::Random.AbstractRNG = Random.GLOBAL_RNG, C::Union{GroupCoset,GroupDoubleCoset})
Expand All @@ -593,7 +618,7 @@ using the random number generator `rng`.
Base.rand(C::Union{GroupCoset,GroupDoubleCoset}) = Base.rand(Random.GLOBAL_RNG, C)

function Base.rand(rng::Random.AbstractRNG, C::Union{GroupCoset,GroupDoubleCoset})
s = GAP.Globals.Random(GAP.wrap_rng(rng), C.X)
s = GAPWrap.Random(GAP.wrap_rng(rng), GapObj(C))
return group_element(C.G, s)
end

Expand All @@ -620,7 +645,7 @@ right_acting_group(C::GroupDoubleCoset) = C.K

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

Base.iterate(G::GroupDoubleCoset) = iterate(G, GAPWrap.Iterator(G.X))
Base.iterate(G::GroupDoubleCoset) = iterate(G, GAPWrap.Iterator(GapObj(G)))

function Base.iterate(G::GroupDoubleCoset, state)
GAPWrap.IsDoneIterator(state) && return nothing
Expand All @@ -634,18 +659,19 @@ end
Return a vector containing all elements belonging to all groups and cosets
in `V`.
"""
function intersect(V::AbstractVector{Union{<: GAPGroup, GroupCoset, GroupDoubleCoset}})
function Base.intersect(V::AbstractVector{Union{<: GAPGroup, GroupCoset, GroupDoubleCoset}})
if V[1] isa GAPGroup
G = V[1]
else
G = V[1].G
end
l = GAP.Obj([v.X for v in V])
ints = GAP.Globals.Intersection(l)
L = Vector{typeof(G)}(undef, length(ints))
l = GAP.Obj(V; recursive = true)
ints = GAPWrap.Intersection(l)
L = Vector{eltype(G)}(undef, length(ints))
for i in 1:length(ints)
L[i] = group_element(G,ints[i])
end

return L
end
#TODO: Can this method get called at all?
22 changes: 22 additions & 0 deletions src/Groups/perm.jl
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,28 @@ function perm(L::AbstractVector{<:IntegerUnion})
return PermGroupElem(symmetric_group(length(L)), GAPWrap.PermList(GapObj(L;recursive=true)))
end

"""
smaller_degree_permutation_representation(G::PermGroup) -> PermGroup, map
Return an isomorphic permutation group of smaller or equal degree
and the isomorphism from `G` to that group.
# Examples
```jldoctest
julia> g = symmetric_group(4);
julia> s, _ = sylow_subgroup(g, 3);
julia> rho = smaller_degree_permutation_representation(s)
(Permutation group of degree 3 and order 3, Hom: s -> permutation group)
```
"""
function smaller_degree_permutation_representation(G::PermGroup)
mp = GAP.Globals.SmallerDegreePermutationRepresentation(GapObj(G))
img = PermGroup(GAP.Globals.Image(mp))
return img, GAPGroupHomomorphism(G, img, mp)
end


@doc raw"""
perm(G::PermGroup, L::AbstractVector{<:IntegerUnion})
Expand Down
1 change: 1 addition & 0 deletions src/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,7 @@ export slpoly_ring
export small_generating_set, has_small_generating_set, set_small_generating_set
export small_group
export small_group_identification, has_small_group_identification
export smaller_degree_permutation_representation
export snub_cube
export snub_dodecahedron
export socle, has_socle, set_socle
Expand Down
7 changes: 7 additions & 0 deletions test/Groups/Permutations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,10 @@ end
@test cc == [[1, 2, 3], [4, 5], [6, 7], [8, 9, 10], [11, 12, 13, 14, 15]]
end
end

@testset "smaller_degree_permuation_group" begin
c = cperm(1:3,4:6,7:9)
G,_ = sub(symmetric_group(9), [c])
H,iso = smaller_degree_permutation_representation(G)
@test degree(H)<degree(G)
end
Loading

0 comments on commit bfcfee3

Please sign in to comment.