Skip to content

Commit 4cf9ee7

Browse files
author
Release Manager
committed
sagemathgh-39948: fix enumeration of implicitly finite sets of partitions <!-- ^ 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#39948 Reported by: Martin Rubey Reviewer(s): Martin Rubey, Travis Scrimshaw
2 parents 23bd37f + 638d902 commit 4cf9ee7

File tree

2 files changed

+103
-15
lines changed

2 files changed

+103
-15
lines changed

src/sage/combinat/integer_lists/base.pyx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ from cpython.object cimport Py_LE, Py_EQ, Py_NE, Py_GE
2626
from sage.misc.constant_function import ConstantFunction
2727
from sage.structure.element cimport RingElement
2828
from sage.rings.integer cimport Integer
29+
from sage.rings.integer_ring import ZZ
2930

3031
Infinity = float('+inf')
3132
MInfinity = float('-inf')
@@ -202,9 +203,19 @@ cdef class IntegerListsBackend():
202203
sage: C = IntegerListsLex(n=2, max_length=3, min_slope=0)
203204
sage: all(l in C for l in C) # indirect doctest
204205
True
206+
207+
TESTS::
208+
209+
sage: [None, 2] in C
210+
False
211+
212+
sage: [1/2, 3/2] in C
213+
False
205214
"""
206215
if len(comp) < self.min_length or len(comp) > self.max_length:
207216
return False
217+
if not all(e in ZZ for e in comp):
218+
return False
208219
n = sum(comp)
209220
if n < self.min_sum or n > self.max_sum:
210221
return False

src/sage/combinat/partition.py

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@
151151
152152
Finally, here are the partitions of `4` with ``[1,1,1]`` as an inner
153153
bound (i. e., the partitions of `4` containing the partition ``[1,1,1]``).
154-
Note that ``inner`` sets ``min_length`` to the length of its argument::
154+
Note that ``inner`` sets ``min_length`` to the length of its argument,
155+
interpreted as a partition::
155156
156157
sage: Partitions(4, inner=[1,1,1]).list()
157158
[[2, 1, 1], [1, 1, 1, 1]]
@@ -6164,6 +6165,27 @@ def __classcall_private__(cls, n=None, **kwargs):
61646165
"""
61656166
if n is infinity:
61666167
raise ValueError("n cannot be infinite")
6168+
if 'length' in kwargs and ('min_length' in kwargs or 'max_length' in kwargs):
6169+
raise ValueError("do not specify the length together with the minimal or maximal length")
6170+
# remove kwargs that specify the default value
6171+
if 'min_part' in kwargs and kwargs['min_part'] == 1:
6172+
del kwargs['min_part']
6173+
if 'max_slope' in kwargs and not kwargs['max_slope']:
6174+
del kwargs['max_slope']
6175+
# preprocess for UniqueRepresentation
6176+
if 'outer' in kwargs and not isinstance(kwargs['outer'], Partition):
6177+
m = infinity
6178+
kwargs['outer'] = [m for e in kwargs['outer']
6179+
if (m := min(m, e if e is infinity else ZZ(e))) > 0]
6180+
if kwargs['outer'] and kwargs['outer'][0] is infinity:
6181+
kwargs['outer'] = tuple(kwargs['outer'])
6182+
else:
6183+
kwargs['outer'] = Partition(kwargs['outer'])
6184+
if 'inner' in kwargs and not isinstance(kwargs['inner'], Partition):
6185+
m = ZZ.zero()
6186+
kwargs['inner'] = Partition(reversed([(m := max(m, e))
6187+
for e in reversed(kwargs['inner'])]))
6188+
61676189
if isinstance(n, (int, Integer)):
61686190
if not kwargs:
61696191
return Partitions_n(n)
@@ -6208,9 +6230,6 @@ def __classcall_private__(cls, n=None, **kwargs):
62086230
+ "'ending', 'regular' and 'restricted' "
62096231
+ "cannot be combined with anything else")
62106232

6211-
if 'length' in kwargs and ('min_length' in kwargs or 'max_length' in kwargs):
6212-
raise ValueError("do not specify the length together with the minimal or maximal length")
6213-
62146233
if set(kwargs).issubset(['length', 'min_part', 'max_part',
62156234
'min_length', 'max_length']):
62166235
if 'length' in kwargs:
@@ -6239,7 +6258,8 @@ def __classcall_private__(cls, n=None, **kwargs):
62396258

62406259
return Partitions_length_and_parts_constrained(n, min_length, max_length, min_part, max_part)
62416260

6242-
# FIXME: should inherit from IntegerListLex, and implement repr, or _name as a lazy attribute
6261+
# translate keywords to IntegerListsLex
6262+
# FIXME: should inherit from IntegerListsLex, and implement repr, or _name as a lazy attribute
62436263
kwargs['name'] = "Partitions of the integer {} satisfying constraints {}".format(n, ", ".join(["{}={}".format(key, kwargs[key]) for key in sorted(kwargs)]))
62446264

62456265
# min_part is at least 1, and it is 1 by default
@@ -6252,18 +6272,17 @@ def __classcall_private__(cls, n=None, **kwargs):
62526272
raise ValueError("the minimum slope must be nonnegative")
62536273

62546274
if 'outer' in kwargs:
6275+
kwargs['ceiling'] = tuple(kwargs['outer'])
62556276
kwargs['max_length'] = min(len(kwargs['outer']),
62566277
kwargs.get('max_length', infinity))
6257-
6258-
kwargs['ceiling'] = tuple(kwargs['outer'])
62596278
del kwargs['outer']
62606279

