diff --git a/docs/src/Groups/action.md b/docs/src/Groups/action.md index 9ed4fc62f152..f18d06c26be8 100644 --- a/docs/src/Groups/action.md +++ b/docs/src/Groups/action.md @@ -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) ``` diff --git a/src/GAP/wrappers.jl b/src/GAP/wrappers.jl index d248a4572fd5..91679de8a744 100644 --- a/src/GAP/wrappers.jl +++ b/src/GAP/wrappers.jl @@ -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 @@ -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 diff --git a/src/Groups/action.jl b/src/Groups/action.jl index 2fb15b4313c0..8c7624163e9d 100644 --- a/src/Groups/action.jl +++ b/src/Groups/action.jl @@ -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 @@ -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)) """ @@ -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) @@ -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] @@ -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] @@ -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) @@ -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) @@ -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 @@ -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 @@ -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, @@ -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 @@ -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) diff --git a/src/Groups/gsets.jl b/src/Groups/gsets.jl index 9f81893c9aaf..7e148722d650 100644 --- a/src/Groups/gsets.jl +++ b/src/Groups/gsets.jl @@ -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. @@ -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; diff --git a/test/Groups/gsets.jl b/test/Groups/gsets.jl index becacba0c2a8..c69eabcb8e45 100644 --- a/test/Groups/gsets.jl +++ b/test/Groups/gsets.jl @@ -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) @@ -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) @@ -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) @@ -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) @@ -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 @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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,