Skip to content

Commit a632716

Browse files
author
Release Manager
committed
sagemathgh-41188: Refactor degree sequence functions Refactor degree sequence generation to use generators, reducing memory usage. Fix sagemath#41187 <!-- ^ Please provide a concise and informative title. --> <!-- ^ Don't put issue numbers in the title, do this in the PR description below. --> <!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method to calculate 1 + 2". --> <!-- v Describe your changes below in detail. --> <!-- v Why is this change required? What problem does it solve? --> <!-- v If this PR resolves an open issue, please link to it here. For example, "Fixes sagemath#12345". --> ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [ ] The title is concise and informative. - [ ] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [ ] I have created tests covering the changes. - [ ] I have updated the documentation and checked the documentation preview. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on. For example, --> <!-- - sagemath#12345: short description why this is a dependency --> <!-- - sagemath#34567: ... --> URL: sagemath#41188 Reported by: Chenxin Zhong Reviewer(s): Chenxin Zhong, Martin Rubey, Max Alekseyev, user202729
2 parents a20f8d2 + 0c79c49 commit a632716

File tree

1 file changed

+53
-64
lines changed

1 file changed

+53
-64
lines changed

src/sage/combinat/degree_sequences.pyx

Lines changed: 53 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ With the object ``DegreeSequences(n)``, one can:
2121
2222
sage: for seq in DegreeSequences(4):
2323
....: print(seq)
24-
[0, 0, 0, 0]
25-
[1, 1, 0, 0]
26-
[2, 1, 1, 0]
27-
[3, 1, 1, 1]
28-
[1, 1, 1, 1]
29-
[2, 2, 1, 1]
30-
[2, 2, 2, 0]
31-
[3, 2, 2, 1]
32-
[2, 2, 2, 2]
33-
[3, 3, 2, 2]
34-
[3, 3, 3, 3]
24+
(0, 0, 0, 0)
25+
(1, 1, 0, 0)
26+
(2, 1, 1, 0)
27+
(3, 1, 1, 1)
28+
(1, 1, 1, 1)
29+
(2, 2, 1, 1)
30+
(2, 2, 2, 0)
31+
(3, 2, 2, 1)
32+
(2, 2, 2, 2)
33+
(3, 3, 2, 2)
34+
(3, 3, 3, 3)
3535
3636
.. NOTE::
3737
@@ -253,15 +253,6 @@ Checking the consistency of enumeration and test::
253253
sage: DS = DegreeSequences(6)
254254
sage: all(seq in DS for seq in DS)
255255
True
256-
257-
.. WARNING::
258-
259-
For the moment, iterating over all degree sequences involves building the
260-
list of them first, then iterate on this list. This is obviously bad,
261-
as it requires uselessly a **lot** of memory for large values of `n`.
262-
263-
This should be changed. Updating the code does not require more
264-
than a couple of minutes.
265256
"""
266257

267258
# ****************************************************************************
@@ -275,11 +266,9 @@ Checking the consistency of enumeration and test::
275266
# ****************************************************************************
276267

277268
from cysignals.memory cimport check_calloc, sig_free
278-
from cysignals.signals cimport sig_on, sig_off
279269

280270

281271
cdef unsigned char * seq
282-
cdef list sequences
283272

284273

285274
class DegreeSequences:
@@ -334,13 +323,13 @@ class DegreeSequences:
334323
:issue:`21824`::
335324
336325
sage: [d for d in DegreeSequences(0)]
337-
[[]]
326+
[()]
338327
sage: [d for d in DegreeSequences(1)]
339-
[[0]]
328+
[(0,)]
340329
sage: [d for d in DegreeSequences(3)]
341-
[[0, 0, 0], [1, 1, 0], [2, 1, 1], [2, 2, 2]]
330+
[(0, 0, 0), (1, 1, 0), (2, 1, 1), (2, 2, 2)]
342331
sage: [d for d in DegreeSequences(1)]
343-
[[0]]
332+
[(0,)]
344333
"""
345334
cdef int n = self._n
346335
if len(seq) != n:
@@ -414,52 +403,54 @@ class DegreeSequences:
414403
sig_free(seq)
415404

416405