62616280
if 'inner' in kwargs:
6262-
inner = [x for x in kwargs['inner'] if x > 0]
6263-
kwargs['floor'] = inner
6264-
kwargs['min_length'] = max(len(inner),
6281+
kwargs['floor'] = tuple(kwargs['inner'])
6282+
kwargs['min_length'] = max(len(kwargs['inner']),
62656283
kwargs.get('min_length', 0))
62666284
del kwargs['inner']
6285+
62676286
return Partitions_with_constraints(n, **kwargs)
62686287

62696288
if n is None or n is NN or n is NonNegativeIntegers():
@@ -6288,6 +6307,8 @@ def __classcall_private__(cls, n=None, **kwargs):
62886307
elif 'max_part' in kwargs and 'max_length' in kwargs:
62896308
return PartitionsInBox(kwargs['max_length'], kwargs['max_part'])
62906309

6310+
# IntegerListsLex does not deal well with infinite sets,
6311+
# so we use a class inheriting from Partitions
62916312
return Partitions_all_constrained(**kwargs)
62926313

62936314
raise ValueError("n must be an integer or be equal to one of "
@@ -6811,17 +6832,41 @@ def __init__(self, **kwargs):
68116832
"""
68126833
TESTS::
68136834
6814-
sage: TestSuite(sage.combinat.partition.Partitions_all_constrained(max_length=3)).run() # long time
6835+
sage: TestSuite(sage.combinat.partition.Partitions_all_constrained(max_length=3, max_slope=0)).run() # long time
6836+
6837+
sage: list(Partitions(max_part=4, max_slope=-3))
6838+
[[], [1], [2], [3], [4], [4, 1]]
6839+
6840+
sage: [pi for n in range(10) for pi in Partitions(n, max_part=4, max_slope=-3)]
6841+
[[], [1], [2], [3], [4], [4, 1]]
68156842
"""
68166843
self._constraints = kwargs
6817-
Partitions.__init__(self, is_infinite=True)
6844+
self._max_sum = infinity
6845+
if 'outer' in kwargs and kwargs['outer'] and kwargs['outer'][0] is not infinity:
6846+
self._max_sum = kwargs['outer'][0] * len(kwargs['outer'])
6847+
else:
6848+
if 'length' in kwargs:
6849+
max_length = kwargs['length']
6850+
elif 'max_length' in kwargs:
6851+
max_length = kwargs['max_length']
6852+
elif 'max_part' in kwargs and kwargs.get('max_slope', 0) < 0:
6853+
max_length = 1 + (kwargs['max_part'] - 1) // (-kwargs['max_slope'])
6854+
else:
6855+
max_length = infinity
6856+
6857+
if max_length is not infinity and 'max_part' in kwargs:
6858+
self._max_sum = kwargs['max_part'] * max_length
6859+
6860+
Partitions.__init__(self, is_infinite=self._max_sum is infinity)
68186861

68196862
def __contains__(self, x):
68206863
"""
6864+
Check if ``x`` is contained in ``self``.
6865+
68216866
TESTS::
68226867
68236868
sage: from sage.combinat.partition import Partitions_all_constrained
6824-
sage: P = Partitions_all_constrained(max_part=3, max_length=2)
6869+
sage: P = Partitions_all_constrained(max_part=3, max_length=2, max_slope=0)
68256870
sage: 1 in P
68266871
False
68276872
sage: Partition([2,1]) in P
@@ -6866,9 +6911,13 @@ def __iter__(self):
68666911
sage: it = iter(P)
68676912
sage: [next(it) for i in range(10)]
68686913
[[], [1], [2], [1, 1], [3], [2, 1], [4], [3, 1], [2, 2], [5]]
6914+
6915+
sage: P = Partitions(inner=[2,2], outer=[3,2,1])
6916+
sage: list(P)
6917+
[[2, 2], [3, 2], [2, 2, 1], [3, 2, 1]]
68696918
"""
68706919
n = 0
6871-
while True:
6920+
while n <= self._max_sum:
68726921
for p in Partitions(n, **self._constraints):
68736922
yield self.element_class(self, p)
68746923
n += 1
@@ -7608,7 +7657,7 @@ def __classcall_private__(cls, n, parts):
76087657
sage: list(Partitions(4,parts_in=vector(ZZ,[2,4])))
76097658
[[4], [2, 2]]
76107659
"""
7611-
parts = tuple(sorted(set(map(ZZ,parts))))
7660+
parts = tuple(sorted(set(map(ZZ, parts))))
76127661
return super().__classcall__(cls, Integer(n), parts)
76137662

76147663
def __init__(self, n, parts):
@@ -8356,6 +8405,34 @@ class Partitions_with_constraints(IntegerListsLex):
83568405
Element = Partition
83578406
options = Partitions.options
83588407

8408+
def __contains__(self, x):
8409+
"""
8410+
Check if ``x`` is contained in ``self``.
8411+
8412+
TESTS::
8413+
8414+
sage: P = Partitions(4, max_slope=-2)
8415+
sage: [3,1] in P
8416+
True
8417+
sage: [3,1,0] in P
8418+
True
8419+
sage: [2,2] in P
8420+
False
8421+
sage: [3,1,None] in P
8422+
False
8423+
8424+
sage: [1,3] in Partitions(4, min_slope=-1)
8425+
False
8426+
"""
8427+
# strip off trailing 0s
8428+
for i in range(len(x)-1, -1, -1):
8429+
if x[i] != 0:
8430+
x = x[:i+1]
8431+
break
8432+
else:
8433+
x = []
8434+
return super().__contains__(x)
8435+
83598436

83608437
######################
83618438
# Regular Partitions #

0 commit comments

Comments
 (0)