Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
sagemathgh-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 sagemath#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 sagemath#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
- sagemath#12345: short description why this is a dependency
- sagemath#34567: ...
-->

<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
    
URL: sagemath#36224
Reported by: Travis Scrimshaw
Reviewer(s): ayyer, Martin Rubey, Travis Scrimshaw
  • Loading branch information
Release Manager committed Sep 13, 2023
2 parents 3c714e8 + e3397a7 commit 492fcde
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 12 deletions.
144 changes: 134 additions & 10 deletions src/sage/combinat/colored_permutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -1189,14 +1189,13 @@ def has_left_descent(self, i):
return self._colors[i] == 1 or self._perm[i - 1] < self._perm[i]
return self._colors[i] == 1 and self._perm[i - 1] > self._perm[i]

def to_cycles(self, singletons=True, use_min=True, negative_singletons=True):
"""
def to_cycles(self, singletons=True, use_min=True, negative_cycles=True):
r"""
Return the signed permutation ``self`` as a list of disjoint cycles.
The cycles are returned in the order of increasing smallest
elements, and each cycle is returned as a tuple which starts
with its smallest positive element. We do not include the
corresponding negative cycles.
with its smallest positive element.
INPUT:
Expand All @@ -1205,18 +1204,30 @@ def to_cycles(self, singletons=True, use_min=True, negative_singletons=True):
- ``use_min`` -- (default: ``True``) if ``False``, the cycles are
returned in the order of increasing *largest* (not smallest)
elements, and each cycle starts with its largest element
- ``negative_cycles`` -- (default: ``True``) if ``False``, for any
two cycles `C^{\pm} = \{\pm c_1, \ldots, \pm c_k\}` such that
`C^+ \neq C^-`, this does not include the cycle `C^-`
.. WARNING::
The arugment ``negative_cycles`` does not refer to the usual
definition of a negative cycle; see :meth:`cycle_type`.
EXAMPLES::
sage: pi = SignedPermutations(7)([2,-1,4,-6,-5,-3,7])
sage: pi.to_cycles()
[(1, 2, -1, -2), (3, 4, -6), (5, -5), (7,)]
[(1, 2, -1, -2), (3, 4, -6), (-3, -4, 6), (5, -5), (7,), (-7,)]
sage: pi.to_cycles(singletons=False)
[(1, 2, -1, -2), (3, 4, -6), (-3, -4, 6), (5, -5)]
sage: pi.to_cycles(negative_cycles=False)
[(1, 2, -1, -2), (3, 4, -6), (5, -5), (7,)]
sage: pi.to_cycles(singletons=False, negative_cycles=False)
[(1, 2, -1, -2), (3, 4, -6), (5, -5)]
sage: pi.to_cycles(use_min=False)
[(7,), (6, -3, -4), (5, -5), (2, -1, -2, 1)]
[(7,), (-7,), (6, -3, -4), (-6, 3, 4), (5, -5), (2, -1, -2, 1)]
sage: pi.to_cycles(singletons=False, use_min=False)
[(6, -3, -4), (5, -5), (2, -1, -2, 1)]
[(6, -3, -4), (-6, 3, 4), (5, -5), (2, -1, -2, 1)]
"""
cycles = []

Expand All @@ -1235,19 +1246,70 @@ def to_cycles(self, singletons=True, use_min=True, negative_singletons=True):
cycle = [cycle_first]
l[i], next_val = False, l[i]
s = self._colors[i]
add_neg = True
while next_val != cycle_first:
cycle.append(s * next_val)
s *= self._colors[next_val - 1]
l[next_val - 1], next_val = False, l[next_val - 1]
if s != 1:
cycle.extend([-e for e in cycle])
add_neg = False

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

return cycles

def cycle_type(self):
r"""
Return a pair of partitions of ``len(self)`` corresponding to the
signed cycle type of ``self``.
A *cycle* is a tuple `C = (c_0, \ldots, c_{k-1})` with
`\pi(c_i) = c_{i+1}` for `0 \leq i < k` and `\pi(c_{k-1}) = c_0`.
If `C` is a cycle, `\overline{C} = (-c_0, \ldots, -c_{k-1})` is
also a cycle. A cycle is *negative*, if `C = \overline{C}` up
to cyclic reordering. In this case, `k` is necessarily even
and the length of `C` is `k/2`. A *positive cycle* is a pair
`C \overline{C}`, its length is `k`.
Let `\alpha` be the partition whose parts are the lengths of the
positive cycles and let `\beta` be the partition whose parts are
the lengths of the negative cycles. Then `(\alpha, \beta)` is
the cycle type of `\pi`.
EXAMPLES::
sage: G = SignedPermutations(7)
sage: pi = G([2, -1, 4, -6, -5, -3, 7])
sage: pi.cycle_type()
([3, 1], [2, 1])
sage: G = SignedPermutations(5)
sage: all(pi.cycle_type().size() == 5 for pi in G)
True
sage: set(pi.cycle_type() for pi in G) == set(PartitionTuples(2, 5))
True
"""
cycles = self.to_cycles(negative_cycles=False)
pos_cycles = []
neg_cycles = []
for C in cycles:
if (not len(C) % 2) and C[0] == -C[len(C)//2]:
neg_cycles.append(C)
else:
pos_cycles.append(C)
pos_type = [len(C) for C in pos_cycles]
pos_type.sort(reverse=True)
neg_type = [len(C) // 2 for C in neg_cycles]
neg_type.sort(reverse=True)
from sage.combinat.partition_tuple import PartitionTuples
PT = PartitionTuples(2, self.parent()._n)
return PT([pos_type, neg_type])

def order(self):
"""
Return the multiplicative order of the signed permutation.
Expand All @@ -1256,11 +1318,11 @@ def order(self):
sage: pi = SignedPermutations(7)([2,-1,4,-6,-5,-3,7])
sage: pi.to_cycles(singletons=False)
[(1, 2, -1, -2), (3, 4, -6), (5, -5)]
[(1, 2, -1, -2), (3, 4, -6), (-3, -4, 6), (5, -5)]
sage: pi.order()
12
"""
return lcm(len(c) for c in self.to_cycles(singletons=False))
return lcm(len(c) for c in self.to_cycles(singletons=False, negative_cycles=False))


class SignedPermutations(ColoredPermutations):
Expand Down Expand Up @@ -1318,7 +1380,6 @@ class SignedPermutations(ColoredPermutations):
- :wikipedia:`Hyperoctahedral_group`
"""

