From 0694992435ab2ce2291082c6876ed2a55fdee187 Mon Sep 17 00:00:00 2001 From: Vasco Schiavo <115561717+VascoSch92@users.noreply.github.com> Date: Sat, 1 Jun 2024 13:12:10 +0200 Subject: [PATCH] [FEATURE] Add method `records` (#82) --- CHANGELOG.md | 2 + .../pages/API_reference/elements/index.rst | 5 ++ symmetria/elements/cycle_decomposition.py | 25 ++++++ symmetria/elements/permutation.py | 77 +++++++++++++------ tests/tests_cycle_decomposition/test_cases.py | 5 ++ .../test_generic_methods.py | 15 ++++ tests/tests_permutation/test_cases.py | 5 ++ .../tests_permutation/test_generic_methods.py | 11 +++ 8 files changed, 122 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b754e64..b56464a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ FEATURE: - `symmetria.CyclePermutation`: add `descents` method - `symmetria.Permutation`: add `exceedances` method - `symmetria.CyclePermutation`: add `exceedances` method +- `symmetria.Permutation`: add `records` method +- `symmetria.CyclePermutation`: add `records` method MAINTENANCE: - `tests`: Refactor. Now the code is more concise, and it is easier to add a new test. diff --git a/docs/source/pages/API_reference/elements/index.rst b/docs/source/pages/API_reference/elements/index.rst index 0c33c65..0c6c61c 100644 --- a/docs/source/pages/API_reference/elements/index.rst +++ b/docs/source/pages/API_reference/elements/index.rst @@ -125,6 +125,11 @@ Here, **P** denotes the class ``Permutation``, **C** the class ``Cycle``, and ** - ✅ - ✅ - ✅ + * - ``records`` + - Return the positions of the permutation records + - ✅ + - ❌ + - ✅ * - ``sgn`` - Return the sign of the permutation - ✅ diff --git a/symmetria/elements/cycle_decomposition.py b/symmetria/elements/cycle_decomposition.py index 40d5f1d..f76f0ea 100644 --- a/symmetria/elements/cycle_decomposition.py +++ b/symmetria/elements/cycle_decomposition.py @@ -775,6 +775,31 @@ def order(self) -> int: """ return lcm(*[len(cycle) for cycle in self]) + def records(self) -> List[int]: + r"""Return the records of the cycle decomposition. + + Recall that a record of a permutation :math:`\sigma \in S_n`, where :math:`n \in \mathbb{N}`, is a position + :math:`i \in \{1, ..., n\}` such that is either :math:`i=1` or :math:`\sigma(j) < \sigma(i)` + for all :math:`j>> from symmetria import Cycle, CycleDecomposition + ... + >>> CycleDecomposition(Cycle(1)).records() + [1] + >>> CycleDecomposition(Cycle(1, 2), Cycle(3)).records() + [1, 3] + >>> CycleDecomposition(Cycle(1), Cycle(2, 4, 7, 6), Cycle(3, 5)).records() + [1, 2, 3, 4] + """ + return symmetria.Permutation.from_cycle_decomposition(self).records() + def sgn(self) -> int: r"""Return the sign of the cycle decomposition. diff --git a/symmetria/elements/permutation.py b/symmetria/elements/permutation.py index 3399eaa..dcce7fa 100644 --- a/symmetria/elements/permutation.py +++ b/symmetria/elements/permutation.py @@ -518,29 +518,29 @@ def equivalent(self, other: Any) -> bool: 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] + 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 + + :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] @@ -910,6 +910,37 @@ def order(self) -> int: """ return self.cycle_decomposition().order() + def records(self) -> List[int]: + r"""Return the records of the permutation. + + Recall that a record of a permutation :math:`\sigma \in S_n`, where :math:`n \in \mathbb{N}`, is a position + :math:`i \in \{1, ..., n\}` such that is either :math:`i=1` or :math:`\sigma(j) < \sigma(i)` + for all :math:`j>> from symmetria import Permutation + ... + >>> Permutation(1, 2, 3).records() + [1, 2, 3] + >>> Permutation(3, 1, 2).records() + [1] + >>> Permutation(1, 3, 4, 5, 2, 6).records() + [1, 2, 3, 4, 6] + """ + records = [1] + tmp_max = self[1] + for i in self.domain: + if self[i] > tmp_max: + records.append(i) + tmp_max = self[i] + return records + def sgn(self) -> int: r"""Return the sign of the permutation. diff --git a/tests/tests_cycle_decomposition/test_cases.py b/tests/tests_cycle_decomposition/test_cases.py index ae270c2..1ce17e3 100644 --- a/tests/tests_cycle_decomposition/test_cases.py +++ b/tests/tests_cycle_decomposition/test_cases.py @@ -150,6 +150,11 @@ (CycleDecomposition(Cycle(1), Cycle(2, 4, 7, 6), Cycle(3, 5)), 4), (CycleDecomposition(Cycle(1, 6, 2, 4, 7), Cycle(3, 5)), 10), ] +TEST_RECORDS = [ + (CycleDecomposition(Cycle(1)), [1]), + (CycleDecomposition(Cycle(1, 2), Cycle(3)), [1, 3]), + (CycleDecomposition(Cycle(1), Cycle(2, 4, 7, 6), Cycle(3, 5)), [1, 2, 3, 4]), +] TEST_SUPPORT = [ (CycleDecomposition(Cycle(1)), set()), (CycleDecomposition(Cycle(1), Cycle(2, 4, 7, 6), Cycle(3, 5)), {2, 4, 7, 6, 3, 5}), diff --git a/tests/tests_cycle_decomposition/test_generic_methods.py b/tests/tests_cycle_decomposition/test_generic_methods.py index 6c0cc86..c7de9e8 100644 --- a/tests/tests_cycle_decomposition/test_generic_methods.py +++ b/tests/tests_cycle_decomposition/test_generic_methods.py @@ -10,6 +10,7 @@ TEST_ASCENTS, TEST_INVERSE, TEST_IS_EVEN, + TEST_RECORDS, TEST_SUPPORT, TEST_DESCENTS, TEST_CYCLE_TYPE, @@ -268,6 +269,20 @@ def test_order(cycle_decomposition, expected_value) -> None: ) +@pytest.mark.parametrize( + argnames="cycle_decomposition, expected_value", + argvalues=TEST_RECORDS, + ids=[f"{p}.records()={o}" for p, o in TEST_RECORDS], +) +def test_records(cycle_decomposition, expected_value) -> None: + """Tests for the method `records()`.""" + _check_values( + expression=f"{cycle_decomposition.rep()}.records()", + evaluation=cycle_decomposition.records(), + expected=expected_value, + ) + + @pytest.mark.parametrize( argnames="cycle_decomposition, expected_value", argvalues=TEST_SGN, diff --git a/tests/tests_permutation/test_cases.py b/tests/tests_permutation/test_cases.py index 24627d7..b2ae273 100644 --- a/tests/tests_permutation/test_cases.py +++ b/tests/tests_permutation/test_cases.py @@ -164,6 +164,11 @@ (Permutation(1, 3, 2), {1: 1, 2: 3, 3: 2}), (Permutation(1, 4, 3, 2), {1: 1, 2: 4, 3: 3, 4: 2}), ] +TEST_RECORDS = [ + (Permutation(1, 2, 3), [1, 2, 3]), + (Permutation(3, 1, 2), [1]), + (Permutation(1, 3, 4, 5, 2, 6), [1, 2, 3, 4, 6]), +] TEST_SUPPORT = [ (Permutation(1), set()), (Permutation(2, 1), {1, 2}), diff --git a/tests/tests_permutation/test_generic_methods.py b/tests/tests_permutation/test_generic_methods.py index 970166d..ea06739 100644 --- a/tests/tests_permutation/test_generic_methods.py +++ b/tests/tests_permutation/test_generic_methods.py @@ -12,6 +12,7 @@ TEST_ASCENTS, TEST_INVERSE, TEST_IS_EVEN, + TEST_RECORDS, TEST_SUPPORT, TEST_DESCENTS, TEST_CYCLE_TYPE, @@ -241,6 +242,16 @@ def test_map(permutation, expected_value) -> None: _check_values(expression=f"{permutation.rep()}.map()", evaluation=permutation.map, expected=expected_value) +@pytest.mark.parametrize( + argnames="permutation, expected_value", + argvalues=TEST_RECORDS, + ids=[f"{p.rep()}.records()={s}" for p, s in TEST_RECORDS], +) +def test_records(permutation, expected_value) -> None: + """Tests for the method `records()`.""" + _check_values(expression=f"{permutation.rep()}.records()", evaluation=permutation.records(), expected=expected_value) + + @pytest.mark.parametrize( argnames="permutation, expected_value", argvalues=TEST_SUPPORT,