diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c80788..68efcf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,14 @@ The version is represented by three digits: a.b.c. ## Unreleased +FEATURE: +- `symmetria.Permutation`: add `degree` method +- `symmetria.CyclePermutation`: add `degree` method +- `symmetria.Cycle`: add `degree` method + +ENHANCEMENT: +- `symmetria.Permutation`: change how the sign is computed + ## \[0.2.0\] - 2024-06-12 diff --git a/docs/source/pages/API_reference/elements/index.rst b/docs/source/pages/API_reference/elements/index.rst index 92c97dc..87720ef 100644 --- a/docs/source/pages/API_reference/elements/index.rst +++ b/docs/source/pages/API_reference/elements/index.rst @@ -60,6 +60,11 @@ Here, **P** denotes the class ``Permutation``, **C** the class ``Cycle``, and ** - ✅ - ✅ - ✅ + * - ``degree`` + - Return the degree of the permutation + - ✅ + - ✅ + - ✅ * - ``descents`` - Return the positions of the permutation descents - ✅ diff --git a/symmetria/elements/cycle.py b/symmetria/elements/cycle.py index fb337e7..cfaef48 100644 --- a/symmetria/elements/cycle.py +++ b/symmetria/elements/cycle.py @@ -368,6 +368,26 @@ def cycle_notation(self) -> str: """ return str(self) + def degree(self) -> int: + """Return the degree of the cycle. + + Recall that the degree of a cycle is the number of elements on which it acts. + + :return: The degree of the cycle. + :rtype: int + + :example: + >>> from symmetria import Cycle + ... + >>> Cycle(1).degree() + 1 + >>> Cycle(1, 3, 2).degree() + 3 + >>> Cycle(1, 4, 3, 2).degree() + 4 + """ + return len(self) + def describe(self) -> str: """Return a table describing the cycle. diff --git a/symmetria/elements/cycle_decomposition.py b/symmetria/elements/cycle_decomposition.py index 427fefa..f547ec5 100644 --- a/symmetria/elements/cycle_decomposition.py +++ b/symmetria/elements/cycle_decomposition.py @@ -434,6 +434,26 @@ def cycle_type(self) -> Tuple[int]: """ return tuple(sorted(len(cycle) for cycle in self)) + def degree(self) -> int: + """Return the degree of the cycle decomposition. + + Recall that the degree of a cycle decomposition is the number of elements on which it acts. + + :return: The degree of the cycle decomposition. + :rtype: int + + :example: + >>> from symmetria import Cycle, CycleDecomposition + ... + >>> CycleDecomposition(Cycle(1)).degree() + 1 + >>> CycleDecomposition(Cycle(1), Cycle(3, 2)).degree() + 3 + >>> CycleDecomposition(Cycle(1, 4), Cycle(3, 2)).degree() + 4 + """ + return max(max(cycle.elements) for cycle in self._cycles) + def descents(self) -> List[int]: r"""Return the descents of the cycle decomposition. diff --git a/symmetria/elements/permutation.py b/symmetria/elements/permutation.py index f8cd6d5..0b9a1f2 100644 --- a/symmetria/elements/permutation.py +++ b/symmetria/elements/permutation.py @@ -444,6 +444,26 @@ def cycle_type(self) -> Tuple[int]: """ return tuple(sorted(len(cycle) for cycle in self.cycle_decomposition())) + def degree(self) -> int: + """Return the degree of the permutation. + + Recall that the degree of a permutation is the number of elements on which it acts. + + :return: The degree of the permutation. + :rtype: int + + :example: + >>> from symmetria import Permutation + ... + >>> Permutation(1).degree() + 1 + >>> Permutation(1, 3, 2).degree() + 3 + >>> Permutation(1, 4, 3, 2).degree() + 4 + """ + return len(self) + def descents(self) -> List[int]: r"""Return the descents of the permutation. @@ -1006,7 +1026,8 @@ def sgn(self) -> int: r"""Return the sign of the permutation. Recall that the sign, signature, or signum of a permutation :math:`\sigma` is defined as +1 if :math:`\sigma` - is even, and -1 if :math:`\sigma` is odd. + is even, i.e., :math:`\sigma` has an even number of inversions, and -1 if :math:`\sigma` is odd, i.e., + :math:`\sigma` has an odd number of inversions. :return: 1 if the permutation is even, -1 if the permutation is odd. :rtype: int @@ -1021,7 +1042,7 @@ def sgn(self) -> int: >>> Permutation(2, 3, 4, 5, 6, 1).sgn() -1 """ - return self.cycle_decomposition().sgn() + return -1 if len(self.inversions()) % 2 else 1 def support(self) -> Set[int]: r"""Return a set containing the indices in the domain of the permutation whose images are different from their diff --git a/tests/tests_elements/tests_cycle/test_cases.py b/tests/tests_elements/tests_cycle/test_cases.py index 7ae6b84..39e859e 100644 --- a/tests/tests_elements/tests_cycle/test_cases.py +++ b/tests/tests_elements/tests_cycle/test_cases.py @@ -22,6 +22,11 @@ (Cycle(1, 2), "(1 2)"), (Cycle(1, 2, 3), "(1 2 3)"), ] +TEST_DEGREE = [ + (Cycle(1), 1), + (Cycle(1, 3, 2), 3), + (Cycle(1, 4, 3, 2), 4), +] TEST_DESCRIBE = [ ( Cycle(1), diff --git a/tests/tests_elements/tests_cycle/test_generic_method.py b/tests/tests_elements/tests_cycle/test_generic_method.py index f5636ef..e49fff1 100644 --- a/tests/tests_elements/tests_cycle/test_generic_method.py +++ b/tests/tests_elements/tests_cycle/test_generic_method.py @@ -6,6 +6,7 @@ TEST_SGN, TEST_ORBIT, TEST_ORDER, + TEST_DEGREE, TEST_DOMAIN, TEST_IS_ODD, TEST_INVERSE, @@ -60,6 +61,16 @@ def test_equivalent(lhs, rhs, expected_value) -> None: ) +@pytest.mark.parametrize( + argnames="cycle, expected_value", + argvalues=TEST_DEGREE, + ids=[f"{c.rep()}.degree()={d}" for c, d in TEST_DEGREE], +) +def test_degree(cycle, expected_value) -> None: + """Tests for the method `degree()`.""" + _check_values(expression=f"{cycle.rep()}.degree()", evaluation=cycle.degree(), expected=expected_value) + + @pytest.mark.parametrize( argnames="cycle, expected_value", argvalues=TEST_DESCRIBE, diff --git a/tests/tests_elements/tests_cycle_decomposition/test_cases.py b/tests/tests_elements/tests_cycle_decomposition/test_cases.py index da3015d..0d3d154 100644 --- a/tests/tests_elements/tests_cycle_decomposition/test_cases.py +++ b/tests/tests_elements/tests_cycle_decomposition/test_cases.py @@ -47,6 +47,11 @@ (CycleDecomposition(Cycle(1, 3, 2), Cycle(4)), (1, 3)), (CycleDecomposition(Cycle(1, 2), Cycle(3, 4)), (2, 2)), ] +TEST_DEGREE = [ + (CycleDecomposition(Cycle(1)), 1), + (CycleDecomposition(Cycle(1), Cycle(2, 3)), 3), + (CycleDecomposition(Cycle(1), Cycle(2), Cycle(3)), 3), +] TEST_DESCENTS = [ (CycleDecomposition(Cycle(1, 2, 3)), [2]), (CycleDecomposition(Cycle(1), Cycle(2), Cycle(3)), []), diff --git a/tests/tests_elements/tests_cycle_decomposition/test_generic_methods.py b/tests/tests_elements/tests_cycle_decomposition/test_generic_methods.py index 6ceb71c..52ed280 100644 --- a/tests/tests_elements/tests_cycle_decomposition/test_generic_methods.py +++ b/tests/tests_elements/tests_cycle_decomposition/test_generic_methods.py @@ -6,6 +6,7 @@ TEST_SGN, TEST_ORBIT, TEST_ORDER, + TEST_DEGREE, TEST_IS_ODD, TEST_ASCENTS, TEST_INVERSE, @@ -97,6 +98,20 @@ def test_descents(cycle_decomposition, expected_value) -> None: ) +@pytest.mark.parametrize( + argnames="cycle_decomposition, expected_value", + argvalues=TEST_DEGREE, + ids=[f"{p.rep()}.degree()={d}" for p, d in TEST_DEGREE], +) +def test_degree(cycle_decomposition, expected_value) -> None: + """Tests for the method `degree()`.""" + _check_values( + expression=f"{cycle_decomposition.rep()}.degree()", + evaluation=cycle_decomposition.degree(), + expected=expected_value, + ) + + @pytest.mark.parametrize( argnames="cycle_decomposition, expected_value", argvalues=TEST_DESCRIBE, diff --git a/tests/tests_elements/tests_permutation/test_cases.py b/tests/tests_elements/tests_permutation/test_cases.py index 26f297a..a2cc5bc 100644 --- a/tests/tests_elements/tests_permutation/test_cases.py +++ b/tests/tests_elements/tests_permutation/test_cases.py @@ -75,6 +75,11 @@ (Permutation(3, 1, 2), "(1 3 2)"), (Permutation(3, 1, 2, 4, 5, 6), "(1 3 2)(4)(5)(6)"), ] +TEST_DEGREE = [ + (Permutation(1), 1), + (Permutation(1, 3, 2), 3), + (Permutation(1, 2, 3, 4, 5, 6, 7, 8, 9), 9), +] TEST_DESCENTS = [ (Permutation(1, 2, 3), []), (Permutation(3, 4, 5, 2, 1, 6, 7), [3, 4]), diff --git a/tests/tests_elements/tests_permutation/test_generic_methods.py b/tests/tests_elements/tests_permutation/test_generic_methods.py index 1df152f..008870c 100644 --- a/tests/tests_elements/tests_permutation/test_generic_methods.py +++ b/tests/tests_elements/tests_permutation/test_generic_methods.py @@ -7,6 +7,7 @@ TEST_IMAGE, TEST_ORBIT, TEST_ORDER, + TEST_DEGREE, TEST_DOMAIN, TEST_IS_ODD, TEST_ASCENTS, @@ -80,6 +81,16 @@ def test_cycle_notation(permutation, expected_value) -> None: ) +@pytest.mark.parametrize( + argnames="permutation, expected_value", + argvalues=TEST_DEGREE, + ids=[f"{p.rep()}.degree()={d}" for p, d in TEST_DEGREE], +) +def test_degree(permutation, expected_value) -> None: + """Tests for the method `degree()`.""" + _check_values(expression=f"{permutation.rep()}.degree()", evaluation=permutation.degree(), expected=expected_value) + + @pytest.mark.parametrize( argnames="permutation, expected_value", argvalues=TEST_DESCENTS,