From 4a518c7375010d1925dccf499414c0dae25f1715 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Sun, 16 Nov 2025 22:48:53 +0800 Subject: [PATCH 01/16] Refactor degree sequence functions Refactor degree sequence generation to use generators, reducing memory usage. --- src/sage/combinat/degree_sequences.pyx | 86 ++++++++++++-------------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 4e0d282d068..56cfe699470 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -253,15 +253,6 @@ Checking the consistency of enumeration and test:: sage: DS = DegreeSequences(6) sage: all(seq in DS for seq in DS) True - -.. WARNING:: - - For the moment, iterating over all degree sequences involves building the - list of them first, then iterate on this list. This is obviously bad, - as it requires uselessly a **lot** of memory for large values of `n`. - - This should be changed. Updating the code does not require more - than a couple of minutes. """ # **************************************************************************** @@ -279,7 +270,6 @@ from cysignals.signals cimport sig_on, sig_off cdef unsigned char * seq -cdef list sequences class DegreeSequences: @@ -414,18 +404,39 @@ class DegreeSequences: sig_free(seq) -cdef init(int n): +cdef build_current_seq(): + """ + Build the degree sequence corresponding to the current state of the + algorithm. + """ + global N + global seq + + cdef list s = [] + cdef int i, j + + for N > i >= 0: + for 0 <= j < seq[i]: + s.append(i) + + return s + + +def init(int n): """ Initialize the memory and starts the enumeration algorithm. + + This is a generator that yields degree sequences one at a time. """ global seq global N - global sequences if n == 0: - return [[]] + yield [] + return elif n == 1: - return [[0]] + yield [0] + return seq = check_calloc(n + 1, sizeof(unsigned char)) @@ -433,33 +444,14 @@ cdef init(int n): seq[0] = 1 N = n - sequences = [] - enum(1, 0) - sig_free(seq) - return sequences - -cdef inline add_seq(): - """ - This function is called whenever a sequence is found. - - Build the degree sequence corresponding to the current state of the - algorithm and adds it to the sequences list. - """ - global sequences - global N - global seq - - cdef list s = [] - cdef int i, j - - for N > i >= 0: - for 0 <= j < seq[i]: - s.append(i) - - sequences.append(s) + + try: + yield from enum(1, 0) + finally: + sig_free(seq) -cdef void enum(int k, int M) noexcept: +def enum(int k, int M): r""" Main function; for an explanation of the algorithm please refer to the :mod:`sage.combinat.degree_sequences` documentation. @@ -468,6 +460,8 @@ cdef void enum(int k, int M) noexcept: - ``k`` -- depth of the partial degree sequence - ``M`` -- value of a maximum element in the partial degree sequence + + This is a generator that yields degree sequences. """ cdef int i, j global seq @@ -479,11 +473,9 @@ cdef void enum(int k, int M) noexcept: # Have we found a new degree sequence ? End of recursion ! if k == N: - add_seq() + yield build_current_seq() return - sig_on() - ############################################# # Creating vertices of Vertices of degree M # ############################################# @@ -493,7 +485,7 @@ cdef void enum(int k, int M) noexcept: if M == 0: seq[0] += 1 - enum(k + 1, M) + yield from enum(k + 1, M) seq[0] -= 1 # We need not automatically increase the degree at each step. In this case, @@ -504,7 +496,7 @@ cdef void enum(int k, int M) noexcept: seq[M] += M + 1 seq[M - 1] -= M - enum(k + 1, M) + yield from enum(k + 1, M) seq[M] -= M + 1 seq[M - 1] += M @@ -550,7 +542,7 @@ cdef void enum(int k, int M) noexcept: new_vertex = taken + i + j seq[new_vertex] += 1 - enum(k+1, new_vertex) + yield from enum(k+1, new_vertex) seq[new_vertex] -= 1 seq[current_box-1] += j @@ -574,7 +566,7 @@ cdef void enum(int k, int M) noexcept: seq[0] -= i seq[taken+i] += 1 - enum(k+1, taken+i) + yield from enum(k+1, taken+i) seq[taken+i] -= 1 seq[1] -= i @@ -583,5 +575,3 @@ cdef void enum(int k, int M) noexcept: # Shift everything back to normal ! ( cell N is always equal to 0) for 1 <= i < N: seq[i] = seq[i+1] - - sig_off() From 908cdfc6977822d3ecb3f1e4718cbe92297bd40f Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Sun, 16 Nov 2025 22:51:51 +0800 Subject: [PATCH 02/16] Update degree_sequences.pyx From a4804a52e291612b2980162c15c9c15ba7720044 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Mon, 17 Nov 2025 00:19:34 +0800 Subject: [PATCH 03/16] Remove unused import for cysignals.signals --- src/sage/combinat/degree_sequences.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 56cfe699470..974ce0d32aa 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -266,7 +266,6 @@ Checking the consistency of enumeration and test:: # **************************************************************************** from cysignals.memory cimport check_calloc, sig_free -from cysignals.signals cimport sig_on, sig_off cdef unsigned char * seq From 22c3976e57c10eb3d5a82b367794e4cd8c267188 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Mon, 17 Nov 2025 14:36:10 +0800 Subject: [PATCH 04/16] Change degree sequences from lists to tuples Updated degree sequences representation to use tuples instead of lists for consistency and improved performance. --- src/sage/combinat/degree_sequences.pyx | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 974ce0d32aa..64d996337f5 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -21,17 +21,17 @@ With the object ``DegreeSequences(n)``, one can: sage: for seq in DegreeSequences(4): ....: print(seq) - [0, 0, 0, 0] - [1, 1, 0, 0] - [2, 1, 1, 0] - [3, 1, 1, 1] - [1, 1, 1, 1] - [2, 2, 1, 1] - [2, 2, 2, 0] - [3, 2, 2, 1] - [2, 2, 2, 2] - [3, 3, 2, 2] - [3, 3, 3, 3] + (0, 0, 0, 0) + (1, 1, 0, 0) + (2, 1, 1, 0) + (3, 1, 1, 1) + (1, 1, 1, 1) + (2, 2, 1, 1) + (2, 2, 2, 0) + (3, 2, 2, 1) + (2, 2, 2, 2) + (3, 3, 2, 2) + (3, 3, 3, 3) .. NOTE:: @@ -323,13 +323,13 @@ class DegreeSequences: :issue:`21824`:: sage: [d for d in DegreeSequences(0)] - [[]] + [()] sage: [d for d in DegreeSequences(1)] - [[0]] + [(0,)] sage: [d for d in DegreeSequences(3)] - [[0, 0, 0], [1, 1, 0], [2, 1, 1], [2, 2, 2]] + [(0, 0, 0), (1, 1, 0), (2, 1, 1), (2, 2, 2)] sage: [d for d in DegreeSequences(1)] - [[0]] + [(0,)] """ cdef int n = self._n if len(seq) != n: @@ -418,7 +418,7 @@ cdef build_current_seq(): for 0 <= j < seq[i]: s.append(i) - return s + return tuple(s) def init(int n): @@ -431,10 +431,10 @@ def init(int n): global N if n == 0: - yield [] + yield () return elif n == 1: - yield [0] + yield (0,) return seq = check_calloc(n + 1, sizeof(unsigned char)) From a8ce96df43c89b297585be9146a4036e9f647a14 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Mon, 17 Nov 2025 21:02:24 +0800 Subject: [PATCH 05/16] Use range instead directly loops --- src/sage/combinat/degree_sequences.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 64d996337f5..55d8b37afdd 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -414,8 +414,8 @@ cdef build_current_seq(): cdef list s = [] cdef int i, j - for N > i >= 0: - for 0 <= j < seq[i]: + for i in range(N-1, -1, -1): + for j in range(seq[i]): s.append(i) return tuple(s) From 0c79c498a4d023123986faf47acda595393e6c02 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Tue, 18 Nov 2025 19:09:01 +0800 Subject: [PATCH 06/16] revert the last commit --- src/sage/combinat/degree_sequences.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 55d8b37afdd..64d996337f5 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -414,8 +414,8 @@ cdef build_current_seq(): cdef list s = [] cdef int i, j - for i in range(N-1, -1, -1): - for j in range(seq[i]): + for N > i >= 0: + for 0 <= j < seq[i]: s.append(i) return tuple(s) From 92bc1adcf994f7b53fa52a6e6237d00e3a913de5 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Fri, 21 Nov 2025 21:53:22 +0800 Subject: [PATCH 07/16] fix lint --- src/sage/combinat/degree_sequences.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 64d996337f5..d2245971a7d 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -424,7 +424,7 @@ cdef build_current_seq(): def init(int n): """ Initialize the memory and starts the enumeration algorithm. - + This is a generator that yields degree sequences one at a time. """ global seq From fa7cba35412d15fe472a32ed07b8c2c85e6813ce Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Fri, 21 Nov 2025 22:10:32 +0800 Subject: [PATCH 08/16] Remove unnecessary blank lines in degree_sequences.pyx --- src/sage/combinat/degree_sequences.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index d2245971a7d..9d488a962e0 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -443,7 +443,7 @@ def init(int n): seq[0] = 1 N = n - + try: yield from enum(1, 0) finally: @@ -459,7 +459,7 @@ def enum(int k, int M): - ``k`` -- depth of the partial degree sequence - ``M`` -- value of a maximum element in the partial degree sequence - + This is a generator that yields degree sequences. """ cdef int i, j From 2e18c1ada030fbf3da56e8a31efbe45ec84edd11 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Sun, 23 Nov 2025 02:04:42 +0800 Subject: [PATCH 09/16] Refactor the logic with no global terms We introduce a new class for Enum --- src/sage/combinat/degree_sequences.pyx | 350 +++++++++++++------------ 1 file changed, 182 insertions(+), 168 deletions(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 9d488a962e0..841dfc9d361 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -12,9 +12,9 @@ With the object ``DegreeSequences(n)``, one can: * Check whether a sequence is indeed a degree sequence:: sage: DS = DegreeSequences(5) - sage: [4, 3, 3, 3, 3] in DS + sage: (4, 3, 3, 3, 3) in DS True - sage: [4, 4, 0, 0, 0] in DS + sage: (4, 4, 0, 0, 0) in DS False * List all the possible degree sequences of length `n`:: @@ -39,7 +39,7 @@ With the object ``DegreeSequences(n)``, one can: :func:`~sage.graphs.generators.degree_sequence.DegreeSequence`. For instance:: - sage: ds = [3, 3, 2, 2, 2, 2, 2, 1, 1, 0] + sage: ds = (3, 3, 2, 2, 2, 2, 2, 1, 1, 0) sage: g = graphs.DegreeSequence(ds) # needs networkx sage.graphs sage: g.degree_sequence() # needs networkx sage.graphs [3, 3, 2, 2, 2, 2, 2, 1, 1, 0] @@ -195,7 +195,8 @@ Indeed, when enumerating all the degree sequences of length `n`, Sage first allocates an array ``seq`` of `n+1` integers where ``seq[i]`` is the number of elements of value ``i`` in the current sequence. Obviously, ``seq[n]=0`` holds in permanence : it is useful to allocate a larger array than necessary to -simplify the code. The ``seq`` array is a global variable. +simplify the code. The ``seq`` array lives inside a short-lived enumerator +object created for each traversal so that no global state leaks between calls. The recursive function ``enum(depth, maximum)`` is the one building the list of sequences. It builds the list of degree sequences of length `n` which *extend* @@ -266,9 +267,7 @@ Checking the consistency of enumeration and test:: # **************************************************************************** from cysignals.memory cimport check_calloc, sig_free - - -cdef unsigned char * seq +from sage.rings.integer import Integer class DegreeSequences: @@ -287,21 +286,33 @@ class DegreeSequences: sage: DegreeSequences(8) Degree sequences on 8 elements - sage: [3,3,2,2,2,2,2,2] in DegreeSequences(8) + sage: DegreeSequences(1) + Degree sequences on 1 element + sage: (3,3,2,2,2,2,2,2) in DegreeSequences(8) True TESTS: :issue:`21824`:: + sage: DegreeSequences(RR(1/2)) + Traceback (most recent call last): + ... + TypeError: the input parameter must be a non-negative integer sage: DegreeSequences(-1) Traceback (most recent call last): ... ValueError: the input parameter must be >= 0 """ + try: + n = Integer(n) + except (TypeError, ValueError): + raise TypeError("the input parameter must be a non-negative integer") + if n < 0: raise ValueError("the input parameter must be >= 0") - self._n = n + + self._n = int(n) def __contains__(self, seq): """ @@ -310,14 +321,14 @@ class DegreeSequences: EXAMPLES:: - sage: [3,3,2,2,2,2,2,2] in DegreeSequences(8) + sage: (3,3,2,2,2,2,2,2) in DegreeSequences(8) True TESTS: :issue:`15503`:: - sage: [2,2,2,2,1,1,1] in DegreeSequences(7) + sage: (2,2,2,2,1,1,1) in DegreeSequences(7) False :issue:`21824`:: @@ -330,6 +341,12 @@ class DegreeSequences: [(0, 0, 0), (1, 1, 0), (2, 1, 1), (2, 2, 2)] sage: [d for d in DegreeSequences(1)] [(0,)] + + For lists we can also check containment:: + sage: [3,3,2,2,2,2,2,2] in DegreeSequences(8) + True + sage: [2,2,2,2,1,1,1] in DegreeSequences(7) + False """ cdef int n = self._n if len(seq) != n: @@ -379,15 +396,14 @@ class DegreeSequences: sage: DegreeSequences(6) Degree sequences on 6 elements """ + if self._n == 1: + return "Degree sequences on 1 element" return "Degree sequences on "+str(self._n)+" elements" def __iter__(self): """ Iterate over all the degree sequences. - TODO: THIS SHOULD BE UPDATED AS SOON AS THE YIELD KEYWORD APPEARS IN - CYTHON. See comment in the class' documentation. - EXAMPLES:: sage: DS = DegreeSequences(6) @@ -396,29 +412,162 @@ class DegreeSequences: """ yield from init(self._n) - def __dealloc__(): +cdef class DegreeSequenceEnumerator: + cdef int N + cdef unsigned char * seq + + def __cinit__(self, int n): + self.seq = NULL + self.N = n + self.seq = check_calloc(n + 1, sizeof(unsigned char)) + self.seq[0] = 1 + + def __dealloc__(self): + if self.seq != NULL: + sig_free(self.seq) + self.seq = NULL + + cdef tuple build_current_seq(self): """ - Freeing the memory + Return the degree sequence represented by the current counts. """ - sig_free(seq) + cdef list s = [] + cdef int i, j, N = self.N + cdef unsigned char * seq = self.seq + cdef int count + for i in range(N - 1, -1, -1): + count = seq[i] + for j in range(count): + s.append(i) -cdef build_current_seq(): - """ - Build the degree sequence corresponding to the current state of the - algorithm. - """ - global N - global seq + return tuple(s) + + def enum(self, int k, int M): + r""" + Main function; for an explanation of the algorithm please refer to the + :mod:`sage.combinat.degree_sequences` documentation. + + INPUT: + + - ``k`` -- depth of the partial degree sequence + - ``M`` -- value of a maximum element in the partial degree sequence + + This is a generator that yields degree sequences. + """ + cdef int i, j + cdef unsigned char * seq = self.seq + cdef int N = self.N + cdef int taken = 0 + cdef int current_box + cdef int n_current_box + cdef int n_previous_box + cdef int new_vertex + + # Have we found a new degree sequence ? End of recursion ! + if k == N: + yield self.build_current_seq() + return + + ############################################# + # Creating vertices of Vertices of degree M # + ############################################# + + # If 0 is the current maximum degree, we can always extend the degree + # sequence with another 0 + if M == 0: + + seq[0] += 1 + yield from self.enum(k + 1, M) + seq[0] -= 1 + + # We need not automatically increase the degree at each step. In this case, + # we have no other choice but to link the new vertex of degree M to vertices + # of degree M-1, which will become vertices of degree M too. + elif seq[M - 1] >= M: + + seq[M] += M + 1 + seq[M - 1] -= M + + yield from self.enum(k + 1, M) + + seq[M] -= M + 1 + seq[M - 1] += M + + ############################################### + # Creating vertices of Vertices of degree > M # + ############################################### + + for current_box in range(M, 0, -1): + + # If there is not enough vertices in the boxes available + if taken + (seq[current_box] - 1) + seq[current_box-1] <= M: + taken += seq[current_box] + seq[current_box+1] += seq[current_box] + seq[current_box] = 0 + continue + + # The degree of the new vertex will be taken + i + j where: + # + # * i is the number of vertices taken in the *current* box + # * j the number of vertices taken in the *previous* one + + n_current_box = seq[current_box] + n_previous_box = seq[current_box-1] + + # Note to self, and others: + # + # In the following lines, there are many incrementation/decrementation + # that *may* be replaced by only +1 and -1 and save some + # instructions. This would involve adding several "if", and I feared it + # would make the code even uglier. If you are willing to give it a try, + # **please check the results** ! It is trickier that it seems ! Even + # changing the lower bounds in the for loops would require tests + # afterwards. + + for i in range(max(0, (M + 1) - n_previous_box - taken), n_current_box): + seq[current_box] -= i + seq[current_box+1] += i + + for j in range(max(0, (M + 1) - taken - i), n_previous_box + 1): + seq[current_box-1] -= j + seq[current_box] += j + + new_vertex = taken + i + j + seq[new_vertex] += 1 + yield from self.enum(k+1, new_vertex) + seq[new_vertex] -= 1 + + seq[current_box-1] += j + seq[current_box] -= j + + seq[current_box] += i + seq[current_box+1] -= i + + taken += n_current_box + seq[current_box] = 0 + seq[current_box+1] += n_current_box + + # Corner case + # + # Now current_box = 0. All the vertices of nonzero degree are taken, we just + # want to know how many vertices of degree 0 will be neighbors of the new + # vertex. + for i in range(max(0, (M + 1) - taken), seq[0] + 1): - cdef list s = [] - cdef int i, j + seq[1] += i + seq[0] -= i + seq[taken+i] += 1 - for N > i >= 0: - for 0 <= j < seq[i]: - s.append(i) + yield from self.enum(k+1, taken+i) - return tuple(s) + seq[taken+i] -= 1 + seq[1] -= i + seq[0] += i + + # Shift everything back to normal ! ( cell N is always equal to 0) + for i in range(1, N): + seq[i] = seq[i+1] def init(int n): @@ -427,9 +576,6 @@ def init(int n): This is a generator that yields degree sequences one at a time. """ - global seq - global N - if n == 0: yield () return @@ -437,140 +583,8 @@ def init(int n): yield (0,) return - seq = check_calloc(n + 1, sizeof(unsigned char)) - - # We begin with one vertex of degree 0 - seq[0] = 1 - - N = n - + cdef DegreeSequenceEnumerator enumerator = DegreeSequenceEnumerator(n) try: - yield from enum(1, 0) + yield from enumerator.enum(1, 0) finally: - sig_free(seq) - - -def enum(int k, int M): - r""" - Main function; for an explanation of the algorithm please refer to the - :mod:`sage.combinat.degree_sequences` documentation. - - INPUT: - - - ``k`` -- depth of the partial degree sequence - - ``M`` -- value of a maximum element in the partial degree sequence - - This is a generator that yields degree sequences. - """ - cdef int i, j - global seq - cdef int taken = 0 - cdef int current_box - cdef int n_current_box - cdef int n_previous_box - cdef int new_vertex - - # Have we found a new degree sequence ? End of recursion ! - if k == N: - yield build_current_seq() - return - - ############################################# - # Creating vertices of Vertices of degree M # - ############################################# - - # If 0 is the current maximum degree, we can always extend the degree - # sequence with another 0 - if M == 0: - - seq[0] += 1 - yield from enum(k + 1, M) - seq[0] -= 1 - - # We need not automatically increase the degree at each step. In this case, - # we have no other choice but to link the new vertex of degree M to vertices - # of degree M-1, which will become vertices of degree M too. - elif seq[M - 1] >= M: - - seq[M] += M + 1 - seq[M - 1] -= M - - yield from enum(k + 1, M) - - seq[M] -= M + 1 - seq[M - 1] += M - - ############################################### - # Creating vertices of Vertices of degree > M # - ############################################### - - for M >= current_box > 0: - - # If there is not enough vertices in the boxes available - if taken + (seq[current_box] - 1) + seq[current_box-1] <= M: - taken += seq[current_box] - seq[current_box+1] += seq[current_box] - seq[current_box] = 0 - continue - - # The degree of the new vertex will be taken + i + j where: - # - # * i is the number of vertices taken in the *current* box - # * j the number of vertices taken in the *previous* one - - n_current_box = seq[current_box] - n_previous_box = seq[current_box-1] - - # Note to self, and others: - # - # In the following lines, there are many incrementation/decrementation - # that *may* be replaced by only +1 and -1 and save some - # instructions. This would involve adding several "if", and I feared it - # would make the code even uglier. If you are willing to give it a try, - # **please check the results** ! It is trickier that it seems ! Even - # changing the lower bounds in the for loops would require tests - # afterwards. - - for max(0, (M+1)-n_previous_box-taken) <= i < n_current_box: - seq[current_box] -= i - seq[current_box+1] += i - - for max(0, ((M+1)-taken-i)) <= j <= n_previous_box: - seq[current_box-1] -= j - seq[current_box] += j - - new_vertex = taken + i + j - seq[new_vertex] += 1 - yield from enum(k+1, new_vertex) - seq[new_vertex] -= 1 - - seq[current_box-1] += j - seq[current_box] -= j - - seq[current_box] += i - seq[current_box+1] -= i - - taken += n_current_box - seq[current_box] = 0 - seq[current_box+1] += n_current_box - - # Corner case - # - # Now current_box = 0. All the vertices of nonzero degree are taken, we just - # want to know how many vertices of degree 0 will be neighbors of the new - # vertex. - for max(0, ((M+1)-taken)) <= i <= seq[0]: - - seq[1] += i - seq[0] -= i - seq[taken+i] += 1 - - yield from enum(k+1, taken+i) - - seq[taken+i] -= 1 - seq[1] -= i - seq[0] += i - - # Shift everything back to normal ! ( cell N is always equal to 0) - for 1 <= i < N: - seq[i] = seq[i+1] + enumerator = None From 1ab6ec264d9ff5bf05387bf7b7323a07e78961a1 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Sun, 23 Nov 2025 02:09:06 +0800 Subject: [PATCH 10/16] let cdef in the top of function --- src/sage/combinat/degree_sequences.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 841dfc9d361..85381136614 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -576,6 +576,8 @@ def init(int n): This is a generator that yields degree sequences one at a time. """ + cdef DegreeSequenceEnumerator enumerator = DegreeSequenceEnumerator(n) + if n == 0: yield () return @@ -583,7 +585,6 @@ def init(int n): yield (0,) return - cdef DegreeSequenceEnumerator enumerator = DegreeSequenceEnumerator(n) try: yield from enumerator.enum(1, 0) finally: From bebfb52ff49a50001dfa25112af4770b4bd8f40f Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Sun, 23 Nov 2025 03:55:27 +0800 Subject: [PATCH 11/16] just self._n=n Update the assignment of self._n to avoid unnecessary conversion. --- src/sage/combinat/degree_sequences.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 85381136614..c3e2ec8dde5 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -312,7 +312,7 @@ class DegreeSequences: if n < 0: raise ValueError("the input parameter must be >= 0") - self._n = int(n) + self._n = n def __contains__(self, seq): """ From dd9a4f2984bf9f3d8273242c646ca03e038d7d27 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Sun, 23 Nov 2025 04:17:56 +0800 Subject: [PATCH 12/16] Update src/sage/combinat/degree_sequences.pyx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/sage/combinat/degree_sequences.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index c3e2ec8dde5..4e9ce7ca6fc 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -341,7 +341,6 @@ class DegreeSequences: [(0, 0, 0), (1, 1, 0), (2, 1, 1), (2, 2, 2)] sage: [d for d in DegreeSequences(1)] [(0,)] - For lists we can also check containment:: sage: [3,3,2,2,2,2,2,2] in DegreeSequences(8) True From 43e434dc7675a74626e4e2e142ba9d604a077d07 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Sun, 23 Nov 2025 04:27:14 +0800 Subject: [PATCH 13/16] Move enumerator initialization after n checks --- src/sage/combinat/degree_sequences.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 4e9ce7ca6fc..50c8c2b0c90 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -575,8 +575,6 @@ def init(int n): This is a generator that yields degree sequences one at a time. """ - cdef DegreeSequenceEnumerator enumerator = DegreeSequenceEnumerator(n) - if n == 0: yield () return @@ -584,6 +582,7 @@ def init(int n): yield (0,) return + cdef DegreeSequenceEnumerator enumerator = DegreeSequenceEnumerator(n) try: yield from enumerator.enum(1, 0) finally: From 34ee380abea365186fed54563cb69f97adb39928 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Sun, 23 Nov 2025 04:29:57 +0800 Subject: [PATCH 14/16] Enhance documentation with containment examples Added examples for checking containment in DegreeSequences. --- src/sage/combinat/degree_sequences.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 50c8c2b0c90..c8381f990ce 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -341,7 +341,9 @@ class DegreeSequences: [(0, 0, 0), (1, 1, 0), (2, 1, 1), (2, 2, 2)] sage: [d for d in DegreeSequences(1)] [(0,)] + For lists we can also check containment:: + sage: [3,3,2,2,2,2,2,2] in DegreeSequences(8) True sage: [2,2,2,2,1,1,1] in DegreeSequences(7) From fddebe4fb74fca7fdb68fd66abf7fe2df3da9f07 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Sun, 23 Nov 2025 04:34:48 +0800 Subject: [PATCH 15/16] Update src/sage/combinat/degree_sequences.pyx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/sage/combinat/degree_sequences.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index c8381f990ce..8bd2af636e4 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -471,7 +471,7 @@ cdef class DegreeSequenceEnumerator: return ############################################# - # Creating vertices of Vertices of degree M # + # Creating vertices of degree M # ############################################# # If 0 is the current maximum degree, we can always extend the degree From 1ce68b0b527cef8d11dba42905556165ce720ef9 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Sun, 23 Nov 2025 04:34:57 +0800 Subject: [PATCH 16/16] Update src/sage/combinat/degree_sequences.pyx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/sage/combinat/degree_sequences.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 8bd2af636e4..96b46bdff08 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -496,7 +496,7 @@ cdef class DegreeSequenceEnumerator: seq[M - 1] += M ############################################### - # Creating vertices of Vertices of degree > M # + # Creating vertices of degree > M # ############################################### for current_box in range(M, 0, -1):