def __init__(self, n):
"""
Initialize ``self``.
Expand Down Expand Up @@ -1529,6 +1590,69 @@ def long_element(self, index_set=None):
return super(SignedPermutations, self).long_element()
return self.element_class(self, [-ZZ.one()] * self._n, self._P.one())

def conjugacy_class_representative(self, nu):
r"""
Return a permutation with (signed) cycle type ``nu``.
EXAMPLES::
sage: G = SignedPermutations(4)
sage: for nu in PartitionTuples(2, 4):
....: print(nu, G.conjugacy_class_representative(nu))
....: assert nu == G.conjugacy_class_representative(nu).cycle_type(), nu
([4], []) [2, 3, 4, 1]
([3, 1], []) [2, 3, 1, 4]
([2, 2], []) [2, 1, 4, 3]
([2, 1, 1], []) [2, 1, 3, 4]
([1, 1, 1, 1], []) [1, 2, 3, 4]
([3], [1]) [2, 3, 1, -4]
([2, 1], [1]) [2, 1, 3, -4]
([1, 1, 1], [1]) [1, 2, 3, -4]
([2], [2]) [2, 1, 4, -3]
([2], [1, 1]) [2, 1, -3, -4]
([1, 1], [2]) [1, 2, 4, -3]
([1, 1], [1, 1]) [1, 2, -3, -4]
([1], [3]) [1, 3, 4, -2]
([1], [2, 1]) [1, 3, -2, -4]
([1], [1, 1, 1]) [1, -2, -3, -4]
([], [4]) [2, 3, 4, -1]
([], [3, 1]) [2, 3, -1, -4]
([], [2, 2]) [2, -1, 4, -3]
([], [2, 1, 1]) [2, -1, -3, -4]
([], [1, 1, 1, 1]) [-1, -2, -3, -4]
TESTS::
sage: all(nu == SignedPermutations(n).conjugacy_class_representative(nu).cycle_type()
....: for n in range(1, 6) for nu in PartitionTuples(2, n))
True
"""
from sage.combinat.partition_tuple import PartitionTuple
nu = PartitionTuple(nu)
if nu.size() != self._n:
raise ValueError("the size of the partition pair (=%s) must equal"
" the rank (=%s)" % (nu.size(), self._n))
la, mu = nu
cyc = []
cnt = 0

for i in la:
cyc += [tuple(range(cnt+1, cnt+i+1))] + [tuple(range(-cnt-1, -cnt-i-1, -1))]
cnt += i
for i in mu:
cyc += [tuple(range(cnt+1, cnt+i+1)) + tuple(range(-cnt-1, -cnt-i-1, -1))]
cnt += i

p = [None] * self._n
for c in cyc:
for i in range(len(c)-1):
if c[i] > 0:
p[c[i]-1] = c[i+1]
if c[-1] > 0:
p[c[-1]-1] = c[0]

return self(p)

Element = SignedPermutation

# TODO: Make this a subgroup
Expand Down
8 changes: 6 additions & 2 deletions src/sage/combinat/permutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7143,12 +7143,16 @@ def element_in_conjugacy_classes(self, nu):
sage: PP = Permutations(5)
sage: PP.element_in_conjugacy_classes([2,2]) # optional - sage.combinat
[2, 1, 4, 3, 5]
sage: PP.element_in_conjugacy_classes([5, 5])
Traceback (most recent call last):
...
ValueError: the size of the partition (=10) should be at most the size of the permutations (=5)
"""
from sage.combinat.partition import Partition
nu = Partition(nu)
if nu.size() > self.n:
raise ValueError("The size of the partition (=%s) should be lower or equal"
" to the size of the permutations (=%s)"%(nu.size,self.n))
raise ValueError("the size of the partition (=%s) should be at most"
" the size of the permutations (=%s)" % (nu.size(), self.n))
l = []
i = 0
for nui in nu:
Expand Down

0 comments on commit 492fcde

Please sign in to comment.