Skip to content

fix enumeration of implicitly finite sets of partitions #39948

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/sage/combinat/integer_lists/base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ from cpython.object cimport Py_LE, Py_EQ, Py_NE, Py_GE
from sage.misc.constant_function import ConstantFunction
from sage.structure.element cimport RingElement
from sage.rings.integer cimport Integer
from sage.rings.integer_ring import ZZ

Infinity = float('+inf')
MInfinity = float('-inf')
Expand Down Expand Up @@ -202,9 +203,19 @@ cdef class IntegerListsBackend():
sage: C = IntegerListsLex(n=2, max_length=3, min_slope=0)
sage: all(l in C for l in C) # indirect doctest
True

TESTS::

sage: [None, 2] in C
False

sage: [1/2, 3/2] in C
False
"""
if len(comp) < self.min_length or len(comp) > self.max_length:
return False
if not all(e in ZZ for e in comp):
return False
n = sum(comp)
if n < self.min_sum or n > self.max_sum:
return False
Expand Down
107 changes: 92 additions & 15 deletions src/sage/combinat/partition.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@

Finally, here are the partitions of `4` with ``[1,1,1]`` as an inner
bound (i. e., the partitions of `4` containing the partition ``[1,1,1]``).
Note that ``inner`` sets ``min_length`` to the length of its argument::
Note that ``inner`` sets ``min_length`` to the length of its argument,
interpreted as a partition::

sage: Partitions(4, inner=[1,1,1]).list()
[[2, 1, 1], [1, 1, 1, 1]]
Expand Down Expand Up @@ -6164,6 +6165,27 @@
"""
if n is infinity:
raise ValueError("n cannot be infinite")
if 'length' in kwargs and ('min_length' in kwargs or 'max_length' in kwargs):
raise ValueError("do not specify the length together with the minimal or maximal length")
# remove kwargs that specify the default value
if 'min_part' in kwargs and kwargs['min_part'] == 1:
del kwargs['min_part']
if 'max_slope' in kwargs and not kwargs['max_slope']:
del kwargs['max_slope']
# preprocess for UniqueRepresentation
if 'outer' in kwargs and not isinstance(kwargs['outer'], Partition):
m = infinity
kwargs['outer'] = [m for e in kwargs['outer']
if (m := min(m, e if e is infinity else ZZ(e))) > 0]
if kwargs['outer'] and kwargs['outer'][0] is infinity:
kwargs['outer'] = tuple(kwargs['outer'])
else:
kwargs['outer'] = Partition(kwargs['outer'])
if 'inner' in kwargs and not isinstance(kwargs['inner'], Partition):
m = ZZ.zero()
kwargs['inner'] = Partition(reversed([(m := max(m, e))
for e in reversed(kwargs['inner'])]))

if isinstance(n, (int, Integer)):
if not kwargs:
return Partitions_n(n)
Expand Down Expand Up @@ -6208,9 +6230,6 @@
+ "'ending', 'regular' and 'restricted' "
+ "cannot be combined with anything else")

if 'length' in kwargs and ('min_length' in kwargs or 'max_length' in kwargs):
raise ValueError("do not specify the length together with the minimal or maximal length")

if set(kwargs).issubset(['length', 'min_part', 'max_part',
'min_length', 'max_length']):
if 'length' in kwargs:
Expand Down Expand Up @@ -6239,7 +6258,8 @@

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

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

# min_part is at least 1, and it is 1 by default
Expand All @@ -6252,18 +6272,17 @@
raise ValueError("the minimum slope must be nonnegative")

if 'outer' in kwargs:
kwargs['ceiling'] = tuple(kwargs['outer'])
kwargs['max_length'] = min(len(kwargs['outer']),
kwargs.get('max_length', infinity))

kwargs['ceiling'] = tuple(kwargs['outer'])
del kwargs['outer']

