From 9f4c0e3eb8338ec700504460d4e09c21720b3d84 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Wed, 22 May 2024 10:49:40 +0200 Subject: [PATCH 01/11] Steinhaus-Johnson-Trotter algorithm for permutations --- src/sage/combinat/SJT.py | 137 +++++++++++++++++++++++++++++++ src/sage/combinat/permutation.py | 57 ++++++++++++- 2 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 src/sage/combinat/SJT.py diff --git a/src/sage/combinat/SJT.py b/src/sage/combinat/SJT.py new file mode 100644 index 00000000000..903b9274af4 --- /dev/null +++ b/src/sage/combinat/SJT.py @@ -0,0 +1,137 @@ +r""" +Steinhaus-Johnson-Trotter algorithm. + +The Steinhaus-Johnson-Trotter algorithm generates permutations in a specific +order by transposing only two elements from the list at each operation. The +algorithm stores an internal state for every element in the permutated list +which corresponds to their direction. To know which elements to move, the +internal state table is accessed and the transposition of two elements is then +done. + +It is important to notice that the permutations are generated in a different +order than the default lexicographic algorithm. + +The class defined here is meant to be used in the ``Permutation`` class when +called with the parameter ``algorithm='sjt'``. + +AUTHORS: + +- Martin Grenouilloux (2024-05-22): initial version +""" + +# **************************************************************************** +# Copyright (C) 2024 Martin Grenouilloux +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# ****************************************************************************s +class SJT: + def __init__(self, l, directions=None) -> None: + r""" + Transpose two elements at positions ``a`` and ``b`` in ``perm`` and + their corresponding directions as well following the + Steinhaus-Johnson-Trotter algorithm. + + INPUT: + + - ``l`` -- list: a list of ``int`` variables. + + - ``directions`` -- list: a list of directions for each element in the + permuted list. There are three possible values: + - negative -> element tranposes to the left + - positive -> element transposes to the right + - null -> element does not move + Used when constructing permutations from a pre-defined intern state. + """ + # The permuted list. + self.__perm = l + + # The length of the permuted list. + self.__n = len(l) + + if directions is None: + if not all(l[i] <= l[i+1] for i in range(len(l) - 1)): + raise ValueError("No internal state directions were given for " + "non-identity starting permutation for " + "Steinhaus-Johnson-Trotter algorithm. Expected identity " + "permutation.") + self.__directions = [-1] * self.__n + + # The first element has null direction. + self.__directions[0] = 0 + else: + self.__directions = directions + + def __idx_largest_element_non_zero_direction(self, perm, directions): + r""" + Find the largest element in ``perm`` with a non null direction. + """ + largest = 0 + index = None + for i in range(self.__n): + if directions[i] != 0: + e = perm[i] + if e > largest: + index = i + largest = e + + return index + + def next(self): + r""" + Produce the next permutation following the Steinhaus-Johnson-Trotter + algorithm. + + OUTPUT: the list of the next permutation. + """ + # Copying lists of permutation and directions to avoid changing internal + # state of the algorithm if ``next()`` is called without reassigning. + perm = self.__perm[:] + directions = self.__directions[:] + + # Assume that the element to move is n (which will be in most cases). + selected_elt = self.__n + xi = perm.index(selected_elt) + direction = directions[xi] + + # If this element has null direction, find the largest whose is + # non-null. + if direction == 0: + xi = self.__idx_largest_element_non_zero_direction(perm, directions) + if xi is None: + # We have created every permutation. Detected when all elements + # have null direction. + return False, None + direction = directions[xi] + selected_elt = perm[xi] + + new_pos = xi + direction + + # Proceed to transpose elements and corresponding directions. + perm[xi], perm[new_pos] = perm[new_pos], perm[xi] + directions[xi], directions[new_pos] = \ + directions[new_pos], directions[xi] + + # If the transposition results in the largest element being on one edge + # or if the following element in its direction is greater than it, then + # then set its direction to 0 + if new_pos == 0 or new_pos == self.__n - 1 or \ + perm[new_pos + direction] > selected_elt: + directions[new_pos] = 0 + + # After each permutation, update each element's direction. If one + # element is greater than selected element, change its direction towards + # the selected element. This loops has no reason to be if selected + # element is n and this will be the case most of the time. + if selected_elt != self.__n: + for i in range(self.__n): + if perm[i] > selected_elt: + if i < new_pos: + directions[i] = 1 + if i > new_pos: + directions[i] = -1 + + return perm, directions diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index b4e93a80b60..16821d5cbc9 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -250,6 +250,7 @@ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.sets_with_grading import SetsWithGrading from sage.combinat.backtrack import GenericBacktracker +from sage.combinat.SJT import SJT from sage.combinat.combinat import CombinatorialElement, catalan_number from sage.combinat.combinatorial_map import combinatorial_map from sage.combinat.composition import Composition @@ -378,6 +379,30 @@ class Permutation(CombinatorialElement): sage: type(p) + Construction using the Steinhaus-Johnson Trotter algorithm to generate + the permutations (lexicographic algorithm by default):: + + sage: p = Permutation([1, 2, 3, 4], algorithm='sjt'); p + [1, 2, 3, 4] + sage: p = p.next(); p + [1, 2, 4, 3] + sage: p = p.next(); p + [1, 4, 2, 3] + sage: p = Permutation([1, 2, 3], algorithm='sjt') + sage: for _ in range(6): + ....: p = p.next() + ....: + sage: p + False + + sage: Permutation([1, 2, 3, 4], algorithm='blah') + ValueError: Unsupported algorithm 'blah'; expected 'lex' or 'sjt' + + sage: Permutation([1, 3, 2, 4], algorithm='sjt') + ValueError: No internal state directions were given for non-identity \ + starting permutation for Steinhaus-Johnson-Trotter algorithm. Expected \ + identity permutation. + Construction from a string in cycle notation:: sage: p = Permutation( '(4,5)' ); p @@ -436,7 +461,7 @@ class Permutation(CombinatorialElement): """ @staticmethod @rename_keyword(deprecation=35233, check_input='check') - def __classcall_private__(cls, l, check=True): + def __classcall_private__(cls, l, algorithm='lex', directions=None, check=True): """ Return a permutation in the general permutations parent. @@ -486,11 +511,14 @@ def __classcall_private__(cls, l, check=True): else: raise ValueError("cannot convert l (= %s) to a Permutation" % l) + if algorithm != "lex" and algorithm != "sjt": + raise ValueError("Unsupported algorithm %s; expected 'lex' or 'sjt'" + % algorithm) # otherwise, it gets processed by CombinatorialElement's __init__. - return Permutations()(l, check=check) + return Permutations()(l, algorithm=algorithm, directions=directions, check=check) @rename_keyword(deprecation=35233, check_input='check') - def __init__(self, parent, l, check=True): + def __init__(self, parent, l, algorithm='lex', directions=None, check=True): """ Constructor. Checks that INPUT is not a mess, and calls :class:`CombinatorialElement`. It should not, because @@ -506,6 +534,13 @@ def __init__(self, parent, l, check=True): This is set to ``True`` by default. + - ``algorithm`` (string) -- the algorithm used to generate the + permutations. Choice between ``lex`` (lexicographic order) or ``sjt`` + (Steinhaus-Johnson-Trotter). ``sjt`` requires ``check`` to be set to + ``True``. + + This is set to ``lex`` by default. + TESTS:: sage: Permutation([1,2,3]) @@ -559,6 +594,12 @@ def __init__(self, parent, l, check=True): raise ValueError("an element appears twice in the input") previous = i + if algorithm != "lex" and algorithm != "sjt": + raise ValueError("unsupported algorithm %s; expected 'lex' or" + "'sjt'." % algorithm) + + self.__sjt = SJT(l, directions=directions) if algorithm == "sjt" else None + CombinatorialElement.__init__(self, parent, l) def __setstate__(self, state): @@ -765,6 +806,14 @@ def __next__(self): sage: next(p) False """ + if self.__sjt is not None: + # Ensure the same permutations is yielded when called multiple times + # without reassigning + p, directions = self.__sjt.next() + if p is False: + return False + return Permutations()(p, algorithm='sjt', directions=directions) + p = self[:] n = len(self) first = -1 @@ -824,6 +873,8 @@ def prev(self): sage: Permutation([1,4,3,2]).prev() [1, 4, 2, 3] """ + if self.__sjt is not None: + raise NotImplementedError p = self[:] n = len(self) From 86b7afb0c399168b8dd18da9062a67478245c2f1 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Thu, 23 May 2024 13:32:26 +0200 Subject: [PATCH 02/11] Fixing doctests --- src/sage/combinat/permutation.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 16821d5cbc9..e45c4ea6a77 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -396,11 +396,15 @@ class Permutation(CombinatorialElement): False sage: Permutation([1, 2, 3, 4], algorithm='blah') - ValueError: Unsupported algorithm 'blah'; expected 'lex' or 'sjt' + Traceback (most recent call last): + ... + ValueError: Unsupported algorithm blah; expected 'lex' or 'sjt' sage: Permutation([1, 3, 2, 4], algorithm='sjt') - ValueError: No internal state directions were given for non-identity \ - starting permutation for Steinhaus-Johnson-Trotter algorithm. Expected \ + Traceback (most recent call last): + ... + ValueError: No internal state directions were given for non-identity + starting permutation for Steinhaus-Johnson-Trotter algorithm. Expected identity permutation. Construction from a string in cycle notation:: From 3e369514ce598065b4798913565b134325b4f9fe Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Thu, 23 May 2024 14:55:13 +0200 Subject: [PATCH 03/11] Fixing edge case on empty list initialization --- src/sage/combinat/permutation.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index e45c4ea6a77..a09e5586b26 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -565,6 +565,8 @@ def __init__(self, parent, l, algorithm='lex', directions=None, check=True): is something wrong with its length. """ l = list(l) + # Default initialisation to ``None`` to declare the variable. + self.__sjt = None if check and len(l) > 0: # Make a copy to sort later @@ -602,7 +604,8 @@ def __init__(self, parent, l, algorithm='lex', directions=None, check=True): raise ValueError("unsupported algorithm %s; expected 'lex' or" "'sjt'." % algorithm) - self.__sjt = SJT(l, directions=directions) if algorithm == "sjt" else None + if algorithm == "sjt": + self.__sjt = SJT(l, directions=directions) CombinatorialElement.__init__(self, parent, l) @@ -793,7 +796,10 @@ def __next__(self): r""" Return the permutation that follows ``self`` in lexicographic order on the symmetric group containing ``self``. If ``self`` is the last - permutation, then ``next`` returns ``False``. + permutation, then ``next`` returns ``False``. If the algorithm parameter + is specified, the permutations will be generated according to it. + Allowed algorithms: lexicographic or "lex" and Steinhaus-Johnson-Trotter + or "sjt". EXAMPLES:: @@ -872,13 +878,20 @@ def prev(self): sage: p.prev() False + sage: p = Permutation([1,2,3], algorithm='sjt') + sage: p.prev() + Traceback (most recent call last): + ... + NotImplementedError: Previous permutation for SJT algorithm is not + yet implemented + Check that :issue:`16913` is fixed:: sage: Permutation([1,4,3,2]).prev() [1, 4, 2, 3] """ if self.__sjt is not None: - raise NotImplementedError + raise NotImplementedError("Previous permutation for SJT algorithm is not yet implemented") p = self[:] n = len(self) From 356704e3b6ce2b28174ae6bb33b057bd6ec34c38 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Thu, 23 May 2024 15:17:06 +0200 Subject: [PATCH 04/11] Removing typo in copyright --- src/sage/combinat/SJT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/SJT.py b/src/sage/combinat/SJT.py index 903b9274af4..d4f715d8587 100644 --- a/src/sage/combinat/SJT.py +++ b/src/sage/combinat/SJT.py @@ -27,7 +27,7 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # https://www.gnu.org/licenses/ -# ****************************************************************************s +# **************************************************************************** class SJT: def __init__(self, l, directions=None) -> None: r""" From 374799b17f47409677520c5f92c0a1e58ddc99ed Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Fri, 24 May 2024 15:08:05 +0200 Subject: [PATCH 05/11] Better testing and better documentation --- src/sage/combinat/SJT.py | 20 ++++--- src/sage/combinat/permutation.py | 99 +++++++++++++++++++++++--------- 2 files changed, 85 insertions(+), 34 deletions(-) diff --git a/src/sage/combinat/SJT.py b/src/sage/combinat/SJT.py index d4f715d8587..ed42d3f7fd3 100644 --- a/src/sage/combinat/SJT.py +++ b/src/sage/combinat/SJT.py @@ -37,14 +37,18 @@ def __init__(self, l, directions=None) -> None: INPUT: - - ``l`` -- list: a list of ``int`` variables. - - - ``directions`` -- list: a list of directions for each element in the - permuted list. There are three possible values: - - negative -> element tranposes to the left - - positive -> element transposes to the right - - null -> element does not move - Used when constructing permutations from a pre-defined intern state. + - ``l`` -- list: a list of ordered ``int``. + + - ``directions`` -- list (default: ``None`` ): a list of directions for + each element in the permuted list. Used when constructing permutations + from a pre-defined internal state. There are three possible values: + + - ``-1`` -> element tranposes to the left + + - ``1`` -> element transposes to the right + + - ``0`` -> element does not move + """ # The permuted list. self.__perm = l diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index a09e5586b26..bc52bb8ce2d 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -309,9 +309,23 @@ class Permutation(CombinatorialElement): the permutation obtained from the pair using the inverse of the Robinson-Schensted algorithm. - - ``check`` (boolean) -- whether to check that input is correct. Slows - the function down, but ensures that nothing bad happens. This is set to - ``True`` by default. + - ``check`` boolean (default: ``True``) -- whether to check that input is + correct. Slows the function down, but ensures that nothing bad happens. This + is set to ``True`` by default. + + - ``algorithm`` string (default: ``lex``) -- the algorithm used to generate + the permutations. Supported algorithms are: + + - ``lex``: lexicographic order generation, this is the default algorithm. + + - ``sjt``: Steinhaus-Johnson-Trotter algorithm to generate permutations + using only transposition of two elements in the list. It is highly + recommended to set ``check=True`` (default value). + + - ``directions`` list (default: ``None``) -- the list of directions to be + used with ``sjt`` algorithm when initializing the ``Permutation`` object + with non-identity permutation. Mainly used to handle internal states, best + to leave it by default. .. WARNING:: @@ -395,11 +409,6 @@ class Permutation(CombinatorialElement): sage: p False - sage: Permutation([1, 2, 3, 4], algorithm='blah') - Traceback (most recent call last): - ... - ValueError: Unsupported algorithm blah; expected 'lex' or 'sjt' - sage: Permutation([1, 3, 2, 4], algorithm='sjt') Traceback (most recent call last): ... @@ -456,6 +465,11 @@ class Permutation(CombinatorialElement): sage: Permutation( [1] ) [1] + sage: Permutation([1, 2, 3, 4], algorithm='blah') + Traceback (most recent call last): + ... + ValueError: Unsupported algorithm blah; expected 'lex' or 'sjt' + From a pair of empty tableaux :: sage: Permutation( ([], []) ) # needs sage.combinat @@ -532,18 +546,22 @@ def __init__(self, parent, l, algorithm='lex', directions=None, check=True): - ``l`` -- a list of ``int`` variables - - ``check`` (boolean) -- whether to check that input is - correct. Slows the function down, but ensures that nothing bad + - ``check`` boolean (default: ``True``) -- whether to check that input + is correct. Slows the function down, but ensures that nothing bad happens. - This is set to ``True`` by default. + - ``algorithm`` string (default: ``lex``) -- the algorithm used to + generate the permutations. Supported algorithms are: - - ``algorithm`` (string) -- the algorithm used to generate the - permutations. Choice between ``lex`` (lexicographic order) or ``sjt`` - (Steinhaus-Johnson-Trotter). ``sjt`` requires ``check`` to be set to - ``True``. + - ``lex``: lexicographic order generation, this is the default + algorithm. + - ``sjt``: Steinhaus-Johnson-Trotter algorithm to generate + permutations using only transposition of two elements in the list. It + is highly recommended to set ``check=True`` (default value). - This is set to ``lex`` by default. + - ``directions`` list (default: ``None``) -- the list of directions to + be used with ``sjt`` algorithm when initializing the ``Permutation`` + object with non-identity permutation. TESTS:: @@ -563,11 +581,29 @@ def __init__(self, parent, l, algorithm='lex', directions=None, check=True): ValueError: The permutation has length 4 but its maximal element is 5. Some element may be repeated, or an element is missing, but there is something wrong with its length. + + sage: Permutation([1, 3, 2], algorithm='sjt') + Traceback (most recent call last): + ... + ValueError: No internal state directions were given for non-identity + starting permutation for Steinhaus-Johnson-Trotter algorithm. + Expected identity permutation. + + sage: Permutation([1, 3, 2], algorithm='sjt', check=False) + Traceback (most recent call last): + ... + ValueError: No internal state directions were given for non-identity + starting permutation for Steinhaus-Johnson-Trotter algorithm. + Expected identity permutation. """ l = list(l) # Default initialisation to ``None`` to declare the variable. self.__sjt = None + if algorithm != "lex" and algorithm != "sjt": + raise ValueError("unsupported algorithm %s; expected 'lex' or 'sjt'" + "." % algorithm) + if check and len(l) > 0: # Make a copy to sort later lst = list(l) @@ -600,12 +636,8 @@ def __init__(self, parent, l, algorithm='lex', directions=None, check=True): raise ValueError("an element appears twice in the input") previous = i - if algorithm != "lex" and algorithm != "sjt": - raise ValueError("unsupported algorithm %s; expected 'lex' or" - "'sjt'." % algorithm) - - if algorithm == "sjt": - self.__sjt = SJT(l, directions=directions) + if algorithm == "sjt": + self.__sjt = SJT(l, directions=directions) CombinatorialElement.__init__(self, parent, l) @@ -796,10 +828,16 @@ def __next__(self): r""" Return the permutation that follows ``self`` in lexicographic order on the symmetric group containing ``self``. If ``self`` is the last - permutation, then ``next`` returns ``False``. If the algorithm parameter - is specified, the permutations will be generated according to it. - Allowed algorithms: lexicographic or "lex" and Steinhaus-Johnson-Trotter - or "sjt". + permutation, then ``next`` returns ``False``. If the ``algorithm`` + parameter is specified, the permutations will be generated according to + it. Supported algorithms are: + + - ``lex``: lexicographic order generation, this is the default + algorithm. + + - ``sjt``: Steinhaus-Johnson-Trotter algorithm to generate + permutations using only transposition of two elements in the list. + It is highly recommended to set ``check=True`` (default value). EXAMPLES:: @@ -809,6 +847,9 @@ def __next__(self): sage: p = Permutation([4,3,2,1]) sage: next(p) False + sage: p = Permutation([1, 2, 3], algorithm='sjt') + sage: next(p) + [1, 3, 2] TESTS:: @@ -862,6 +903,7 @@ def prev(self): Return the permutation that comes directly before ``self`` in lexicographic order on the symmetric group containing ``self``. If ``self`` is the first permutation, then it returns ``False``. + Does not support the Steinhaus-Johnson-Trotter algorithm for the moment. EXAMPLES:: @@ -889,6 +931,11 @@ def prev(self): sage: Permutation([1,4,3,2]).prev() [1, 4, 2, 3] + + .. TODO:: + + Implement the previous permutation for the Steinhaus-Johnson-Trotter + algorithm. """ if self.__sjt is not None: raise NotImplementedError("Previous permutation for SJT algorithm is not yet implemented") From 6cd65d0a944e22a22eb09e9eacc28d8d2a06a390 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Wed, 29 May 2024 16:40:50 +0200 Subject: [PATCH 06/11] Added exhaustive examples and changed phrasings and formatting --- src/sage/combinat/SJT.py | 194 ++++++++++++++++++++++++------- src/sage/combinat/permutation.py | 101 +++++++++------- 2 files changed, 209 insertions(+), 86 deletions(-) diff --git a/src/sage/combinat/SJT.py b/src/sage/combinat/SJT.py index ed42d3f7fd3..359fd44e60d 100644 --- a/src/sage/combinat/SJT.py +++ b/src/sage/combinat/SJT.py @@ -1,18 +1,14 @@ r""" -Steinhaus-Johnson-Trotter algorithm. +The Steinhaus-Johnson-Trotter algorithm generates all permutations of a list in +an order such that each permutation is obtained by transposing two adjacent +elements from the previous permutation. -The Steinhaus-Johnson-Trotter algorithm generates permutations in a specific -order by transposing only two elements from the list at each operation. The -algorithm stores an internal state for every element in the permutated list -which corresponds to their direction. To know which elements to move, the -internal state table is accessed and the transposition of two elements is then -done. +Each element of the list has a direction (initialized at -1) that changes at +each permutation and that is used to determine which elements to transpose. Thus +in addition to the permutation itself, the direction of each element is also +stored. -It is important to notice that the permutations are generated in a different -order than the default lexicographic algorithm. - -The class defined here is meant to be used in the ``Permutation`` class when -called with the parameter ``algorithm='sjt'``. +Note that the permutations are not generated in lexicographic order. AUTHORS: @@ -28,46 +24,128 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -class SJT: +from sage.combinat.combinat import CombinatorialElement + +class SJT(CombinatorialElement): + r""" + A representation of a list permuted using the Steinhaus-Johnson-Trotter + algorithm. + + Each element of the list has a direction (initialized at -1) that changes at + each permutation and that is used to determine which elements to transpose. + The directions have three possible values: + + - ``-1``: element tranposes to the left + + - ``1``: element transposes to the right + + - ``0``: element does not move + + Thus in addition to the permutation itself, the direction of each element is + also stored. + + Note that the permutations are not generated in lexicographic order. + + .. WARNING:: + + An ``SJT`` object should always be created with identity permutation for + the algorithm to behave properly. If the identity permutation is not + provided, it expects a coherent list of directions according to the + provided input. This list is not checked. + + .. TODO:: + + Implement the previous permutation for the Steinhaus-Johnson-Trotter + algorithm. + + EXAMPLES:: + + sage: from sage.combinat.SJT import SJT + sage: s = SJT([1, 2, 3, 4]); s + [1, 2, 3, 4] + sage: s, d = s.next(); s, d + ([1, 2, 4, 3], [0, -1, -1, -1]) + sage: p = Permutation(s, algorithm='sjt', directions=d) + sage: p + [1, 2, 4, 3] + sage: p.next() + [1, 4, 2, 3] + + TESTS:: + sage: from sage.combinat.SJT import SJT + sage: s = SJT([1, 2, 3, 4]); s + [1, 2, 3, 4] + sage: s = SJT([1]); s + [1] + sage: s, _ = s.next(); s + False + sage: s = SJT([]); s + [] + sage: s, _ = s.next(); s + False + """ def __init__(self, l, directions=None) -> None: r""" Transpose two elements at positions ``a`` and ``b`` in ``perm`` and their corresponding directions as well following the Steinhaus-Johnson-Trotter algorithm. + Each permutation is obtained by transposing two adjacent elements from + the previous permutation. + INPUT: - - ``l`` -- list: a list of ordered ``int``. + - ``l`` -- list; a list of ordered ``int``. - - ``directions`` -- list (default: ``None`` ): a list of directions for + - ``directions`` -- list (default: ``None`` ); a list of directions for each element in the permuted list. Used when constructing permutations - from a pre-defined internal state. There are three possible values: - - - ``-1`` -> element tranposes to the left - - - ``1`` -> element transposes to the right - - - ``0`` -> element does not move - + from a pre-defined internal state. + + EXAMPLES:: + + sage: from sage.combinat.SJT import SJT + sage: s = SJT([1, 2, 3, 4]); s + [1, 2, 3, 4] + sage: s, d = s.next(); s, d + ([1, 2, 4, 3], [0, -1, -1, -1]) + sage: p = Permutation(s, algorithm='sjt', directions=d) + sage: p + [1, 2, 4, 3] + sage: p.next() + [1, 4, 2, 3] + + TESTS:: + + sage: from sage.combinat.SJT import SJT + sage: s = SJT([1, 3, 2, 4]) + Traceback (most recent call last): + ... + ValueError: no internal state directions were given for non-identity + starting permutation for Steinhaus-Johnson-Trotter algorithm + sage: s = SJT([]); s + [] + sage: s, _ = s.next(); s + False """ # The permuted list. - self.__perm = l + self._list = l - # The length of the permuted list. - self.__n = len(l) + # The length of the permuted list. Return early on empty list. + self._n = len(l) + if self._n == 0: + return if directions is None: - if not all(l[i] <= l[i+1] for i in range(len(l) - 1)): - raise ValueError("No internal state directions were given for " + if not all(l[i] <= l[i+1] for i in range(self._n - 1)): + raise ValueError("no internal state directions were given for " "non-identity starting permutation for " - "Steinhaus-Johnson-Trotter algorithm. Expected identity " - "permutation.") - self.__directions = [-1] * self.__n + "Steinhaus-Johnson-Trotter algorithm") + self._directions = [-1] * self._n # The first element has null direction. - self.__directions[0] = 0 + self._directions[0] = 0 else: - self.__directions = directions + self._directions = directions def __idx_largest_element_non_zero_direction(self, perm, directions): r""" @@ -75,7 +153,7 @@ def __idx_largest_element_non_zero_direction(self, perm, directions): """ largest = 0 index = None - for i in range(self.__n): + for i in range(self._n): if directions[i] != 0: e = perm[i] if e > largest: @@ -86,18 +164,46 @@ def __idx_largest_element_non_zero_direction(self, perm, directions): def next(self): r""" - Produce the next permutation following the Steinhaus-Johnson-Trotter - algorithm. + Produce the next permutation of ``self`` following the + Steinhaus-Johnson-Trotter algorithm. + + OUTPUT: a tuple of + + - the list of the next permutation - OUTPUT: the list of the next permutation. + - the list of associated directions + + EXAMPLES:: + + sage: from sage.combinat.SJT import SJT + sage: s = SJT([1, 2, 3, 4]) + sage: s, d = s.next() + sage: s = SJT(s, directions=d) + sage: s, _ = s.next(); s + [1, 4, 2, 3] + + TESTS:: + + sage: from sage.combinat.SJT import SJT + sage: s = SJT([1, 2, 3]) + sage: s.next() + ([1, 3, 2], [0, -1, -1]) + + sage: s = SJT([1]) + sage: s.next() + (False, None) """ + # Return on empty list. + if self._n == 0: + return False, None + # Copying lists of permutation and directions to avoid changing internal # state of the algorithm if ``next()`` is called without reassigning. - perm = self.__perm[:] - directions = self.__directions[:] + perm = self._list[:] + directions = self._directions[:] # Assume that the element to move is n (which will be in most cases). - selected_elt = self.__n + selected_elt = self._n xi = perm.index(selected_elt) direction = directions[xi] @@ -122,7 +228,7 @@ def next(self): # If the transposition results in the largest element being on one edge # or if the following element in its direction is greater than it, then # then set its direction to 0 - if new_pos == 0 or new_pos == self.__n - 1 or \ + if new_pos == 0 or new_pos == self._n - 1 or \ perm[new_pos + direction] > selected_elt: directions[new_pos] = 0 @@ -130,8 +236,8 @@ def next(self): # element is greater than selected element, change its direction towards # the selected element. This loops has no reason to be if selected # element is n and this will be the case most of the time. - if selected_elt != self.__n: - for i in range(self.__n): + if selected_elt != self._n: + for i in range(self._n): if perm[i] > selected_elt: if i < new_pos: directions[i] = 1 @@ -139,3 +245,5 @@ def next(self): directions[i] = -1 return perm, directions + + __next__ = next diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index bc52bb8ce2d..ed532e0faa0 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -309,11 +309,11 @@ class Permutation(CombinatorialElement): the permutation obtained from the pair using the inverse of the Robinson-Schensted algorithm. - - ``check`` boolean (default: ``True``) -- whether to check that input is - correct. Slows the function down, but ensures that nothing bad happens. This - is set to ``True`` by default. + - ``check`` boolean (default: ``True``); -- whether to check that input is + correct. Slows the function down, but ensures that nothing bad happens. + This is set to ``True`` by default. - - ``algorithm`` string (default: ``lex``) -- the algorithm used to generate + - ``algorithm`` string (default: ``lex``); -- the algorithm used to generate the permutations. Supported algorithms are: - ``lex``: lexicographic order generation, this is the default algorithm. @@ -322,10 +322,9 @@ class Permutation(CombinatorialElement): using only transposition of two elements in the list. It is highly recommended to set ``check=True`` (default value). - - ``directions`` list (default: ``None``) -- the list of directions to be + - ``directions`` list (default: ``None``); -- the list of directions to be used with ``sjt`` algorithm when initializing the ``Permutation`` object - with non-identity permutation. Mainly used to handle internal states, best - to leave it by default. + with non-identity permutation. .. WARNING:: @@ -412,9 +411,8 @@ class Permutation(CombinatorialElement): sage: Permutation([1, 3, 2, 4], algorithm='sjt') Traceback (most recent call last): ... - ValueError: No internal state directions were given for non-identity - starting permutation for Steinhaus-Johnson-Trotter algorithm. Expected - identity permutation. + ValueError: no internal state directions were given for non-identity + starting permutation for Steinhaus-Johnson-Trotter algorithm Construction from a string in cycle notation:: @@ -468,7 +466,7 @@ class Permutation(CombinatorialElement): sage: Permutation([1, 2, 3, 4], algorithm='blah') Traceback (most recent call last): ... - ValueError: Unsupported algorithm blah; expected 'lex' or 'sjt' + ValueError: unsupported algorithm blah; expected 'lex' or 'sjt' From a pair of empty tableaux :: @@ -479,7 +477,8 @@ class Permutation(CombinatorialElement): """ @staticmethod @rename_keyword(deprecation=35233, check_input='check') - def __classcall_private__(cls, l, algorithm='lex', directions=None, check=True): + def __classcall_private__(cls, l, algorithm='lex', directions=None, + check=True): """ Return a permutation in the general permutations parent. @@ -529,11 +528,8 @@ def __classcall_private__(cls, l, algorithm='lex', directions=None, check=True): else: raise ValueError("cannot convert l (= %s) to a Permutation" % l) - if algorithm != "lex" and algorithm != "sjt": - raise ValueError("Unsupported algorithm %s; expected 'lex' or 'sjt'" - % algorithm) # otherwise, it gets processed by CombinatorialElement's __init__. - return Permutations()(l, algorithm=algorithm, directions=directions, check=check) + return Permutations()(l, algorithm, directions, check) @rename_keyword(deprecation=35233, check_input='check') def __init__(self, parent, l, algorithm='lex', directions=None, check=True): @@ -546,20 +542,21 @@ def __init__(self, parent, l, algorithm='lex', directions=None, check=True): - ``l`` -- a list of ``int`` variables - - ``check`` boolean (default: ``True``) -- whether to check that input + - ``check`` boolean (default: ``True``); -- whether to check that input is correct. Slows the function down, but ensures that nothing bad happens. - - ``algorithm`` string (default: ``lex``) -- the algorithm used to + - ``algorithm`` string (default: ``lex``); -- the algorithm used to generate the permutations. Supported algorithms are: - ``lex``: lexicographic order generation, this is the default algorithm. + - ``sjt``: Steinhaus-Johnson-Trotter algorithm to generate - permutations using only transposition of two elements in the list. It - is highly recommended to set ``check=True`` (default value). + permutations using only transposition of two elements in the list. + It is highly recommended to set ``check=True`` (default value). - - ``directions`` list (default: ``None``) -- the list of directions to + - ``directions`` list (default: ``None``); -- the list of directions to be used with ``sjt`` algorithm when initializing the ``Permutation`` object with non-identity permutation. @@ -578,23 +575,21 @@ def __init__(self, parent, l, algorithm='lex', directions=None, check=True): sage: Permutation([1,2,4,5]) Traceback (most recent call last): ... - ValueError: The permutation has length 4 but its maximal element is + ValueError: the permutation has length 4 but its maximal element is 5. Some element may be repeated, or an element is missing, but there is something wrong with its length. sage: Permutation([1, 3, 2], algorithm='sjt') Traceback (most recent call last): ... - ValueError: No internal state directions were given for non-identity - starting permutation for Steinhaus-Johnson-Trotter algorithm. - Expected identity permutation. + ValueError: no internal state directions were given for non-identity + starting permutation for Steinhaus-Johnson-Trotter algorithm sage: Permutation([1, 3, 2], algorithm='sjt', check=False) Traceback (most recent call last): ... - ValueError: No internal state directions were given for non-identity - starting permutation for Steinhaus-Johnson-Trotter algorithm. - Expected identity permutation. + ValueError: no internal state directions were given for non-identity + starting permutation for Steinhaus-Johnson-Trotter algorithm """ l = list(l) # Default initialisation to ``None`` to declare the variable. @@ -602,7 +597,7 @@ def __init__(self, parent, l, algorithm='lex', directions=None, check=True): if algorithm != "lex" and algorithm != "sjt": raise ValueError("unsupported algorithm %s; expected 'lex' or 'sjt'" - "." % algorithm) + % algorithm) if check and len(l) > 0: # Make a copy to sort later @@ -615,18 +610,19 @@ def __init__(self, parent, l, algorithm='lex', directions=None, check=True): except TypeError: raise ValueError("the elements must be integer variables") if i < 1: - raise ValueError("the elements must be strictly positive integers") + raise ValueError("the elements must be strictly positive " + "integers") lst.sort() # Is the maximum element of the permutation the length of input, # or is some integer missing ? if int(lst[-1]) != len(lst): - raise ValueError("The permutation has length "+str(len(lst)) + + raise ValueError("the permutation has length "+str(len(lst)) + " but its maximal element is " + - str(int(lst[-1]))+". Some element " + - "may be repeated, or an element is missing" + - ", but there is something wrong with its length.") + str(int(lst[-1])) + ". Some element may be " + + "repeated, or an element is missing, but " + + "there is something wrong with its length.") # Do the elements appear only once ? previous = lst[0]-1 @@ -826,11 +822,11 @@ def cycle_string(self, singletons=False) -> str: def __next__(self): r""" - Return the permutation that follows ``self`` in lexicographic order on - the symmetric group containing ``self``. If ``self`` is the last - permutation, then ``next`` returns ``False``. If the ``algorithm`` - parameter is specified, the permutations will be generated according to - it. Supported algorithms are: + Return the permutation that follows ``self`` on the symmetric group + containing ``self``. If ``self`` is the last permutation, then ``next`` + returns ``False``. If the ``algorithm`` parameter is specified, the + permutations will be generated according to it. Supported algorithms + are: - ``lex``: lexicographic order generation, this is the default algorithm. @@ -848,17 +844,35 @@ def __next__(self): sage: next(p) False sage: p = Permutation([1, 2, 3], algorithm='sjt') - sage: next(p) + sage: p = next(p); p [1, 3, 2] + sage: p = next(p); p + [3, 1, 2] TESTS:: sage: p = Permutation([]) sage: next(p) False + sage: p = Permutation([], algorithm='sjt') + sage: next(p) + False + sage: p = Permutation([1], algorithm='sjt') + sage: next(p) + False + sage: l = [1, 2, 3, 4] + sage: s = set() + sage: p = Permutation(l, algorithm='sjt') + sage: for _ in range(factorial(len(l))): + ....: s.add(p) + ....: p = p.next() + ....: + sage: p + False + sage: assert(len(s)) == factorial(len(l)) """ if self.__sjt is not None: - # Ensure the same permutations is yielded when called multiple times + # Ensure the same permutation is yielded when called multiple times # without reassigning p, directions = self.__sjt.next() if p is False: @@ -924,7 +938,7 @@ def prev(self): sage: p.prev() Traceback (most recent call last): ... - NotImplementedError: Previous permutation for SJT algorithm is not + NotImplementedError: previous permutation for SJT algorithm is not yet implemented Check that :issue:`16913` is fixed:: @@ -938,7 +952,8 @@ def prev(self): algorithm. """ if self.__sjt is not None: - raise NotImplementedError("Previous permutation for SJT algorithm is not yet implemented") + raise NotImplementedError("previous permutation for SJT algorithm " + "is not yet implemented") p = self[:] n = len(self) From 81027200003167a791fdf099ce21ac90220ccf5c Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Fri, 31 May 2024 11:26:38 +0200 Subject: [PATCH 07/11] SJT class next() method now returns a new SJT object Some doctring formatting has also been done. --- src/sage/combinat/SJT.py | 40 +++++++++++++++----------- src/sage/combinat/permutation.py | 49 ++++++++++++++++---------------- 2 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/sage/combinat/SJT.py b/src/sage/combinat/SJT.py index 359fd44e60d..b541d9b7f47 100644 --- a/src/sage/combinat/SJT.py +++ b/src/sage/combinat/SJT.py @@ -63,9 +63,9 @@ class SJT(CombinatorialElement): sage: from sage.combinat.SJT import SJT sage: s = SJT([1, 2, 3, 4]); s [1, 2, 3, 4] - sage: s, d = s.next(); s, d - ([1, 2, 4, 3], [0, -1, -1, -1]) - sage: p = Permutation(s, algorithm='sjt', directions=d) + sage: s = s.next(); s + [1, 2, 4, 3] + sage: p = Permutation(s._get_perm(), algorithm='sjt', sjt=s) sage: p [1, 2, 4, 3] sage: p.next() @@ -77,11 +77,11 @@ class SJT(CombinatorialElement): [1, 2, 3, 4] sage: s = SJT([1]); s [1] - sage: s, _ = s.next(); s + sage: s = s.next(); s False sage: s = SJT([]); s [] - sage: s, _ = s.next(); s + sage: s = s.next(); s False """ def __init__(self, l, directions=None) -> None: @@ -106,9 +106,9 @@ def __init__(self, l, directions=None) -> None: sage: from sage.combinat.SJT import SJT sage: s = SJT([1, 2, 3, 4]); s [1, 2, 3, 4] - sage: s, d = s.next(); s, d - ([1, 2, 4, 3], [0, -1, -1, -1]) - sage: p = Permutation(s, algorithm='sjt', directions=d) + sage: s = s.next(); s + [1, 2, 4, 3] + sage: p = Permutation(s._get_perm(), algorithm='sjt', sjt=s) sage: p [1, 2, 4, 3] sage: p.next() @@ -124,7 +124,7 @@ def __init__(self, l, directions=None) -> None: starting permutation for Steinhaus-Johnson-Trotter algorithm sage: s = SJT([]); s [] - sage: s, _ = s.next(); s + sage: s = s.next(); s False """ # The permuted list. @@ -162,6 +162,12 @@ def __idx_largest_element_non_zero_direction(self, perm, directions): return index + def _get_perm(self): + r""" + Return the current permutation of ``self``. + """ + return self._list + def next(self): r""" Produce the next permutation of ``self`` following the @@ -177,9 +183,9 @@ def next(self): sage: from sage.combinat.SJT import SJT sage: s = SJT([1, 2, 3, 4]) - sage: s, d = s.next() - sage: s = SJT(s, directions=d) - sage: s, _ = s.next(); s + sage: s = s.next(); s + [1, 2, 4, 3] + sage: s = s.next(); s [1, 4, 2, 3] TESTS:: @@ -187,15 +193,15 @@ def next(self): sage: from sage.combinat.SJT import SJT sage: s = SJT([1, 2, 3]) sage: s.next() - ([1, 3, 2], [0, -1, -1]) + [1, 3, 2] sage: s = SJT([1]) sage: s.next() - (False, None) + False """ # Return on empty list. if self._n == 0: - return False, None + return False # Copying lists of permutation and directions to avoid changing internal # state of the algorithm if ``next()`` is called without reassigning. @@ -214,7 +220,7 @@ def next(self): if xi is None: # We have created every permutation. Detected when all elements # have null direction. - return False, None + return False direction = directions[xi] selected_elt = perm[xi] @@ -244,6 +250,6 @@ def next(self): if i > new_pos: directions[i] = -1 - return perm, directions + return SJT(perm, directions) __next__ = next diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index ed532e0faa0..7032e74d23c 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -309,11 +309,11 @@ class Permutation(CombinatorialElement): the permutation obtained from the pair using the inverse of the Robinson-Schensted algorithm. - - ``check`` boolean (default: ``True``); -- whether to check that input is + - ``check`` -- boolean (default: ``True``); whether to check that input is correct. Slows the function down, but ensures that nothing bad happens. This is set to ``True`` by default. - - ``algorithm`` string (default: ``lex``); -- the algorithm used to generate + - ``algorithm`` -- string (default: ``lex``); the algorithm used to generate the permutations. Supported algorithms are: - ``lex``: lexicographic order generation, this is the default algorithm. @@ -322,9 +322,9 @@ class Permutation(CombinatorialElement): using only transposition of two elements in the list. It is highly recommended to set ``check=True`` (default value). - - ``directions`` list (default: ``None``); -- the list of directions to be - used with ``sjt`` algorithm when initializing the ``Permutation`` object - with non-identity permutation. + - ``sjt`` -- SJT (default: ``None``); the ``SJT`` object holding the + permutation internal state. This should only be specified when + initializing with non-identity permutation. .. WARNING:: @@ -392,8 +392,8 @@ class Permutation(CombinatorialElement): sage: type(p) - Construction using the Steinhaus-Johnson Trotter algorithm to generate - the permutations (lexicographic algorithm by default):: + Generate permutations using the Steinhaus-Johnson Trotter algorithm. The + output is not in lexicographic order:: sage: p = Permutation([1, 2, 3, 4], algorithm='sjt'); p [1, 2, 3, 4] @@ -477,7 +477,7 @@ class Permutation(CombinatorialElement): """ @staticmethod @rename_keyword(deprecation=35233, check_input='check') - def __classcall_private__(cls, l, algorithm='lex', directions=None, + def __classcall_private__(cls, l, algorithm='lex', sjt=None, check=True): """ Return a permutation in the general permutations parent. @@ -529,10 +529,10 @@ def __classcall_private__(cls, l, algorithm='lex', directions=None, raise ValueError("cannot convert l (= %s) to a Permutation" % l) # otherwise, it gets processed by CombinatorialElement's __init__. - return Permutations()(l, algorithm, directions, check) + return Permutations()(l, algorithm, sjt, check) @rename_keyword(deprecation=35233, check_input='check') - def __init__(self, parent, l, algorithm='lex', directions=None, check=True): + def __init__(self, parent, l, algorithm='lex', sjt=None, check=True): """ Constructor. Checks that INPUT is not a mess, and calls :class:`CombinatorialElement`. It should not, because @@ -556,9 +556,9 @@ def __init__(self, parent, l, algorithm='lex', directions=None, check=True): permutations using only transposition of two elements in the list. It is highly recommended to set ``check=True`` (default value). - - ``directions`` list (default: ``None``); -- the list of directions to - be used with ``sjt`` algorithm when initializing the ``Permutation`` - object with non-identity permutation. + - ``sjt`` -- SJT (default: ``None``); the ``SJT`` object holding the + permutation internal state. This should only be specified when + initializing with non-identity permutation. TESTS:: @@ -592,12 +592,13 @@ def __init__(self, parent, l, algorithm='lex', directions=None, check=True): starting permutation for Steinhaus-Johnson-Trotter algorithm """ l = list(l) - # Default initialisation to ``None`` to declare the variable. - self.__sjt = None - if algorithm != "lex" and algorithm != "sjt": + self._algorithm = algorithm.lower() + + if self._algorithm != "lex" and self._algorithm != "sjt": raise ValueError("unsupported algorithm %s; expected 'lex' or 'sjt'" - % algorithm) + % self._algorithm) + if check and len(l) > 0: # Make a copy to sort later @@ -632,8 +633,8 @@ def __init__(self, parent, l, algorithm='lex', directions=None, check=True): raise ValueError("an element appears twice in the input") previous = i - if algorithm == "sjt": - self.__sjt = SJT(l, directions=directions) + if self._algorithm == "sjt": + self._sjt = SJT(l) if sjt is None else sjt CombinatorialElement.__init__(self, parent, l) @@ -871,13 +872,13 @@ def __next__(self): False sage: assert(len(s)) == factorial(len(l)) """ - if self.__sjt is not None: + if self._algorithm == "sjt": # Ensure the same permutation is yielded when called multiple times # without reassigning - p, directions = self.__sjt.next() - if p is False: + sjt = self._sjt.next() + if sjt is False: return False - return Permutations()(p, algorithm='sjt', directions=directions) + return Permutations()(sjt._get_perm(), algorithm='sjt', sjt=sjt) p = self[:] n = len(self) @@ -951,7 +952,7 @@ def prev(self): Implement the previous permutation for the Steinhaus-Johnson-Trotter algorithm. """ - if self.__sjt is not None: + if self._algorithm == "sjt": raise NotImplementedError("previous permutation for SJT algorithm " "is not yet implemented") From 48564bc7dd34001a6fee56f17c7ce765858fc193 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Fri, 31 May 2024 11:33:13 +0200 Subject: [PATCH 08/11] Fixed style on inputs in docstring --- src/sage/combinat/permutation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 7032e74d23c..ea916cdafed 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -542,11 +542,11 @@ def __init__(self, parent, l, algorithm='lex', sjt=None, check=True): - ``l`` -- a list of ``int`` variables - - ``check`` boolean (default: ``True``); -- whether to check that input + - ``check`` -- boolean (default: ``True``); whether to check that input is correct. Slows the function down, but ensures that nothing bad happens. - - ``algorithm`` string (default: ``lex``); -- the algorithm used to + - ``algorithm`` -- string (default: ``lex``); the algorithm used to generate the permutations. Supported algorithms are: - ``lex``: lexicographic order generation, this is the default From 6ed2a795eae2689005a344fa6a22584f300113d3 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Mon, 3 Jun 2024 09:33:18 +0200 Subject: [PATCH 09/11] Style correction and removed useless _get_perm() method --- src/sage/combinat/SJT.py | 19 +++++-------------- src/sage/combinat/permutation.py | 7 ++----- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/sage/combinat/SJT.py b/src/sage/combinat/SJT.py index b541d9b7f47..03883eeb401 100644 --- a/src/sage/combinat/SJT.py +++ b/src/sage/combinat/SJT.py @@ -65,13 +65,14 @@ class SJT(CombinatorialElement): [1, 2, 3, 4] sage: s = s.next(); s [1, 2, 4, 3] - sage: p = Permutation(s._get_perm(), algorithm='sjt', sjt=s) + sage: p = Permutation(s._list, algorithm='sjt', sjt=s) sage: p [1, 2, 4, 3] sage: p.next() [1, 4, 2, 3] TESTS:: + sage: from sage.combinat.SJT import SJT sage: s = SJT([1, 2, 3, 4]); s [1, 2, 3, 4] @@ -97,7 +98,7 @@ def __init__(self, l, directions=None) -> None: - ``l`` -- list; a list of ordered ``int``. - - ``directions`` -- list (default: ``None`` ); a list of directions for + - ``directions`` -- list (default: ``None``); a list of directions for each element in the permuted list. Used when constructing permutations from a pre-defined internal state. @@ -108,7 +109,7 @@ def __init__(self, l, directions=None) -> None: [1, 2, 3, 4] sage: s = s.next(); s [1, 2, 4, 3] - sage: p = Permutation(s._get_perm(), algorithm='sjt', sjt=s) + sage: p = Permutation(s._list, algorithm='sjt', sjt=s) sage: p [1, 2, 4, 3] sage: p.next() @@ -162,22 +163,12 @@ def __idx_largest_element_non_zero_direction(self, perm, directions): return index - def _get_perm(self): - r""" - Return the current permutation of ``self``. - """ - return self._list - def next(self): r""" Produce the next permutation of ``self`` following the Steinhaus-Johnson-Trotter algorithm. - OUTPUT: a tuple of - - - the list of the next permutation - - - the list of associated directions + OUTPUT: the list of the next permutation EXAMPLES:: diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index ea916cdafed..617607551e5 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -404,7 +404,6 @@ class Permutation(CombinatorialElement): sage: p = Permutation([1, 2, 3], algorithm='sjt') sage: for _ in range(6): ....: p = p.next() - ....: sage: p False @@ -477,8 +476,7 @@ class Permutation(CombinatorialElement): """ @staticmethod @rename_keyword(deprecation=35233, check_input='check') - def __classcall_private__(cls, l, algorithm='lex', sjt=None, - check=True): + def __classcall_private__(cls, l, algorithm='lex', sjt=None, check=True): """ Return a permutation in the general permutations parent. @@ -867,7 +865,6 @@ def __next__(self): sage: for _ in range(factorial(len(l))): ....: s.add(p) ....: p = p.next() - ....: sage: p False sage: assert(len(s)) == factorial(len(l)) @@ -878,7 +875,7 @@ def __next__(self): sjt = self._sjt.next() if sjt is False: return False - return Permutations()(sjt._get_perm(), algorithm='sjt', sjt=sjt) + return Permutations()(sjt._list, algorithm='sjt', sjt=sjt) p = self[:] n = len(self) From f9d76cf9877608f6a66152d1eb7dd4b718e9ba9b Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Thu, 6 Jun 2024 14:40:18 +0200 Subject: [PATCH 10/11] Fixed unindent style error on __init__ docstring --- src/sage/combinat/permutation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 617607551e5..8293881a3e8 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -548,11 +548,11 @@ def __init__(self, parent, l, algorithm='lex', sjt=None, check=True): generate the permutations. Supported algorithms are: - ``lex``: lexicographic order generation, this is the default - algorithm. + algorithm. - ``sjt``: Steinhaus-Johnson-Trotter algorithm to generate - permutations using only transposition of two elements in the list. - It is highly recommended to set ``check=True`` (default value). + permutations using only transposition of two elements in the list. + It is highly recommended to set ``check=True`` (default value). - ``sjt`` -- SJT (default: ``None``); the ``SJT`` object holding the permutation internal state. This should only be specified when From 31395364be87d250b3a372f5e9c7f7c44e54bb62 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Sat, 8 Jun 2024 16:03:02 +0200 Subject: [PATCH 11/11] Trailing blank line --- src/sage/combinat/permutation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 8293881a3e8..d77495acd17 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -597,7 +597,6 @@ def __init__(self, parent, l, algorithm='lex', sjt=None, check=True): raise ValueError("unsupported algorithm %s; expected 'lex' or 'sjt'" % self._algorithm) - if check and len(l) > 0: # Make a copy to sort later lst = list(l)