Skip to content

Commit 33d6cc5

Browse files
author
Release Manager
committed
gh-36224: Add cycle_type() and fix to_cycle() to SignedPermutation. <!-- ^^^^^ Please provide a concise, informative and self-explanatory title. Don't put issue numbers in there, do this in the PR body below. For example, instead of "Fixes #1234" use "Introduce new method to calculate 1+1" --> <!-- Describe your changes here in detail --> We fix a "bug" in `to_cycle()` of `SignedPermutation` that does not return the negation of a positive cycle, and add an option to hide these cycles (the current behavior in Sage). We add a `cycle_type()` method that returns the corresponding `PartitionTuple` object. We add a `conjugacy_class_reprensentative()` method. A while-we-are-at-it thing: we fix a bug in the error output for `Permutation.element_in_conjugacy_class()` (which a better name IMO is `conjugacy_class_reprensentative()`). <!-- Why is this change required? What problem does it solve? --> <!-- If this PR resolves an open issue, please link to it here. For example "Fixes #12345". --> <!-- If your change requires a documentation PR, please link it appropriately. --> ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> <!-- If your change requires a documentation PR, please link it appropriately --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> <!-- Feel free to remove irrelevant items. --> - [x] The title is concise, informative, and self-explanatory. - [x] The description explains in detail what this PR is about. - [x] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [x] I have updated the documentation accordingly. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on - #12345: short description why this is a dependency - #34567: ... --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> URL: #36224 Reported by: Travis Scrimshaw Reviewer(s): ayyer, Martin Rubey, Travis Scrimshaw
2 parents 3e6db24 + e3397a7 commit 33d6cc5

File tree

2 files changed

+140
-12
lines changed

2 files changed

+140
-12
lines changed

src/sage/combinat/colored_permutations.py

Lines changed: 134 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,14 +1189,13 @@ def has_left_descent(self, i):
11891189
return self._colors[i] == 1 or self._perm[i - 1] < self._perm[i]
11901190
return self._colors[i] == 1 and self._perm[i - 1] > self._perm[i]
11911191