417-
cdef init(int n):
406+
cdef build_current_seq():
407+
"""
408+
Build the degree sequence corresponding to the current state of the
409+
algorithm.
410+
"""
411+
global N
412+
global seq
413+
414+
cdef list s = []
415+
cdef int i, j
416+
417+
for N > i >= 0:
418+
for 0 <= j < seq[i]:
419+
s.append(i)
420+
421+
return tuple(s)
422+
423+
424+
def init(int n):
418425
"""
419426
Initialize the memory and starts the enumeration algorithm.
427+
428+
This is a generator that yields degree sequences one at a time.
420429
"""
421430
global seq
422431
global N
423-
global sequences
424432

425433
if n == 0:
426-
return [[]]
434+
yield ()
435+
return
427436
elif n == 1:
428-
return [[0]]
437+
yield (0,)
438+
return
429439

430440
seq = <unsigned char *>check_calloc(n + 1, sizeof(unsigned char))
431441

432442
# We begin with one vertex of degree 0
433443
seq[0] = 1
434444

435445
N = n
436-
sequences = []
437-
enum(1, 0)
438-
sig_free(seq)
439-
return sequences
440-
441-
cdef inline add_seq():
442-
"""
443-
This function is called whenever a sequence is found.
444-
445-
Build the degree sequence corresponding to the current state of the
446-
algorithm and adds it to the sequences list.
447-
"""
448-
global sequences
449-
global N
450-
global seq
451-
452-
cdef list s = []
453-
cdef int i, j
454-
455-
for N > i >= 0:
456-
for 0 <= j < seq[i]:
457-
s.append(i)
458-
459-
sequences.append(s)
446+
447+
try:
448+
yield from enum(1, 0)
449+
finally:
450+
sig_free(seq)
460451

461452

462-
cdef void enum(int k, int M) noexcept:
453+
def enum(int k, int M):
463454
r"""
464455
Main function; for an explanation of the algorithm please refer to the
465456
:mod:`sage.combinat.degree_sequences` documentation.
@@ -468,6 +459,8 @@ cdef void enum(int k, int M) noexcept:
468459
469460
- ``k`` -- depth of the partial degree sequence
470461
- ``M`` -- value of a maximum element in the partial degree sequence
462+
463+
This is a generator that yields degree sequences.
471464
"""
472465
cdef int i, j
473466
global seq
@@ -479,11 +472,9 @@ cdef void enum(int k, int M) noexcept:
479472

480473
# Have we found a new degree sequence ? End of recursion !
481474
if k == N:
482-
add_seq()
475+
yield build_current_seq()
483476
return
484477

485-
sig_on()
486-
487478
#############################################
488479
# Creating vertices of Vertices of degree M #
489480
#############################################
@@ -493,7 +484,7 @@ cdef void enum(int k, int M) noexcept:
493484
if M == 0:
494485

495486
seq[0] += 1
496-
enum(k + 1, M)
487+
yield from enum(k + 1, M)
497488
seq[0] -= 1
498489

499490
# We need not automatically increase the degree at each step. In this case,
@@ -504,7 +495,7 @@ cdef void enum(int k, int M) noexcept:
504495
seq[M] += M + 1
505496
seq[M - 1] -= M
506497

507-
enum(k + 1, M)
498+
yield from enum(k + 1, M)
508499

509500
seq[M] -= M + 1
510501
seq[M - 1] += M
@@ -550,7 +541,7 @@ cdef void enum(int k, int M) noexcept:
550541

551542
new_vertex = taken + i + j
552543
seq[new_vertex] += 1
553-
enum(k+1, new_vertex)
544+
yield from enum(k+1, new_vertex)
554545
seq[new_vertex] -= 1
555546

556547
seq[current_box-1] += j
@@ -574,7 +565,7 @@ cdef void enum(int k, int M) noexcept:
574565
seq[0] -= i
575566
seq[taken+i] += 1
576567

577-
enum(k+1, taken+i)
568+
yield from enum(k+1, taken+i)
578569

579570
seq[taken+i] -= 1
580571
seq[1] -= i
@@ -583,5 +574,3 @@ cdef void enum(int k, int M) noexcept:
583574
# Shift everything back to normal ! ( cell N is always equal to 0)
584575
for 1 <= i < N:
585576
seq[i] = seq[i+1]
586-
587-
sig_off()

0 commit comments

Comments
 (0)