Skip to content

Commit 83a89a0

Browse files
author
Release Manager
committed
Trac #33784: provide a framework for finite group actions
This ticket provides an easy user interface to define a permutation group corresponding to a finite group action. {{{ sage: a = lambda x: (2*x) % 7 sage: H = PermutationGroup(action=a, domain=range(7)) sage: H.orbits() [[0], [1, 2, 4], [3, 6, 5]] sage: H.gens() [(1,2,4), (3,6,5)] sage: a = lambda g, x: vector(g*x, immutable=True) sage: X = [vector(x, immutable=True) for x in GF(3)^2] sage: G = SL(2,3); G.gens() ( [1 1] [0 1] [0 1], [2 0] ) sage: H = PermutationGroup(G.gens(), action=a, domain=X) sage: H.orbits() [[(0, 0)], [(1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)]] sage: H.gens() [((0,1),(1,1),(2,1))((0,2),(2,2),(1,2)), ((1,0),(0,2),(2,0),(0,1))((1,1),(1,2),(2,2),(2,1))] }}} URL: https://trac.sagemath.org/33784 Reported by: mantepse Ticket author(s): Martin Rubey Reviewer(s): David Joyner, Travis Scrimshaw
2 parents fa3b529 + fdea936 commit 83a89a0

File tree

1 file changed

+144
-1
lines changed

1 file changed

+144
-1
lines changed

src/sage/groups/perm_gps/permgroup.py

+144-1
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,39 @@ def PermutationGroup(gens=None, *args, **kwds):
331331
sage: G2.GeneratorsSmallest()
332332
[ (3,4,5), (2,3)(4,5), (1,2)(4,5) ]
333333
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+
334367
TESTS::
335368
336369
sage: r = Permutation("(1,7,9,3)(2,4,8,6)")
@@ -350,6 +383,7 @@ def PermutationGroup(gens=None, *args, **kwds):
350383
...
351384
DeprecationWarning: gap_group, domain, canonicalize, category will become keyword only
352385
See https://trac.sagemath.org/31510 for details.
386+
353387
"""
354388
if not is_ExpectElement(gens) and hasattr(gens, '_permgroup_'):
355389
return gens._permgroup_()
@@ -359,6 +393,11 @@ def PermutationGroup(gens=None, *args, **kwds):
359393
domain = kwds.get("domain", None)
360394
canonicalize = kwds.get("canonicalize", True)
361395
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)
362401
if args:
363402
from sage.misc.superseded import deprecation
364403
deprecation(31510, "gap_group, domain, canonicalize, category will become keyword only")
@@ -1299,7 +1338,7 @@ def one(self):
12991338
EXAMPLES::
13001339
13011340
sage: G = PermutationGroup([[(1,2,3),(4,5)]])
1302-
sage: e = G.identity()
1341+
sage: e = G.identity() # indirect doctest
13031342
sage: e
13041343
()
13051344
sage: g = G.gen(0)
@@ -5015,5 +5054,109 @@ def is_normal(self, other=None):
50155054
# Allow for subclasses to use a different subgroup class
50165055
PermutationGroup_generic.Subgroup = PermutationGroup_subgroup
50175056

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 \times X\to X` if ``gens``
5096+
is given, or a cyclic group action `X\to 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+
50185161
from sage.misc.rest_index_of_methods import gen_rest_table_index
50195162
__doc__ = __doc__.format(METHODS_OF_PermutationGroup_generic=gen_rest_table_index(PermutationGroup_generic))

0 commit comments

Comments
 (0)