1192-
def to_cycles(self, singletons=True, use_min=True, negative_singletons=True):
1193-
"""
1192+
def to_cycles(self, singletons=True, use_min=True, negative_cycles=True):
1193+
r"""
11941194
Return the signed permutation ``self`` as a list of disjoint cycles.
11951195
11961196
The cycles are returned in the order of increasing smallest
11971197
elements, and each cycle is returned as a tuple which starts
1198-
with its smallest positive element. We do not include the
1199-
corresponding negative cycles.
1198+
with its smallest positive element.
12001199
12011200
INPUT:
12021201
@@ -1205,18 +1204,30 @@ def to_cycles(self, singletons=True, use_min=True, negative_singletons=True):
12051204
- ``use_min`` -- (default: ``True``) if ``False``, the cycles are
12061205
returned in the order of increasing *largest* (not smallest)
12071206
elements, and each cycle starts with its largest element
1207+
- ``negative_cycles`` -- (default: ``True``) if ``False``, for any
1208+
two cycles `C^{\pm} = \{\pm c_1, \ldots, \pm c_k\}` such that
1209+
`C^+ \neq C^-`, this does not include the cycle `C^-`
1210+
1211+
.. WARNING::
1212+
1213+
The arugment ``negative_cycles`` does not refer to the usual
1214+
definition of a negative cycle; see :meth:`cycle_type`.
12081215
12091216
EXAMPLES::
12101217
12111218
sage: pi = SignedPermutations(7)([2,-1,4,-6,-5,-3,7])
12121219
sage: pi.to_cycles()
1213-
[(1, 2, -1, -2), (3, 4, -6), (5, -5), (7,)]
1220+
[(1, 2, -1, -2), (3, 4, -6), (-3, -4, 6), (5, -5), (7,), (-7,)]
12141221
sage: pi.to_cycles(singletons=False)
1222+
[(1, 2, -1, -2), (3, 4, -6), (-3, -4, 6), (5, -5)]
1223+
sage: pi.to_cycles(negative_cycles=False)
1224+
[(1, 2, -1, -2), (3, 4, -6), (5, -5), (7,)]
1225+
sage: pi.to_cycles(singletons=False, negative_cycles=False)
12151226
[(1, 2, -1, -2), (3, 4, -6), (5, -5)]
12161227
sage: pi.to_cycles(use_min=False)
1217-
[(7,), (6, -3, -4), (5, -5), (2, -1, -2, 1)]
1228+
[(7,), (-7,), (6, -3, -4), (-6, 3, 4), (5, -5), (2, -1, -2, 1)]
12181229
sage: pi.to_cycles(singletons=False, use_min=False)
1219-
[(6, -3, -4), (5, -5), (2, -1, -2, 1)]
1230+
[(6, -3, -4), (-6, 3, 4), (5, -5), (2, -1, -2, 1)]
12201231
"""
12211232
cycles = []
12221233

@@ -1235,19 +1246,70 @@ def to_cycles(self, singletons=True, use_min=True, negative_singletons=True):
12351246
cycle = [cycle_first]
12361247
l[i], next_val = False, l[i]
12371248
s = self._colors[i]
1249+
add_neg = True
12381250
while next_val != cycle_first:
12391251
cycle.append(s * next_val)
12401252
s *= self._colors[next_val - 1]
12411253
l[next_val - 1], next_val = False, l[next_val - 1]
12421254
if s != 1:
12431255
cycle.extend([-e for e in cycle])
1256+
add_neg = False
12441257

12451258
# Add the cycle to the list of cycles
12461259
if singletons or len(cycle) > 1:
12471260
cycles.append(tuple(cycle))
1261+
if negative_cycles and add_neg:
1262+
cycles.append(tuple([-e for e in cycle]))
12481263

12491264
return cycles
12501265

1266+
def cycle_type(self):
1267+
r"""
1268+
Return a pair of partitions of ``len(self)`` corresponding to the
1269+
signed cycle type of ``self``.
1270+
1271+
A *cycle* is a tuple `C = (c_0, \ldots, c_{k-1})` with
1272+
`\pi(c_i) = c_{i+1}` for `0 \leq i < k` and `\pi(c_{k-1}) = c_0`.
1273+
If `C` is a cycle, `\overline{C} = (-c_0, \ldots, -c_{k-1})` is
1274+
also a cycle. A cycle is *negative*, if `C = \overline{C}` up
1275+
to cyclic reordering. In this case, `k` is necessarily even
1276+
and the length of `C` is `k/2`. A *positive cycle* is a pair
1277+
`C \overline{C}`, its length is `k`.
1278+
1279+
Let `\alpha` be the partition whose parts are the lengths of the
1280+
positive cycles and let `\beta` be the partition whose parts are
1281+
the lengths of the negative cycles. Then `(\alpha, \beta)` is
1282+
the cycle type of `\pi`.
1283+
1284+
EXAMPLES::
1285+
1286+
sage: G = SignedPermutations(7)
1287+
sage: pi = G([2, -1, 4, -6, -5, -3, 7])
1288+
sage: pi.cycle_type()
1289+
([3, 1], [2, 1])
1290+
1291+
sage: G = SignedPermutations(5)
1292+
sage: all(pi.cycle_type().size() == 5 for pi in G)
1293+
True
1294+
sage: set(pi.cycle_type() for pi in G) == set(PartitionTuples(2, 5))
1295+
True
1296+
"""
1297+
cycles = self.to_cycles(negative_cycles=False)
1298+
pos_cycles = []
1299+
neg_cycles = []
1300+
for C in cycles:
1301+
if (not len(C) % 2) and C[0] == -C[len(C)//2]:
1302+
neg_cycles.append(C)
1303+
else:
1304+
pos_cycles.append(C)
1305+
pos_type = [len(C) for C in pos_cycles]
1306+
pos_type.sort(reverse=True)
1307+
neg_type = [len(C) // 2 for C in neg_cycles]
1308+
neg_type.sort(reverse=True)
1309+
from sage.combinat.partition_tuple import PartitionTuples
1310+
PT = PartitionTuples(2, self.parent()._n)
1311+
return PT([pos_type, neg_type])
1312+
12511313
def order(self):
12521314
"""
12531315
Return the multiplicative order of the signed permutation.
@@ -1256,11 +1318,11 @@ def order(self):
12561318
12571319
sage: pi = SignedPermutations(7)([2,-1,4,-6,-5,-3,7])
12581320
sage: pi.to_cycles(singletons=False)
1259-
[(1, 2, -1, -2), (3, 4, -6), (5, -5)]
1321+
[(1, 2, -1, -2), (3, 4, -6), (-3, -4, 6), (5, -5)]
12601322
sage: pi.order()
12611323
12
12621324
"""
1263-
return lcm(len(c) for c in self.to_cycles(singletons=False))
1325+
return lcm(len(c) for c in self.to_cycles(singletons=False, negative_cycles=False))
12641326

12651327

12661328
class SignedPermutations(ColoredPermutations):
@@ -1318,7 +1380,6 @@ class SignedPermutations(ColoredPermutations):
13181380
13191381
- :wikipedia:`Hyperoctahedral_group`
13201382
"""
1321-
13221383
def __init__(self, n):
13231384
"""
13241385
Initialize ``self``.
@@ -1529,6 +1590,69 @@ def long_element(self, index_set=None):
15291590
return super(SignedPermutations, self).long_element()
15301591
return self.element_class(self, [-ZZ.one()] * self._n, self._P.one())
15311592

1593+
def conjugacy_class_representative(self, nu):
1594+
r"""
1595+
Return a permutation with (signed) cycle type ``nu``.
1596+
1597+
EXAMPLES::
1598+
1599+
sage: G = SignedPermutations(4)
1600+
sage: for nu in PartitionTuples(2, 4):
1601+
....: print(nu, G.conjugacy_class_representative(nu))
1602+
....: assert nu == G.conjugacy_class_representative(nu).cycle_type(), nu
1603+
([4], []) [2, 3, 4, 1]
1604+
([3, 1], []) [2, 3, 1, 4]
1605+
([2, 2], []) [2, 1, 4, 3]
1606+
([2, 1, 1], []) [2, 1, 3, 4]
1607+
([1, 1, 1, 1], []) [1, 2, 3, 4]
1608+
([3], [1]) [2, 3, 1, -4]
1609+
([2, 1], [1]) [2, 1, 3, -4]
1610+
([1, 1, 1], [1]) [1, 2, 3, -4]
1611+
([2], [2]) [2, 1, 4, -3]
1612+
([2], [1, 1]) [2, 1, -3, -4]
1613+
([1, 1], [2]) [1, 2, 4, -3]
1614+
([1, 1], [1, 1]) [1, 2, -3, -4]
1615+
([1], [3]) [1, 3, 4, -2]
1616+
([1], [2, 1]) [1, 3, -2, -4]
1617+
([1], [1, 1, 1]) [1, -2, -3, -4]
1618+
([], [4]) [2, 3, 4, -1]
1619+
([], [3, 1]) [2, 3, -1, -4]
1620+
([], [2, 2]) [2, -1, 4, -3]
1621+
([], [2, 1, 1]) [2, -1, -3, -4]
1622+
([], [1, 1, 1, 1]) [-1, -2, -3, -4]
1623+
1624+
TESTS::
1625+
1626+
sage: all(nu == SignedPermutations(n).conjugacy_class_representative(nu).cycle_type()
1627+
....: for n in range(1, 6) for nu in PartitionTuples(2, n))
1628+
True
1629+
"""
1630+
from sage.combinat.partition_tuple import PartitionTuple
1631+
nu = PartitionTuple(nu)
1632+
if nu.size() != self._n:
1633+
raise ValueError("the size of the partition pair (=%s) must equal"
1634+
" the rank (=%s)" % (nu.size(), self._n))
1635+
la, mu = nu
1636+
cyc = []
1637+
cnt = 0
1638+
1639+
for i in la:
1640+
cyc += [tuple(range(cnt+1, cnt+i+1))] + [tuple(range(-cnt-1, -cnt-i-1, -1))]
1641+
cnt += i
1642+
for i in mu:
1643+
cyc += [tuple(range(cnt+1, cnt+i+1)) + tuple(range(-cnt-1, -cnt-i-1, -1))]
1644+
cnt += i
1645+
1646+
p = [None] * self._n
1647+
for c in cyc:
1648+
for i in range(len(c)-1):
1649+
if c[i] > 0:
1650+
p[c[i]-1] = c[i+1]
1651+
if c[-1] > 0:
1652+
p[c[-1]-1] = c[0]
1653+
1654+
return self(p)
1655+
15321656
Element = SignedPermutation
15331657

15341658
# TODO: Make this a subgroup

src/sage/combinat/permutation.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7143,12 +7143,16 @@ def element_in_conjugacy_classes(self, nu):
71437143
sage: PP = Permutations(5)
71447144
sage: PP.element_in_conjugacy_classes([2,2]) # optional - sage.combinat
71457145
[2, 1, 4, 3, 5]
7146+
sage: PP.element_in_conjugacy_classes([5, 5])
7147+
Traceback (most recent call last):
7148+
...
7149+
ValueError: the size of the partition (=10) should be at most the size of the permutations (=5)
71467150
"""
71477151
from sage.combinat.partition import Partition
71487152
nu = Partition(nu)
71497153
if nu.size() > self.n:
7150-
raise ValueError("The size of the partition (=%s) should be lower or equal"
7151-
" to the size of the permutations (=%s)"%(nu.size,self.n))
7154+
raise ValueError("the size of the partition (=%s) should be at most"
7155+
" the size of the permutations (=%s)" % (nu.size(), self.n))
71527156
l = []
71537157
i = 0
71547158
for nui in nu:

0 commit comments

Comments
 (0)