@@ -331,6 +331,39 @@ def PermutationGroup(gens=None, *args, **kwds):
331
331
sage: G2.GeneratorsSmallest()
332
332
[ (3,4,5), (2,3)(4,5), (1,2)(4,5) ]
333
333
334
+ We can create a permutation group from a group action::
335
+
336
+ sage: a = lambda x: (2*x) % 7
337
+ sage: H = PermutationGroup(action=a, domain=range(7))
338
+ sage: H.orbits()
339
+ ((0,), (1, 2, 4), (3, 6, 5))
340
+ sage: H.gens()
341
+ ((1,2,4), (3,6,5))
342
+
343
+ Note that we provide generators for the acting group. The
344
+ permutation group we construct is its homomorphic image::
345
+
346
+ sage: a = lambda g, x: vector(g*x, immutable=True)
347
+ sage: X = [vector(x, immutable=True) for x in GF(3)^2]
348
+ sage: G = SL(2,3); G.gens()
349
+ (
350
+ [1 1] [0 1]
351
+ [0 1], [2 0]
352
+ )
353
+ sage: H = PermutationGroup(G.gens(), action=a, domain=X)
354
+ sage: H.orbits()
355
+ (((0, 0),), ((1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)))
356
+ sage: H.gens()
357
+ (((0,1),(1,1),(2,1))((0,2),(2,2),(1,2)),
358
+ ((1,0),(0,2),(2,0),(0,1))((1,1),(1,2),(2,2),(2,1)))
359
+
360
+ The orbits of the conjugation action are the conjugacy classes,
361
+ i.e., in bijection with integer partitions::
362
+
363
+ sage: a = lambda g, x: g*x*g^-1
364
+ sage: [len(PermutationGroup(SymmetricGroup(n).gens(), action=a, domain=SymmetricGroup(n)).orbits()) for n in range(1, 8)]
365
+ [1, 2, 3, 5, 7, 11, 15]
366
+
334
367
TESTS::
335
368
336
369
sage: r = Permutation("(1,7,9,3)(2,4,8,6)")
@@ -350,6 +383,7 @@ def PermutationGroup(gens=None, *args, **kwds):
350
383
...
351
384
DeprecationWarning: gap_group, domain, canonicalize, category will become keyword only
352
385
See https://trac.sagemath.org/31510 for details.
386
+
353
387
"""
354
388
if not is_ExpectElement (gens ) and hasattr (gens , '_permgroup_' ):
355
389
return gens ._permgroup_ ()
@@ -359,6 +393,11 @@ def PermutationGroup(gens=None, *args, **kwds):
359
393
domain = kwds .get ("domain" , None )
360
394
canonicalize = kwds .get ("canonicalize" , True )
361
395
category = kwds .get ("category" , None )
396
+ action = kwds .get ("action" , None )
397
+ if action is not None :
398
+ if domain is None :
399
+ raise ValueError ("you must specify the domain for an action" )
400
+ return PermutationGroup_action (gens , action , domain , gap_group = gap_group )
362
401
if args :
363
402
from sage .misc .superseded import deprecation
364
403
deprecation (31510 , "gap_group, domain, canonicalize, category will become keyword only" )
@@ -1299,7 +1338,7 @@ def one(self):
1299
1338
EXAMPLES::
1300
1339
1301
1340
sage: G = PermutationGroup([[(1,2,3),(4,5)]])
1302
- sage: e = G.identity()
1341
+ sage: e = G.identity() # indirect doctest
1303
1342
sage: e
1304
1343
()
1305
1344
sage: g = G.gen(0)
@@ -5015,5 +5054,109 @@ def is_normal(self, other=None):
5015
5054
# Allow for subclasses to use a different subgroup class
5016
5055
PermutationGroup_generic .Subgroup = PermutationGroup_subgroup
5017
5056
5057
+ class PermutationGroup_action (PermutationGroup_generic ):
5058
+ """
5059
+ A permutation group given by a finite group action.
5060
+
5061
+ EXAMPLES:
5062
+
5063
+ A cyclic action::
5064
+
5065
+ sage: n = 3
5066
+ sage: a = lambda x: SetPartition([[e % n + 1 for e in b] for b in x])
5067
+ sage: S = SetPartitions(n)
5068
+ sage: G = PermutationGroup(action=a, domain=S)
5069
+ sage: G.orbits()
5070
+ (({{1}, {2}, {3}},),
5071
+ ({{1, 2}, {3}}, {{1}, {2, 3}}, {{1, 3}, {2}}),
5072
+ ({{1, 2, 3}},))
5073
+
5074
+ The regular action of the symmetric group::
5075
+
5076
+ sage: a = lambda g, x: g*x*g^-1
5077
+ sage: S = SymmetricGroup(3)
5078
+ sage: G = PermutationGroup(S.gens(), action=a, domain=S)
5079
+ sage: G.orbits()
5080
+ (((),), ((1,3,2), (1,2,3)), ((2,3), (1,3), (1,2)))
5081
+
5082
+ The trivial action of the symmetric group::
5083
+
5084
+ sage: PermutationGroup(SymmetricGroup(3).gens(), action=lambda g, x: x, domain=[1])
5085
+ Permutation Group with generators [()]
5086
+ """
5087
+ def __init__ (self , gens , action , domain , gap_group = None , category = None , canonicalize = None ):
5088
+ """
5089
+ Initialize ``self``.
5090
+
5091
+ INPUT:
5092
+
5093
+ - ``gens`` -- list of generators of the group or ``None`` if the action is cyclic
5094
+
5095
+ - ``action`` -- a group action `G \t imes X\t o X` if ``gens``
5096
+ is given, or a cyclic group action `X\t o X` if ``gens`` is
5097
+ ``None``
5098
+
5099
+ - ``domain`` -- the set the group is acting on
5100
+
5101
+ - ``gap_group`` -- a gap or libgap permutation group, or a string
5102
+ defining one (default: ``None``), this is currently not supported
5103
+
5104
+ - ``canonicalize`` -- bool (default: ``True``); if ``True``,
5105
+ sort generators and remove duplicates
5106
+
5107
+ OUTPUT:
5108
+
5109
+ - A finite group action given as a permutation group.
5110
+
5111
+ EXAMPLES::
5112
+
5113
+ sage: a = lambda x: (2*x) % 7
5114
+ sage: G = PermutationGroup(action=a, domain=range(7))
5115
+ sage: G.orbits()
5116
+ ((0,), (1, 2, 4), (3, 6, 5))
5117
+
5118
+ """
5119
+ from sage .combinat .cyclic_sieving_phenomenon import orbit_decomposition
5120
+ from sage .sets .disjoint_set import DisjointSet
5121
+ if gap_group is not None :
5122
+ raise ValueError ("gap_group is not supported with action" )
5123
+ if gens is None :
5124
+ self ._orbits = tuple (tuple (o ) for o in orbit_decomposition (domain , action ))
5125
+ gens = [o for o in self ._orbits if len (o ) > 1 ]
5126
+ else :
5127
+ g_orbits = [orbit_decomposition (domain , lambda x : action (g , x ))
5128
+ for g in gens ]
5129
+ gens = []
5130
+ for g_orbit in g_orbits :
5131
+ g_gens = [tuple (o ) for o in g_orbit if len (o ) > 1 ]
5132
+ if g_gens :
5133
+ gens .append (g_gens )
5134
+
5135
+ D = DisjointSet (domain )
5136
+ for g_orbit in g_orbits :
5137
+ for o in g_orbit :
5138
+ for i in range (1 , len (o )):
5139
+ D .union (o [0 ], o [i ])
5140
+ self ._orbits = tuple (tuple (o ) for o in D )
5141
+
5142
+ PermutationGroup_generic .__init__ (self , gens = gens ,
5143
+ gap_group = gap_group , domain = domain ,
5144
+ category = category ,
5145
+ canonicalize = canonicalize )
5146
+
5147
+ def orbits (self ):
5148
+ """
5149
+ Returns the orbits of the elements of the domain under the
5150
+ default group action.
5151
+
5152
+ EXAMPLES::
5153
+
5154
+ sage: a = lambda x: (2*x) % 7
5155
+ sage: G = PermutationGroup(action=a, domain=range(7))
5156
+ sage: G.orbits()
5157
+ ((0,), (1, 2, 4), (3, 6, 5))
5158
+ """
5159
+ return self ._orbits
5160
+
5018
5161
from sage .misc .rest_index_of_methods import gen_rest_table_index
5019
5162
__doc__ = __doc__ .format (METHODS_OF_PermutationGroup_generic = gen_rest_table_index (PermutationGroup_generic ))
0 commit comments