if 'inner' in kwargs:
inner = [x for x in kwargs['inner'] if x > 0]
kwargs['floor'] = inner
kwargs['min_length'] = max(len(inner),
kwargs['floor'] = tuple(kwargs['inner'])
kwargs['min_length'] = max(len(kwargs['inner']),
kwargs.get('min_length', 0))
del kwargs['inner']

return Partitions_with_constraints(n, **kwargs)

if n is None or n is NN or n is NonNegativeIntegers():
Expand All @@ -6288,6 +6307,8 @@
elif 'max_part' in kwargs and 'max_length' in kwargs:
return PartitionsInBox(kwargs['max_length'], kwargs['max_part'])

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

raise ValueError("n must be an integer or be equal to one of "
Expand Down Expand Up @@ -6811,17 +6832,41 @@
"""
TESTS::

sage: TestSuite(sage.combinat.partition.Partitions_all_constrained(max_length=3)).run() # long time
sage: TestSuite(sage.combinat.partition.Partitions_all_constrained(max_length=3, max_slope=0)).run() # long time

sage: list(Partitions(max_part=4, max_slope=-3))
[[], [1], [2], [3], [4], [4, 1]]

sage: [pi for n in range(10) for pi in Partitions(n, max_part=4, max_slope=-3)]
[[], [1], [2], [3], [4], [4, 1]]
"""
self._constraints = kwargs
Partitions.__init__(self, is_infinite=True)
self._max_sum = infinity
if 'outer' in kwargs and kwargs['outer'] and kwargs['outer'][0] is not infinity:
self._max_sum = kwargs['outer'][0] * len(kwargs['outer'])
else:
if 'length' in kwargs:
max_length = kwargs['length']
elif 'max_length' in kwargs:
max_length = kwargs['max_length']
elif 'max_part' in kwargs and kwargs.get('max_slope', 0) < 0:
max_length = 1 + (kwargs['max_part'] - 1) // (-kwargs['max_slope'])
else:
max_length = infinity

Check warning on line 6855 in src/sage/combinat/partition.py

View check run for this annotation

Codecov / codecov/patch

src/sage/combinat/partition.py#L6855

Added line #L6855 was not covered by tests

if max_length is not infinity and 'max_part' in kwargs:
self._max_sum = kwargs['max_part'] * max_length

Partitions.__init__(self, is_infinite=self._max_sum is infinity)

def __contains__(self, x):
"""
Check if ``x`` is contained in ``self``.

TESTS::

sage: from sage.combinat.partition import Partitions_all_constrained
sage: P = Partitions_all_constrained(max_part=3, max_length=2)
sage: P = Partitions_all_constrained(max_part=3, max_length=2, max_slope=0)
sage: 1 in P
False
sage: Partition([2,1]) in P
Expand Down Expand Up @@ -6866,9 +6911,13 @@
sage: it = iter(P)
sage: [next(it) for i in range(10)]
[[], [1], [2], [1, 1], [3], [2, 1], [4], [3, 1], [2, 2], [5]]

sage: P = Partitions(inner=[2,2], outer=[3,2,1])
sage: list(P)
[[2, 2], [3, 2], [2, 2, 1], [3, 2, 1]]
"""
n = 0
while True:
while n <= self._max_sum:
for p in Partitions(n, **self._constraints):
yield self.element_class(self, p)
n += 1
Expand Down Expand Up @@ -7608,7 +7657,7 @@
sage: list(Partitions(4,parts_in=vector(ZZ,[2,4])))
[[4], [2, 2]]
"""
parts = tuple(sorted(set(map(ZZ,parts))))
parts = tuple(sorted(set(map(ZZ, parts))))
return super().__classcall__(cls, Integer(n), parts)

def __init__(self, n, parts):
Expand Down Expand Up @@ -8356,6 +8405,34 @@
Element = Partition
options = Partitions.options

def __contains__(self, x):
"""
Check if ``x`` is contained in ``self``.

TESTS::

sage: P = Partitions(4, max_slope=-2)
sage: [3,1] in P
True
sage: [3,1,0] in P
True
sage: [2,2] in P
False
sage: [3,1,None] in P
False

sage: [1,3] in Partitions(4, min_slope=-1)
False
"""
# strip off trailing 0s
for i in range(len(x)-1, -1, -1):
if x[i] != 0:
x = x[:i+1]
break
else:
x = []

Check warning on line 8433 in src/sage/combinat/partition.py

View check run for this annotation

Codecov / codecov/patch

src/sage/combinat/partition.py#L8433

Added line #L8433 was not covered by tests
return super().__contains__(x)


######################
# Regular Partitions #
Expand Down
Loading