Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
VascoSch92 committed May 31, 2024
1 parent 39a422b commit e91a236
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ FEATURE:
- `symmetria.CyclePermutation`: add `ascents` method
- `symmetria.Permutation`: add `descents` method
- `symmetria.CyclePermutation`: add `descents` method
- `symmetria.Permutation`: add `exceedances` method
- `symmetria.CyclePermutation`: add `exceedances` method


## \[0.0.4\] - 2024-05-28
Expand Down
5 changes: 5 additions & 0 deletions docs/source/pages/API_reference/elements/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ Here, **P** denotes the class ``Permutation``, **C** the class ``Cycle``, and **
- ✅
- ❌
- ✅
* - ``exceedances``
- Return the positions of the permutation exceedances
- ✅
- ❌
- ✅
* - ``inverse``
- Compute the inverse of the permutation
- ✅
Expand Down
11 changes: 3 additions & 8 deletions symmetria/elements/_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,6 @@ def __str__(self) -> str:
"""Implement method `str()`."""
raise NotImplementedError

# TODO: decide if we want to implement this method also for the classes Cycle and CycleDecomposition
# @staticmethod
# @abstractmethod
# def from_dict(permutation: Dict) -> "Element":
# raise NotImplementedError

@abstractmethod
def cycle_decomposition(self) -> "CycleDecomposition":
"""Return the cycle decomposition of the element."""
Expand Down Expand Up @@ -117,9 +111,10 @@ def map(self) -> Dict[int, int]:
"""Return a dictionary representing the map defined the element."""
raise NotImplementedError

def name(self) -> str:
@staticmethod
def name() -> str:
"""Shortcut for the class name. Used for the tests."""
return self.__class__.__name__
return __class__.__name__

@abstractmethod
def orbit(self, item: Any) -> List[Any]:
Expand Down
29 changes: 29 additions & 0 deletions symmetria/elements/cycle_decomposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,35 @@ def equivalent(self, other: Any) -> bool:
return symmetria.elements.permutation.Permutation.from_cycle_decomposition(self) == other
return False

def exceedances(self, weakly: bool = False) -> List[int]:
r"""Return the exceedances of the cycle decomposition.
Recall that an exceedance of a permutation :math:`\sigma \in S_n`, where :math:`n \in \mathbb{N}`, is any
position :math:`i \in \{ 1, ..., n\}` where :math:`\sigma(i) > i`. An exceedance is called weakly if
:math:`\sigma(i) \geq i`.
:param weakly: `True` to return the weakly exceedances of the cycle decomposition. Default `False`.
:type weakly: bool
:return: The exceedances of the cycle decomposition.
:rtype: List[int]
:example:
>>> from symmetria import Cycle, CycleDecomposition
...
>>> CycleDecomposition(Cycle(1, 2), Cycle(3)).exceedances()
[1]
>>> CycleDecomposition(Cycle(1, 2), Cycle(3)).exceedances(weakly=True)
[1, 3]
>>> CycleDecomposition(Cycle(2, 3), Cycle(4, 5, 1)).exceedances()
[1, 2, 4]
>>> CycleDecomposition(Cycle(1), Cycle(2), Cycle(3)).exceedances()
[]
>>> CycleDecomposition(Cycle(1), Cycle(2), Cycle(3)).exceedances(weakly=True)
[1, 2, 3]
"""
return symmetria.Permutation.from_cycle_decomposition(self).exceedances(weakly=weakly)

