Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 91 additions & 5 deletions src/Groups/cosets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ the set $HgK = \{ hgk; h \in H, k \in K \}$ is a $H-K$-double coset in $G$.

- [`left_acting_group(C::GroupDoubleCoset)`](@ref) returns $H$.

- [`right_acting_group(C::GroupDoubleCoset)`](@ref) returns $H$.
- [`right_acting_group(C::GroupDoubleCoset)`](@ref) returns $K$.

- [`representative(C::GroupDoubleCoset)`](@ref) returns an element
(the same element for each call) of `C`.
Expand All @@ -607,9 +607,11 @@ struct GroupDoubleCoset{T <: GAPGroup, S <: GAPGroupElem}
repr::S
X::Ref{GapObj}
size::Ref{ZZRingElem}
right_coset_reps::Ref{Dict{GAPGroupElem, Tuple{GAPGroupElem, GAPGroupElem}}}

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}())
return new{T, S}(G, H, K, representative, Ref{GapObj}(), Ref{ZZRingElem}(),
Ref{Dict{GAPGroupElem, Tuple{GAPGroupElem, GAPGroupElem}}}())
end
end

Expand Down Expand Up @@ -874,9 +876,93 @@ function Base.in(g::GAPGroupElem, C::GroupCoset)
end

function Base.in(g::GAPGroupElem, C::GroupDoubleCoset)
return GapObj(g) in GapObj(C)
#TODO: avoid delegation to GAP?
# (GAP uses `RepresentativesContainedRightCosets`, `CanonicalRightCosetElement`)
if !isassigned(C.right_coset_reps)
C.right_coset_reps[] = _right_coset_reps(C)
end
canon = GAP.Globals.CanonicalRightCosetElement(GapObj(left_acting_group(C)), GapObj(g))
return haskey(C.right_coset_reps[], group_element(group(C), canon))
end

"""
_decompose(C::GroupDoubleCoset, x::GAPGroupElem)

Return `flag, u, v` such that `flag` is `true` if `x` is an element
of `C`, and `false` otherwise.

If `flag = true` then `x = u*g*v` holds where `g` is `representative(C)`,
`u` is an element of `left_acting_group(C)`,
and `v` is an element of `right_acting_group(C)`.

# Examples
```jldoctest
julia> G = symmetric_group(5);

julia> H = sylow_subgroup(G, 2)[1]; K = sylow_subgroup(G, 3)[1];

julia> x = gen(G, 1); C = double_coset(H, x, K);

julia> d = Oscar._decompose(C, x^3)
(true, (1,3)(2,4), (1,3,2))

julia> d[2] * x * d[3] == x^3
true

julia> Oscar._decompose(C, x^2)
(false, (), ())
```
"""
function _decompose(C::GroupDoubleCoset, x::GAPGroupElem)
G = group(C)
Comment thread
simonbrandhorst marked this conversation as resolved.
U = left_acting_group(C)
rcr = _right_coset_reps(C)
GAP_x = GapObj(x)
GAP_y = GAP.Globals.CanonicalRightCosetElement(GapObj(U), GAP_x)
y = group_element(G, GAP_y)
haskey(rcr, y) || return false, one(U), one(right_acting_group(C))
u, v = rcr[y]
return true, group_element(U, GAP_x/GAP_y)*u, v
end

function _right_coset_reps(C)
if !isassigned(C.right_coset_reps)
C.right_coset_reps[] = _compute_right_coset_reps(C)
end
return C.right_coset_reps[]
end
# Compute the data for the (constructive) membership test.
function _compute_right_coset_reps(C::GroupDoubleCoset)
# `C = UxV`
G = group(C)
U = left_acting_group(C)
V = right_acting_group(C)
Gx = representative(C)
x = GapObj(Gx)
GAP_U = GapObj(U)

# `C` is a disjoint union of right cosets `Ur`
# where each representative `r` is canonical and has the form `u_r*x*v_r`,
# with `u_r` in `U` and `v_r` in `V`.
# `data` stores `(u_r, v_r)` at the key `r`.
y = GAP.Globals.CanonicalRightCosetElement(GAP_U, x)
Gy = group_element(G, y)
data = Dict{GAPGroupElem, Tuple{GAPGroupElem, GAPGroupElem}}(Gy => (group_element(U, y/x), one(V)))
orb = IndexedSet([Gy])
for Gr in orb
r = GapObj(Gr)
for v in gens(V)
rv = r*GapObj(v)
k = GAP.Globals.CanonicalRightCosetElement(GAP_U, rv)
Gk = group_element(G, k)
if !(Gk in orb)
# `k = u*r*v` for some `u` in `U`
# `r = u_r*x*v_r` means `u_k = u*u_r`, `v_k = v_r*v`.
u_r, v_r = data[Gr]
data[Gk] = (group_element(U, k/rv)*u_r, v_r*v)
push!(orb, Gk)
end
end
end
return data
end

Base.IteratorSize(::Type{<:GroupCoset{TG, TH, S}}) where {TG, TH, S} = Base.IteratorSize(TH)
Expand Down
9 changes: 9 additions & 0 deletions test/Groups/subgroups_and_cosets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -305,11 +305,20 @@ end
dc = double_coset(H, x, K)
@test !isassigned(dc.X)
@test !isassigned(dc.size)
@test !isassigned(dc.right_coset_reps)
GapObj(dc)
@test isassigned(dc.X)
order(dc)
@test isassigned(dc.size)
@test x in dc
@test isassigned(dc.right_coset_reps)
@test GapObj([dc]; recursive = true) isa GapObj

@test !(y in dc)
@test !Oscar._decompose(dc, y)[1]
r = rand(dc)
flag, u, v = Oscar._decompose(dc, r)
@test flag && (r == u*x*v)
end

@testset "Predicates for groups" begin
Expand Down
Loading