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; if- fis a list, it is considered as the function- f(i)=f[i], completed for larger \(i\) with- f(i)=max_part.
- min_part,- max_part,- min_slope,- max_slope, … as for- IntegerListsLex(please consult for details).
- sign– (+1 or -1) multiply the input values with- signand multiply the output with- sign. 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_partand- max_slopeconditions. Furthermore, for- i,i+1<min_length, the upper envelope also satisfies the- min_slopecondition.- 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_slopeand- max_slopeconditions 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_slopehas 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_lengthis 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 - selfat \(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 - limitholds.- 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