def inverse(self) -> "CycleDecomposition":
r"""Return the inverse of the cycle decomposition.
Expand Down
31 changes: 31 additions & 0 deletions symmetria/elements/permutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,37 @@ def equivalent(self, other: Any) -> bool:
return self == Permutation.from_cycle_decomposition(other)
return False

def exceedances(self, weakly: bool = False) -> List[int]:
r"""Return the exceedances of the permutation.
Recall that an exceedance of a permutation :math:`\sigma \in S_n`, where :math:`n \in \mathbb{N}`, is any
position :math:`i \in \{ 1, ..., n\}` where :math:`\sigma(i) > i`. An exceedance is called weakly if
:math:`\sigma(i) \geq i`.
:param weakly: `True` to return the weakly exceedances of the permutation. Default `False`.
:type weakly: bool
co
:return: The exceedances of the permutation.
:rtype: List[int]
:example:
>>> from symmetria import Permutation
...
>>> Permutation(1, 2, 3).exceedances()
[]
>>> Permutation(1, 2, 3).exceedances(weakly=True)
[1, 2, 3]
>>> Permutation(4, 3, 2, 1).exceedances()
[1, 2]
>>> Permutation(3, 4, 5, 2, 1, 6, 7).exceedances()
[1, 2, 3]
>>> Permutation(3, 4, 5, 2, 1, 6, 7).exceedances(weakly=True)
[1, 2, 3, 6, 7]
"""
if weakly:
return [i for i, p in enumerate(self.image, 1) if p >= i]
return [i for i, p in enumerate(self.image, 1) if p > i]

@classmethod
def from_cycle(cls, cycle: "Cycle") -> "Permutation":
"""Return a permutation from a cycle.
Expand Down
7 changes: 7 additions & 0 deletions tests/tests_cycle_decomposition/test_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@
(CycleDecomposition(Cycle(1), Cycle(2), Cycle(3)), []),
(CycleDecomposition(Cycle(2, 3), Cycle(4, 5, 1)), [1, 2, 4]),
]
TEST_EXCEEDANCES = [
(CycleDecomposition(Cycle(1, 2), Cycle(3)), False, [1]),
(CycleDecomposition(Cycle(1, 2), Cycle(3)), True, [1, 3]),
(CycleDecomposition(Cycle(2, 3), Cycle(4, 5, 1)), False, [1, 2, 4]),
(CycleDecomposition(Cycle(1), Cycle(2), Cycle(3)), False, []),
(CycleDecomposition(Cycle(1), Cycle(2), Cycle(3)), True, [1, 2, 3]),
]
TEST_INVERSE = [
(CycleDecomposition(Cycle(1, 2, 3)), CycleDecomposition(Cycle(3, 2, 1))),
(CycleDecomposition(Cycle(1, 2), Cycle(3, 4)), CycleDecomposition(Cycle(2, 1), Cycle(4, 3))),
Expand Down
12 changes: 12 additions & 0 deletions tests/tests_cycle_decomposition/test_generic_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
validate_equivalent,
validate_inversions,
validate_is_regular,
validate_exceedances,
validate_is_conjugate,
validate_cycle_notation,
validate_is_derangement,
Expand All @@ -35,6 +36,7 @@
TEST_EQUIVALENT,
TEST_INVERSIONS,
TEST_IS_REGULAR,
TEST_EXCEEDANCES,
TEST_IS_CONJUGATE,
TEST_CYCLE_NOTATION,
TEST_IS_DERANGEMENT,
Expand Down Expand Up @@ -93,6 +95,16 @@ def test_descents(cycle_decomposition, expected_value) -> None:
validate_descents(item=cycle_decomposition, expected_value=expected_value)


@pytest.mark.parametrize(
argnames="cycle_decomposition, weakly, expected_value",
argvalues=TEST_EXCEEDANCES,
ids=[f"{p.__repr__()}.exceedences(weakly={w})={i}" for p, w, i in TEST_EXCEEDANCES],
)
def test_exceedences(cycle_decomposition, weakly, expected_value) -> None:
"""Tests for the method `exceedences()`."""
validate_exceedances(item=cycle_decomposition, weakly=weakly, expected_value=expected_value)


@pytest.mark.parametrize(
argnames="cycle_decomposition, expected_value",
argvalues=TEST_INVERSE,
Expand Down
7 changes: 7 additions & 0 deletions tests/tests_permutation/test_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@
(Permutation(1, 2, 3), 123, False),
(Permutation(1, 3, 2, 4), "hello-world", False),
]
TEST_EXCEEDANCES = [
(Permutation(1, 2, 3), False, []),
(Permutation(1, 2, 3), True, [1, 2, 3]),
(Permutation(4, 3, 2, 1), False, [1, 2]),
(Permutation(3, 4, 5, 2, 1, 6, 7), False, [1, 2, 3]),
(Permutation(3, 4, 5, 2, 1, 6, 7), True, [1, 2, 3, 6, 7]),
]
TEST_IMAGE = [
(Permutation(1, 2, 3), (1, 2, 3)),
(Permutation(1, 2, 3), (1, 2, 3)),
Expand Down
12 changes: 12 additions & 0 deletions tests/tests_permutation/test_generic_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
validate_equivalent,
validate_inversions,
validate_is_regular,
validate_exceedances,
validate_is_conjugate,
validate_cycle_notation,
validate_is_derangement,
Expand All @@ -38,6 +39,7 @@
TEST_EQUIVALENT,
TEST_INVERSIONS,
TEST_IS_REGULAR,
TEST_EXCEEDANCES,
TEST_IS_CONJUGATE,
TEST_CYCLE_NOTATION,
TEST_IS_DERANGEMENT,
Expand Down Expand Up @@ -117,6 +119,16 @@ def test_equivalent(lhs, rhs, expected_value) -> None:
validate_equivalent(lhs=lhs, rhs=rhs, expected_value=expected_value)


@pytest.mark.parametrize(
argnames="permutation, weakly, expected_value",
argvalues=TEST_EXCEEDANCES,
ids=[f"{p}.exceedances(weakly={w})={i}" for p, w, i in TEST_EXCEEDANCES],
)
def test_exceedances(permutation, weakly, expected_value) -> None:
"""Tests the method `exceedances()`."""
validate_exceedances(item=permutation, weakly=weakly, expected_value=expected_value)


@pytest.mark.parametrize(
argnames="permutation, expected_value",
argvalues=TEST_IMAGE,
Expand Down

0 comments on commit e91a236

Please sign in to comment.