Skip to content

Commit

Permalink
add stabilizer for G-sets (#4206)
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomasBreuer authored Oct 17, 2024
1 parent 81eecc2 commit 8451da1
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 25 deletions.
3 changes: 2 additions & 1 deletion docs/src/Groups/action.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,6 @@ orbits(Omega::T) where T <: GSetByElements{TG} where TG <: GAPGroup
## Stabilizers

```@docs
stabilizer(G::Oscar.GAPGroup, pnt::Any, actfun::Function)
stabilizer(G::GAPGroup, pnt::Any, actfun::Function)
stabilizer(Omega::GSet)
```
2 changes: 2 additions & 0 deletions src/GAP/wrappers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ GAP.@wrap EpimorphismSchurCover(x::GapObj)::GapObj
GAP.@wrap ExponentsOfPcElement(x::GapObj, y::GapObj)::GapObj
GAP.@wrap ExtRepOfObj(x::GapObj)::GapObj
GAP.@wrap ExtRepPolynomialRatFun(x::GapObj)::GapObj
GAP.@wrap FactorCosetAction(x::GapObj, y::GapObj)::GapObj
GAP.@wrap FamilyObj(x::GAP.Obj)::GapObj
GAP.@wrap FamilyPcgs(x::GAP.Obj)::GapObj
GAP.@wrap Field(x::Any)::GapObj
Expand Down Expand Up @@ -342,6 +343,7 @@ GAP.@wrap SizesCentralizers(x::GapObj)::GapObj
GAP.@wrap SizesConjugacyClasses(x::GapObj)::GapObj
GAP.@wrap Source(x::GapObj)::GapObj
GAP.@wrap Sqrt(x::Int64)::GAP.Obj
GAP.@wrap Stabilizer(v::GapObj, w::Any, x::GapObj, y::GapObj, z::GapObj)::GapObj
GAP.@wrap StringViewObj(x::Any)::GapObj
GAP.@wrap StructureConstantsTable(x::GapObj)::GapObj
GAP.@wrap StructureDescription(x::GapObj)::GapObj
Expand Down
43 changes: 22 additions & 21 deletions src/Groups/action.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ julia> g = GL(2,3);
julia> m = g[1]
[ [ Z(3), 0*Z(3) ], [ 0*Z(3), Z(3)^0 ] ]
julia> v = m.X[1]
julia> v = GapObj(m)[1]
GAP: [ Z(3), 0*Z(3) ]
julia> v^m
Expand All @@ -32,9 +32,9 @@ GAP: [ Z(3)^0, 0*Z(3) ]
```
"""

^(pnt::GAP.Obj, x::GAPGroupElem) = GAP.Globals.:^(pnt, x.X)
^(pnt::GAP.Obj, x::GAPGroupElem) = GAP.Globals.:^(pnt, GapObj(x))

*(pnt::GAP.Obj, x::GAPGroupElem) = GAP.Globals.:*(pnt, x.X)
*(pnt::GAP.Obj, x::GAPGroupElem) = GAP.Globals.:*(pnt, GapObj(x))


"""
Expand Down Expand Up @@ -72,7 +72,7 @@ julia> (1, 2, 4)^g[1]
(2, 3, 4)
```
"""
on_tuples(tuple::GapObj, x::GAPGroupElem) = GAPWrap.OnTuples(tuple, x.X)
on_tuples(tuple::GapObj, x::GAPGroupElem) = GAPWrap.OnTuples(tuple, GapObj(x))

on_tuples(tuple::Vector{T}, x::GAPGroupElem) where T = T[pnt^x for pnt in tuple]
^(tuple::Vector{T}, x::GAPGroupElem) where T = on_tuples(tuple, x)
Expand Down Expand Up @@ -124,7 +124,7 @@ BitSet with 2 elements:
2
```
"""
on_sets(set::GapObj, x::GAPGroupElem) = GAPWrap.OnSets(set, x.X)
on_sets(set::GapObj, x::GAPGroupElem) = GAPWrap.OnSets(set, GapObj(x))

function on_sets(set::Vector{T}, x::GAPGroupElem) where T
res = T[pnt^x for pnt in set]
Expand Down Expand Up @@ -188,7 +188,7 @@ julia> ans == setset^g[1]
true
```
"""
on_sets_sets(set::GapObj, x::GAPGroupElem) = GAPWrap.OnSetsSets(set, x.X)
on_sets_sets(set::GapObj, x::GAPGroupElem) = GAPWrap.OnSetsSets(set, GapObj(x))

function on_sets_sets(set::Vector{T}, x::GAPGroupElem) where T
res = T[on_sets(pnt, x) for pnt in set]
Expand Down Expand Up @@ -240,7 +240,7 @@ julia> permuted(l, g[1])
GAP: [ "c", "a", "b" ]
```
"""
permuted(pnt::GapObj, x::PermGroupElem) = GAPWrap.Permuted(pnt, x.X)
permuted(pnt::GapObj, x::PermGroupElem) = GAPWrap.Permuted(pnt, GapObj(x))

function permuted(pnt::Vector{T}, x::PermGroupElem) where T
invx = inv(x)
Expand Down Expand Up @@ -286,7 +286,7 @@ julia> on_indeterminates(f, p)
GAP: x_1*x_3+x_2*x_3
```
"""
on_indeterminates(f::GapObj, p::PermGroupElem) = GAPWrap.OnIndeterminates(f, p.X)
on_indeterminates(f::GapObj, p::PermGroupElem) = GAPWrap.OnIndeterminates(f, GapObj(p))

function on_indeterminates(f::MPolyRingElem, s::PermGroupElem)
G = parent(s)
Expand Down Expand Up @@ -349,7 +349,7 @@ function on_indeterminates(f::GapObj, p::MatrixGroupElem)
n = nrows(p)
fam = GAPWrap.CoefficientsFamily(GAPWrap.FamilyObj(f))
indets = GapObj([GAPWrap.Indeterminate(fam, i) for i in 1:n])
return GAPWrap.Value(f, indets, p.X * indets)
return GAPWrap.Value(f, indets, GapObj(p) * indets)
end

function on_indeterminates(f::MPolyRingElem{T}, p::MatrixGroupElem{T}) where T
Expand Down Expand Up @@ -413,7 +413,7 @@ julia> on_lines(v, m)
(1, 4)
```
"""
on_lines(line::GapObj, x::GAPGroupElem) = GAPWrap.OnLines(line, x.X)
on_lines(line::GapObj, x::GAPGroupElem) = GAPWrap.OnLines(line, GapObj(x))

function on_lines(line::AbstractAlgebra.Generic.FreeModuleElem, x::GAPGroupElem)
res = line * x
Expand Down Expand Up @@ -448,17 +448,18 @@ true
```
"""
function on_subgroups(x::GapObj, g::GAPGroupElem)
return GAPWrap.Image(g.X, x)
return GAPWrap.Image(GapObj(g), x)
end

on_subgroups(x::T, g::GAPGroupElem) where T <: GAPGroup = T(on_subgroups(x.X, g))
on_subgroups(x::T, g::GAPGroupElem) where T <: GAPGroup = T(on_subgroups(GapObj(x), g))

@doc raw"""
stabilizer(G::Oscar.GAPGroup, pnt::Any[, actfun::Function])
stabilizer(G::GAPGroup, pnt::Any[, actfun::Function])
Return the subgroup of `G` that consists of all those elements `g`
that fix `pnt` under the action given by `actfun`,
that is, `actfun(pnt, g) == pnt` holds.
Return `S, emb` where `S` is the subgroup of `G` that consists of
all those elements `g` that fix `pnt` under the action given by `actfun`,
that is, `actfun(pnt, g) == pnt` holds,
and `emb` is the embedding of `S` into `G`.
The default for `actfun` depends on the types of `G` and `pnt`:
If `G` is a `PermGroup` then the default actions on integers,
Expand All @@ -485,10 +486,10 @@ julia> S = stabilizer(G, [1, 1, 2, 2, 3], permuted); order(S[1])
4
```
"""
function stabilizer(G::Oscar.GAPGroup, pnt::Any, actfun::Function)
return Oscar._as_subgroup(G, GAP.Globals.Stabilizer(G.X, pnt,
GapObj([x.X for x in gens(G)]), GapObj(gens(G)),
GAP.WrapJuliaFunc(actfun))::GapObj)
function stabilizer(G::GAPGroup, pnt::Any, actfun::Function)
return Oscar._as_subgroup(G, GAPWrap.Stabilizer(GapObj(G), pnt,
GapObj(gens(G), recursive = true), GapObj(gens(G)),
GapObj(actfun)))
end

# natural stabilizers in permutation groups
Expand Down Expand Up @@ -529,7 +530,7 @@ true
"""
function right_coset_action(G::GAPGroup, U::GAPGroup)
_check_compatible(G, U)
mp = GAP.Globals.FactorCosetAction(G.X, U.X)
mp = GAPWrap.FactorCosetAction(GapObj(G), GapObj(U))
@req mp !== GAP.Globals.fail "Invalid input"
H = PermGroup(GAPWrap.Range(mp))
return GAPGroupHomomorphism(G, H, mp)
Expand Down
30 changes: 29 additions & 1 deletion src/Groups/gsets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import Hecke.orbit


"""
GSetByElements{T,S} <: GSet{T}
GSetByElements{T,S} <: GSet{T,S}
Objects of this type represent G-sets that are willing to write down
orbits and elements lists as vectors.
Expand Down Expand Up @@ -442,6 +442,34 @@ julia> map(length, orbs)
@attr Vector{GSetByElements{PermGroup, Int}} orbits(G::PermGroup) = orbits(gset(G))


"""
stabilizer(Omega::GSet{T,S})
stabilizer(Omega::GSet{T,S}, omega::S = representative(Omega); check::Bool = true) where {T,S}
Return the subgroup of `G = acting_group(Omega)` that fixes `omega`,
together with the embedding of this subgroup into `G`.
If `check` is `false` then it is not checked whether `omega` is in `Omega`.
# Examples
```jldoctest
julia> Omega = gset(symmetric_group(3));
julia> stabilizer(Omega)
(Permutation group of degree 3 and order 2, Hom: permutation group -> Sym(3))
```
"""
@attr Tuple{sub_type(T), Map{sub_type(T), T}} function stabilizer(Omega::GSet{T,S}) where {T,S}
return stabilizer(Omega, representative(Omega), check = false)
end

function stabilizer(Omega::GSet{T,S}, omega::S; check::Bool = true) where {T,S}
check && @req omega in Omega "omega must be an element of Omega"
G = acting_group(Omega)
gfun = action_function(Omega)
return stabilizer(G, omega, gfun)
end


#############################################################################
##
## `:elements` a vector of points;
Expand Down
33 changes: 31 additions & 2 deletions test/Groups/gsets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
@test ! is_regular(Omega)
@test ! is_semiregular(Omega)
@test collect(Omega) == 1:6 # ordering is kept
@test order(stabilizer(Omega)[1]) * length(Omega) == order(G)

Omega = gset(G, [Set([1, 2])]) # action on unordered pairs
@test isa(Omega, GSet)
@test length(Omega) == 15
@test order(stabilizer(Omega)[1]) * length(Omega) == order(G)
@test length(orbits(Omega)) == 1
@test is_transitive(Omega)
@test ! is_regular(Omega)
Expand All @@ -23,6 +25,7 @@
Omega = gset(G, [[1, 2]]) # action on ordered pairs
@test isa(Omega, GSet)
@test length(Omega) == 30
@test order(stabilizer(Omega)[1]) * length(Omega) == order(G)
@test length(orbits(Omega)) == 1
@test is_transitive(Omega)
@test ! is_regular(Omega)
Expand All @@ -31,15 +34,18 @@
Omega = gset(G, [(1, 2)]) # action on ordered pairs (repres. by tuples)
@test isa(Omega, GSet)
@test length(Omega) == 30
@test order(stabilizer(Omega)[1]) * length(Omega) == order(G)
@test length(orbits(Omega)) == 1
@test is_transitive(Omega)
@test ! is_regular(Omega)
@test ! is_semiregular(Omega)

# constructions by explicit action functions
Omega = gset(G, permuted, [[0,1,0,1,0,1], [1,2,3,4,5,6]])
omega = [0,1,0,1,0,1]
Omega = gset(G, permuted, [omega, [1,2,3,4,5,6]])
@test isa(Omega, GSet)
@test length(Omega) == 740
@test order(stabilizer(Omega, omega)[1]) * length(orbit(Omega, omega)) == order(G)
@test length(orbits(Omega)) == 2
@test ! is_transitive(Omega)
@test ! is_regular(Omega)
Expand All @@ -52,6 +58,7 @@
@test isa(Omega, GSet)
@test length(Omega) == 3
@test length(orbits(Omega)) == 1
@test order(stabilizer(Omega)[1]) * length(orbit(Omega, f)) == order(G)
@test is_transitive(Omega)
@test ! is_regular(Omega)
@test ! is_semiregular(Omega)
Expand Down Expand Up @@ -141,6 +148,14 @@
rep = is_conjugate_with_data(Omega, [0,1,0,1,0,1], [1,2,3,4,5,6])
@test ! rep[1]

# stabilizer
G = symmetric_group(6)
Omega = gset(G, permuted, [[0,1,0,1,0,1], [1,2,3,4,5,6]])
@test_throws ArgumentError stabilizer(Omega, [0,0,0,0,0,0])
omega = representative(Omega)
@test stabilizer(Omega) == stabilizer(Omega, omega)
@test stabilizer(Omega) !== stabilizer(Omega, omega)
@test stabilizer(Omega) === stabilizer(Omega)
end

@testset "natural action of permutation groups" begin
Expand Down Expand Up @@ -210,18 +225,21 @@ end
# natural constructions (determined by the types of the seeds)
G = general_linear_group(2, 3)
V = free_module(base_ring(G), degree(G))
v = gen(V, 1)
Omega = gset(G)
@test isa(Omega, GSet)
@test length(Omega) == 9
@test order(stabilizer(Omega, v)[1]) * length(orbit(Omega, v)) == order(G)
@test length(orbits(Omega)) == 2
@test ! is_transitive(Omega)
@test ! is_regular(Omega)
@test ! is_semiregular(Omega)
@test collect(Omega) == collect(V) # ordering is kept

Omega = orbit(G, gen(V, 1))
Omega = orbit(G, v)
@test isa(Omega, GSet)
@test length(Omega) == 8
@test order(stabilizer(Omega, v)[1]) * length(Omega) == order(G)
@test length(orbits(Omega)) == 1
@test is_transitive(Omega)
@test ! is_regular(Omega)
Expand All @@ -230,6 +248,7 @@ end
Omega = gset(G, [Set(gens(V))]) # action on unordered pairs of vectors
@test isa(Omega, GSet)
@test length(Omega) == 24
@test order(stabilizer(Omega)[1]) * length(Omega) == order(G)
@test length(orbits(Omega)) == 1
@test is_transitive(Omega)
@test ! is_regular(Omega)
Expand All @@ -238,6 +257,7 @@ end
Omega = gset(G, [gens(V)]) # action on ordered pairs of vectors
@test isa(Omega, GSet)
@test length(Omega) == 48
@test order(stabilizer(Omega)[1]) * length(Omega) == order(G)
@test length(orbits(Omega)) == 1
@test is_transitive(Omega)
@test is_regular(Omega)
Expand Down Expand Up @@ -325,6 +345,7 @@ end
@test isa(Omega, GSet)
@test acting_group(Omega) == G
@test length(Omega) == index(G, H)
@test order(stabilizer(Omega)[1]) * length(Omega) == order(G)
@test Omega[end] == Omega[length(Omega)]
@test length(orbits(Omega)) == 1
@test is_transitive(Omega)
Expand Down Expand Up @@ -386,6 +407,7 @@ end
@test isa(Omega, GSet)
@test acting_group(Omega) == G
@test length(Omega) == index(G, H)
@test order(stabilizer(Omega)[1]) * length(Omega) == order(G)
@test Omega[end] == Omega[length(Omega)]
@test length(orbits(Omega)) == 1
@test is_transitive(Omega)
Expand Down Expand Up @@ -439,6 +461,13 @@ end
@test Oscar.action_function(Omega)(x, rep[2]) == y
end

@testset "G-sets of PcGroups" begin
G = small_group(24, 12)
Omega = orbit(G, gen(G, 1))
S, mp = stabilizer(Omega)
@test length(Omega) == index(G, S)
end

@testset "G-sets of FinGenAbGroups" begin
# Define an action on class functions.
function galois_conjugate(chi::Oscar.GAPGroupClassFunction,
Expand Down

0 comments on commit 8451da1

Please sign in to comment.