Skip to content

Commit c2063c7

Browse files
authored
[FEATURE] add lexicographic rank (#111)
1 parent c869034 commit c2063c7

File tree

6 files changed

+89
-0
lines changed

6 files changed

+89
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ The version is represented by three digits: a.b.c.
2222

2323
## Unreleased
2424

25+
FEATURE:
26+
- `symmetria.Permutation`: add `lexicographic_rank` method
27+
- `symmetria.CycleDecomposition`: add `lexicographic_rank` method
28+
2529
## \[0.3.0\] - 2024-07-15
2630

2731
FEATURE:

docs/source/pages/API_reference/elements/index.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ Here, **P** denotes the class ``Permutation``, **C** the class ``Cycle``, and **
115115
- ✅
116116
- ❌
117117
- ✅
118+
* - ``lexicographic_rank``
119+
- Return the lexicographic rank of the permutation
120+
- ✅
121+
- ❌
122+
- ✅
118123
* - ``map``
119124
- Return the map defining the permutation
120125
- ✅

symmetria/elements/cycle_decomposition.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,27 @@ def is_regular(self) -> bool:
764764
"""
765765
return all(len(cycle) == len(self[0]) for cycle in self)
766766

767+
def lexicographic_rank(self) -> int:
768+
"""Return the lexicographic rank of the cycle decomposition.
769+
770+
Recall that the lexicographic rank of a permutation refers to its position in the list of all
771+
permutations of the same degree sorted in lexicographic order.
772+
773+
:return: the lexocographic rank of the cycle decomposition.
774+
:rtype: int
775+
776+
:example:
777+
>>> from symmetria import Cycle, CycleDecomposition
778+
...
779+
>>> CycleDecomposition(Cycle(1)).lexicographic_rank()
780+
1
781+
>>> CycleDecomposition(Cycle(1, 3, 2)).lexicographic_rank()
782+
5
783+
>>> CycleDecomposition(Cycle(1, 3, 2), Cycle(4, 5)).lexicographic_rank()
784+
50
785+
"""
786+
return symmetria.elements.permutation.Permutation.from_cycle_decomposition(self).lexicographic_rank()
787+
767788
@property
768789
def map(self) -> Dict[int, int]:
769790
"""Return a dictionary representing the mapping of the cycle decomposition,

symmetria/elements/permutation.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from math import factorial
12
from typing import Any, Set, Dict, List, Tuple, Union, Iterable
23
from collections import OrderedDict
34

@@ -876,6 +877,39 @@ def is_regular(self) -> bool:
876877
cycle_decomposition = self.cycle_decomposition()
877878
return all(len(cycle) == len(cycle_decomposition[0]) for cycle in cycle_decomposition)
878879

880+
def lexicographic_rank(self) -> int:
881+
"""Return the lexicographic rank of the permutation.
882+
883+
Recall that the lexicographic rank of a permutation refers to its position in the list of all
884+
permutations of the same degree sorted in lexicographic order.
885+
886+
:return: the lexocographic rank of the permutation.
887+
:rtype: int
888+
889+
:example:
890+
>>> from symmetria import Permutation
891+
...
892+
>>> Permutation(1).lexicographic_rank()
893+
1
894+
>>> Permutation(1, 2, 3).lexicographic_rank()
895+
1
896+
>>> Permutation(1, 3, 2).lexicographic_rank()
897+
2
898+
>>> Permutation(3, 2, 1, 4).lexicographic_rank()
899+
15
900+
"""
901+
n = self.__len__()
902+
rank = 1
903+
904+
for i in range(n):
905+
right_smaller = 0
906+
for j in range(i + 1, n):
907+
if self[i + 1] > self[j + 1]:
908+
right_smaller += 1
909+
rank += right_smaller * factorial(n - i - 1)
910+
911+
return rank
912+
879913
@property
880914
def map(self) -> Dict[int, int]:
881915
"""Return a dictionary representing the mapping of the permutation.

tests/tests_elements/tests_permutation/test_cases.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,16 @@
225225
(Permutation(2, 1), True),
226226
(Permutation(2, 1, 3), False),
227227
]
228+
TEST_LEXICOGRAPHIC_RANK = [
229+
(Permutation(1), 1),
230+
(Permutation(1, 2), 1),
231+
(Permutation(2, 1), 2),
232+
(Permutation(1, 2, 3), 1),
233+
(Permutation(1, 3, 2), 2),
234+
(Permutation(3, 2, 1), 6),
235+
(Permutation(3, 2, 1, 4), 15),
236+
(Permutation(1, 2, 5, 4, 3), 6),
237+
]
228238
TEST_MAP = [
229239
(Permutation(1), {1: 1}),
230240
(Permutation(2, 1), {1: 2, 2: 1}),

tests/tests_elements/tests_permutation/test_generic_methods.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
TEST_IS_DERANGEMENT,
2828
TEST_ONE_LINE_NOTATION,
2929
TEST_IS_CONJUGATE_ERROR,
30+
TEST_LEXICOGRAPHIC_RANK,
3031
TEST_CYCLE_DECOMPOSITION,
3132
)
3233

@@ -256,6 +257,20 @@ def test_is_regular(permutation, expected_value) -> None:
256257
)
257258

258259

260+
@pytest.mark.parametrize(
261+
argnames="permutation, expected_value",
262+
argvalues=TEST_LEXICOGRAPHIC_RANK,
263+
ids=[f"{p}.lexicographic_rank()={m}" for p, m in TEST_LEXICOGRAPHIC_RANK],
264+
)
265+
def test_lexicographic_rank(permutation, expected_value) -> None:
266+
"""Tests for the method `lexicographic_rank()`."""
267+
_check_values(
268+
expression=f"{permutation.rep()}.lexicographic_rank()",
269+
evaluation=permutation.lexicographic_rank(),
270+
expected=expected_value,
271+
)
272+
273+
259274
@pytest.mark.parametrize(
260275
argnames="permutation, expected_value",
261276
argvalues=TEST_MAP,

0 commit comments

Comments
 (0)