Enumerated set of lists of integers with constraints: base classes¶
IntegerListsBackend
: base class for the Cython back-end of an enumerated set of lists of integers with specified constraints.Envelope
: a utility class for upper (lower) envelope of a function under constraints.
- class sage.combinat.integer_lists.base.Envelope[source]¶
Bases:
object
The (currently approximated) upper (lower) envelope of a function under the specified constraints.
INPUT:
f
– a function, list, or tuple; iff
is a list, it is considered as the functionf(i)=f[i]
, completed for larger \(i\) withf(i)=max_part
.min_part
,max_part
,min_slope
,max_slope
, … as forIntegerListsLex
(please consult for details).sign
– (+1 or -1) multiply the input values withsign
and multiply the output withsign
. Setting this to \(-1\) can be used to implement a lower envelope.
The upper envelope \(U(f)\) of \(f\) is the (pointwise) largest function which is bounded above by \(f\) and satisfies the
max_part
andmax_slope
conditions. Furthermore, fori,i+1<min_length
, the upper envelope also satisfies themin_slope
condition.Upon computing \(U(f)(i)\), all the previous values for \(j\leq i\) are computed and cached; in particular \(f(i)\) will be computed at most once for each \(i\).
Todo
This class is a good candidate for Cythonization, especially to get the critical path in
__call__
super fast.To get full envelopes, we would want both the
min_slope
andmax_slope
conditions to always be satisfied. This is only properly defined for the restriction of \(f\) to a finite interval \(0,..,k\), and depends on \(k\).This is the core “data structure” of
IntegerListsLex
. Improving the lookahead there essentially depends on having functions with a good complexity to compute the area below an envelope; and in particular how it evolves when increasing the length.
EXAMPLES:
sage: from sage.combinat.integer_lists import Envelope
>>> from sage.all import * >>> from sage.combinat.integer_lists import Envelope
Trivial upper and lower envelopes:
sage: f = Envelope([3,2,2]) sage: [f(i) for i in range(10)] [3, 2, 2, inf, inf, inf, inf, inf, inf, inf] sage: f = Envelope([3,2,2], sign=-1) sage: [f(i) for i in range(10)] [3, 2, 2, 0, 0, 0, 0, 0, 0, 0]
>>> from sage.all import * >>> f = Envelope([Integer(3),Integer(2),Integer(2)]) >>> [f(i) for i in range(Integer(10))] [3, 2, 2, inf, inf, inf, inf, inf, inf, inf] >>> f = Envelope([Integer(3),Integer(2),Integer(2)], sign=-Integer(1)) >>> [f(i) for i in range(Integer(10))] [3, 2, 2, 0, 0, 0, 0, 0, 0, 0]
A more interesting lower envelope:
sage: f = Envelope([4,1,5,3,5], sign=-1, min_part=2, min_slope=-1) sage: [f(i) for i in range(10)] [4, 3, 5, 4, 5, 4, 3, 2, 2, 2]
>>> from sage.all import * >>> f = Envelope([Integer(4),Integer(1),Integer(5),Integer(3),Integer(5)], sign=-Integer(1), min_part=Integer(2), min_slope=-Integer(1)) >>> [f(i) for i in range(Integer(10))] [4, 3, 5, 4, 5, 4, 3, 2, 2, 2]
Currently, adding
max_slope
has no effect:sage: f = Envelope([4,1,5,3,5], sign=-1, min_part=2, min_slope=-1, max_slope=0) sage: [f(i) for i in range(10)] [4, 3, 5, 4, 5, 4, 3, 2, 2, 2]
>>> from sage.all import * >>> f = Envelope([Integer(4),Integer(1),Integer(5),Integer(3),Integer(5)], sign=-Integer(1), min_part=Integer(2), min_slope=-Integer(1), max_slope=Integer(0)) >>> [f(i) for i in range(Integer(10))] [4, 3, 5, 4, 5, 4, 3, 2, 2, 2]
unless
min_length
is large enough:sage: f = Envelope([4,1,5,3,5], sign=-1, min_part=2, min_slope=-1, max_slope=0, min_length=2) sage: [f(i) for i in range(10)] [4, 3, 5, 4, 5, 4, 3, 2, 2, 2] sage: f = Envelope([4,1,5,3,5], sign=-1, min_part=2, min_slope=-1, max_slope=0, min_length=4) sage: [f(i) for i in range(10)] [5, 5, 5, 4, 5, 4, 3, 2, 2, 2] sage: f = Envelope([4,1,5,3,5], sign=-1, min_part=2, min_slope=-1, max_slope=0, min_length=5) sage: [f(i) for i in range(10)] [5, 5, 5, 5, 5, 4, 3, 2, 2, 2]
>>> from sage.all import * >>> f = Envelope([Integer(4),Integer(1),Integer(5),Integer(3),Integer(5)], sign=-Integer(1), min_part=Integer(2), min_slope=-Integer(1), max_slope=Integer(0), min_length=Integer(2)) >>> [f(i) for i in range(Integer(10))] [4, 3, 5, 4, 5, 4, 3, 2, 2, 2] >>> f = Envelope([Integer(4),Integer(1),Integer(5),Integer(3),Integer(5)], sign=-Integer(1), min_part=Integer(2), min_slope=-Integer(1), max_slope=Integer(0), min_length=Integer(4)) >>> [f(i) for i in range(Integer(10))] [5, 5, 5, 4, 5, 4, 3, 2, 2, 2] >>> f = Envelope([Integer(4),Integer(1),Integer(5),Integer(3),Integer(5)], sign=-Integer(1), min_part=Integer(2), min_slope=-Integer(1), max_slope=Integer(0), min_length=Integer(5)) >>> [f(i) for i in range(Integer(10))] [5, 5, 5, 5, 5, 4, 3, 2, 2, 2]
A non trivial upper envelope:
sage: f = Envelope([9,1,5,4], max_part=7, max_slope=2) sage: [f(i) for i in range(10)] [7, 1, 3, 4, 6, 7, 7, 7, 7, 7]
>>> from sage.all import * >>> f = Envelope([Integer(9),Integer(1),Integer(5),Integer(4)], max_part=Integer(7), max_slope=Integer(2)) >>> [f(i) for i in range(Integer(10))] [7, 1, 3, 4, 6, 7, 7, 7, 7, 7]
- adapt(m, j)[source]¶
Return this envelope adapted to an additional local constraint.
INPUT:
m
– nonnegative integer (starting value)j
– nonnegative integer (position)
This method adapts this envelope to the additional local constraint imposed by having a part \(m\) at position \(j\). Namely, this returns a function which computes, for any \(i>j\), the minimum of the ceiling function and the value restriction given by the slope conditions.
EXAMPLES:
sage: from sage.combinat.integer_lists import Envelope sage: f = Envelope(3) sage: g = f.adapt(1,1) sage: g is f True sage: [g(i) for i in range(10)] [3, 3, 3, 3, 3, 3, 3, 3, 3, 3] sage: f = Envelope(3, max_slope=1) sage: g = f.adapt(1,1) sage: [g(i) for i in range(10)] [0, 1, 2, 3, 3, 3, 3, 3, 3, 3]
>>> from sage.all import * >>> from sage.combinat.integer_lists import Envelope >>> f = Envelope(Integer(3)) >>> g = f.adapt(Integer(1),Integer(1)) >>> g is f True >>> [g(i) for i in range(Integer(10))] [3, 3, 3, 3, 3, 3, 3, 3, 3, 3] >>> f = Envelope(Integer(3), max_slope=Integer(1)) >>> g = f.adapt(Integer(1),Integer(1)) >>> [g(i) for i in range(Integer(10))] [0, 1, 2, 3, 3, 3, 3, 3, 3, 3]
Note that, in both cases above, the adapted envelope is only guaranteed to be valid for \(i>j\)! This is to leave potential room in the future for sharing similar adapted envelopes:
sage: g = f.adapt(0,0) sage: [g(i) for i in range(10)] [0, 1, 2, 3, 3, 3, 3, 3, 3, 3] sage: g = f.adapt(2,2) sage: [g(i) for i in range(10)] [0, 1, 2, 3, 3, 3, 3, 3, 3, 3] sage: g = f.adapt(3,3) sage: [g(i) for i in range(10)] [0, 1, 2, 3, 3, 3, 3, 3, 3, 3]
>>> from sage.all import * >>> g = f.adapt(Integer(0),Integer(0)) >>> [g(i) for i in range(Integer(10))] [0, 1, 2, 3, 3, 3, 3, 3, 3, 3] >>> g = f.adapt(Integer(2),Integer(2)) >>> [g(i) for i in range(Integer(10))] [0, 1, 2, 3, 3, 3, 3, 3, 3, 3] >>> g = f.adapt(Integer(3),Integer(3)) >>> [g(i) for i in range(Integer(10))] [0, 1, 2, 3, 3, 3, 3, 3, 3, 3]
Now with a lower envelope:
sage: f = Envelope(1, sign=-1, min_slope=-1) sage: g = f.adapt(2,2) sage: [g(i) for i in range(10)] [4, 3, 2, 1, 1, 1, 1, 1, 1, 1] sage: g = f.adapt(1,3) sage: [g(i) for i in range(10)] [4, 3, 2, 1, 1, 1, 1, 1, 1, 1]
>>> from sage.all import * >>> f = Envelope(Integer(1), sign=-Integer(1), min_slope=-Integer(1)) >>> g = f.adapt(Integer(2),Integer(2)) >>> [g(i) for i in range(Integer(10))] [4, 3, 2, 1, 1, 1, 1, 1, 1, 1] >>> g = f.adapt(Integer(1),Integer(3)) >>> [g(i) for i in range(Integer(10))] [4, 3, 2, 1, 1, 1, 1, 1, 1, 1]
- limit()[source]¶
Return a bound on the limit of
self
.OUTPUT: nonnegative integer or \(\infty\)
This returns some upper bound for the accumulation points of this upper envelope. For a lower envelope, a lower bound is returned instead.
In particular this gives a bound for the value of
self
at \(i\) for \(i\) large enough. Special case: for a lower envelop, and when the limit is \(\infty\), the envelope is guaranteed to tend to \(\infty\) instead.When
s=self.limit_start()
is finite, this bound is guaranteed to be valid for \(i>=s\).Sometimes it’s better to have a loose bound that starts early; sometimes the converse holds. At this point which specific bound and starting point is returned is not set in stone, in order to leave room for later optimizations.
EXAMPLES:
sage: from sage.combinat.integer_lists import Envelope sage: Envelope([4,1,5]).limit() inf sage: Envelope([4,1,5], max_part=2).limit() 2 sage: Envelope([4,1,5], max_slope=0).limit() 1 sage: Envelope(lambda x: 3, max_part=2).limit() 2
>>> from sage.all import * >>> from sage.combinat.integer_lists import Envelope >>> Envelope([Integer(4),Integer(1),Integer(5)]).limit() inf >>> Envelope([Integer(4),Integer(1),Integer(5)], max_part=Integer(2)).limit() 2 >>> Envelope([Integer(4),Integer(1),Integer(5)], max_slope=Integer(0)).limit() 1 >>> Envelope(lambda x: Integer(3), max_part=Integer(2)).limit() 2
Lower envelopes:
sage: Envelope(lambda x: 3, min_part=2, sign=-1).limit() 2 sage: Envelope([4,1,5], min_slope=0, sign=-1).limit() 5 sage: Envelope([4,1,5], sign=-1).limit() 0
>>> from sage.all import * >>> Envelope(lambda x: Integer(3), min_part=Integer(2), sign=-Integer(1)).limit() 2 >>> Envelope([Integer(4),Integer(1),Integer(5)], min_slope=Integer(0), sign=-Integer(1)).limit() 5 >>> Envelope([Integer(4),Integer(1),Integer(5)], sign=-Integer(1)).limit() 0
See also
- limit_start()[source]¶
Return from which \(i\) on the bound returned by
limit
holds.See also
limit()
for the precise specifications.EXAMPLES:
sage: from sage.combinat.integer_lists import Envelope sage: Envelope([4,1,5]).limit_start() 3 sage: Envelope([4,1,5], sign=-1).limit_start() 3 sage: Envelope([4,1,5], max_part=2).limit_start() 3 sage: Envelope(4).limit_start() 0 sage: Envelope(4, sign=-1).limit_start() 0 sage: Envelope(lambda x: 3).limit_start() == Infinity True sage: Envelope(lambda x: 3, max_part=2).limit_start() == Infinity True sage: Envelope(lambda x: 3, sign=-1, min_part=2).limit_start() == Infinity True
>>> from sage.all import * >>> from sage.combinat.integer_lists import Envelope >>> Envelope([Integer(4),Integer(1),Integer(5)]).limit_start() 3 >>> Envelope([Integer(4),Integer(1),Integer(5)], sign=-Integer(1)).limit_start() 3 >>> Envelope([Integer(4),Integer(1),Integer(5)], max_part=Integer(2)).limit_start() 3 >>> Envelope(Integer(4)).limit_start() 0 >>> Envelope(Integer(4), sign=-Integer(1)).limit_start() 0 >>> Envelope(lambda x: Integer(3)).limit_start() == Infinity True >>> Envelope(lambda x: Integer(3), max_part=Integer(2)).limit_start() == Infinity True >>> Envelope(lambda x: Integer(3), sign=-Integer(1), min_part=Integer(2)).limit_start() == Infinity True
- class sage.combinat.integer_lists.base.IntegerListsBackend[source]¶
Bases:
object
Base class for the Cython back-end of an enumerated set of lists of integers with specified constraints.
This base implements the basic operations, including checking for containment using
_contains()
, but not iteration. For iteration, subclass this class and implement an_iter()
method.EXAMPLES:
sage: from sage.combinat.integer_lists.base import IntegerListsBackend sage: L = IntegerListsBackend(6, max_slope=-1) sage: L._contains([3,2,1]) True
>>> from sage.all import * >>> from sage.combinat.integer_lists.base import IntegerListsBackend >>> L = IntegerListsBackend(Integer(6), max_slope=-Integer(1)) >>> L._contains([Integer(3),Integer(2),Integer(1)]) True