Path: blob/master/src/sage/numerical/linear_functions.pyx
8815 views
"""1Linear Functions and Constraints23This module implements linear functions (see :class:`LinearFunction`)4in formal variables and chained (in)equalities between them (see5:class:`LinearConstraint`). By convention, these are always written as6either equalities or less-or-equal. For example::78sage: p = MixedIntegerLinearProgram()9sage: x = p.new_variable()10sage: f = 1 + x[1] + 2*x[2]; f # a linear function111 + x_0 + 2*x_112sage: type(f)13<type 'sage.numerical.linear_functions.LinearFunction'>1415sage: c = (0 <= f); c # a constraint160 <= 1 + x_0 + 2*x_117sage: type(c)18<type 'sage.numerical.linear_functions.LinearConstraint'>1920Note that you can use this module without any reference to linear21programming, it only implements linear functions over a base ring and22constraints. However, for ease of demonstration we will always23construct them out of linear programs (see24:mod:`~sage.numerical.mip`).2526Constraints can be equations or (non-strict) inequalities. They can be27chained::2829sage: p = MixedIntegerLinearProgram()30sage: x = p.new_variable()31sage: x[0] == x[1] == x[2] == x[3]32x_0 == x_1 == x_2 == x_33334sage: ieq_01234 = x[0] <= x[1] <= x[2] <= x[3] <= x[4]35sage: ieq_0123436x_0 <= x_1 <= x_2 <= x_3 <= x_43738If necessary, the direction of inequality is flipped to always write39inqualities as less or equal::4041sage: x[5] >= ieq_0123442x_0 <= x_1 <= x_2 <= x_3 <= x_4 <= x_54344sage: (x[5]<=x[6]) >= ieq_0123445x_0 <= x_1 <= x_2 <= x_3 <= x_4 <= x_5 <= x_646sage: (x[5]<=x[6]) <= ieq_0123447x_5 <= x_6 <= x_0 <= x_1 <= x_2 <= x_3 <= x_44849TESTS:5051See :trac:`12091` ::5253sage: p = MixedIntegerLinearProgram()54sage: b = p.new_variable()55sage: b[0] <= b[1] <= 256x_0 <= x_1 <= 257sage: list(b[0] <= b[1] <= 2)58[x_0, x_1, 2]59sage: 1 >= b[1] >= 2*b[0]602*x_0 <= x_1 <= 161sage: b[2] >= b[1] >= 2*b[0]622*x_0 <= x_1 <= x_263"""646566#*****************************************************************************67# Copyright (C) 2012 Nathann Cohen <[email protected]>68# Copyright (C) 2012 Volker Braun <[email protected]>69#70# Distributed under the terms of the GNU General Public License (GPL)71# as published by the Free Software Foundation; either version 2 of72# the License, or (at your option) any later version.73# http://www.gnu.org/licenses/74#*****************************************************************************7576include "sage/ext/stdsage.pxi"77include "sage/ext/interrupt.pxi"78include "sage/ext/cdefs.pxi"79from cpython.object cimport *8081cdef extern from "limits.h":82long LONG_MAX838485from sage.structure.parent cimport Parent86from sage.structure.element cimport ModuleElement, Element87from sage.misc.cachefunc import cached_function88899091#*****************************************************************************92#93# Utility functions to test that something is a linear function / constraint94#95#*****************************************************************************9697def is_LinearFunction(x):98"""99Test whether ``x`` is a linear function100101INPUT:102103- ``x`` -- anything.104105OUTPUT:106107Boolean.108109EXAMPLES::110111sage: p = MixedIntegerLinearProgram()112sage: x = p.new_variable()113sage: from sage.numerical.linear_functions import is_LinearFunction114sage: is_LinearFunction(x[0] - 2*x[2])115True116sage: is_LinearFunction('a string')117False118"""119return isinstance(x, LinearFunction)120121def is_LinearConstraint(x):122"""123Test whether ``x`` is a linear constraint124125INPUT:126127- ``x`` -- anything.128129OUTPUT:130131Boolean.132133EXAMPLES::134135sage: p = MixedIntegerLinearProgram()136sage: x = p.new_variable()137sage: ieq = (x[0] <= x[1])138sage: from sage.numerical.linear_functions import is_LinearConstraint139sage: is_LinearConstraint(ieq)140True141sage: is_LinearConstraint('a string')142False143"""144return isinstance(x, LinearConstraint)145146147#*****************************************************************************148#149# Factory functions for the parents to ensure uniqueness150#151#*****************************************************************************152153@cached_function154def LinearFunctionsParent(base_ring):155"""156Return the parent for linear functions over ``base_ring``.157158The output is cached, so only a single parent is ever constructed159for a given base ring.160161INPUT:162163- ``base_ring`` -- a ring. The coefficient ring for the linear164funcitons.165166OUTPUT:167168The parent of the linear functions over ``base_ring``.169170EXAMPLES::171172sage: from sage.numerical.linear_functions import LinearFunctionsParent173sage: LinearFunctionsParent(QQ)174Linear functions over Rational Field175"""176return LinearFunctionsParent_class(base_ring)177178@cached_function179def LinearConstraintsParent(linear_functions_parent):180"""181Return the parent for linear functions over ``base_ring``.182183The output is cached, so only a single parent is ever constructed184for a given base ring.185186INPUT:187188- ``linear_functions_parent`` -- a189:class:`LinearFunctionsParent_class`. The type of linear190functions that the constraints are made out of.191192OUTPUT:193194The parent of the linear constraints with the given linear functions.195196EXAMPLES::197198sage: from sage.numerical.linear_functions import \199... LinearFunctionsParent, LinearConstraintsParent200sage: LF = LinearFunctionsParent(QQ)201sage: LinearConstraintsParent(LF)202Linear constraints over Rational Field203"""204return LinearConstraintsParent_class(linear_functions_parent)205206207208#*****************************************************************************209#210# Parent of linear functions211#212#*****************************************************************************213214cdef class LinearFunctionsParent_class(Parent):215r"""216The parent for all linear functions over a fixed base ring.217218.. warning::219220You should use :func:`LinearFunctionsParent` to construct221instances of this class.222223INPUT/OUTPUT:224225See :func:`LinearFunctionsParent`226227EXAMPLES::228229sage: from sage.numerical.linear_functions import LinearFunctionsParent_class230sage: LinearFunctionsParent_class231<type 'sage.numerical.linear_functions.LinearFunctionsParent_class'>232"""233234def __init__(self, base_ring):235"""236The Python constructor237238TESTS::239240sage: from sage.numerical.linear_functions import LinearFunctionsParent241sage: LinearFunctionsParent(RDF)242Linear functions over Real Double Field243"""244from sage.categories.modules_with_basis import ModulesWithBasis245Parent.__init__(self, base=base_ring, category=ModulesWithBasis(base_ring))246self._multiplication_symbol = '*'247248def set_multiplication_symbol(self, symbol='*'):249"""250Set the multiplication symbol when pretty-printing linear functions.251252INPUT:253254- ``symbol`` -- string, default: ``'*'``. The multiplication255symbol to be used.256257EXAMPLES::258259sage: p = MixedIntegerLinearProgram()260sage: x = p.new_variable()261sage: f = -1-2*x[0]-3*x[1]262sage: LF = f.parent()263sage: LF._get_multiplication_symbol()264'*'265sage: f266-1 - 2*x_0 - 3*x_1267sage: LF.set_multiplication_symbol(' ')268sage: f269-1 - 2 x_0 - 3 x_1270sage: LF.set_multiplication_symbol()271sage: f272-1 - 2*x_0 - 3*x_1273"""274self._multiplication_symbol = symbol275276cpdef _get_multiplication_symbol(self):277"""278Return the multiplication symbol.279280OUTPUT:281282String. By default, this is ``'*'``.283284EXAMPLES::285286sage: LF = MixedIntegerLinearProgram().linear_functions_parent()287sage: LF._get_multiplication_symbol()288'*'289"""290return self._multiplication_symbol291292def _repr_(self):293"""294Return as string representation295296EXAMPLES::297298sage: MixedIntegerLinearProgram().linear_functions_parent()299Linear functions over Real Double Field300"""301return 'Linear functions over '+str(self.base_ring())302303cpdef _element_constructor_(self, x):304"""305Construt a :class:`LinearFunction` from ``x``.306307EXAMPLES::308309sage: p = MixedIntegerLinearProgram()310sage: LF = p.linear_functions_parent()311sage: LF._element_constructor_(123)312123313sage: p(123) # indirect doctest314123315sage: type(_)316<type 'sage.numerical.linear_functions.LinearFunction'>317318sage: p_QQ = MixedIntegerLinearProgram(solver='ppl')319sage: LF_QQ = p_QQ.linear_functions_parent()320sage: f = LF({-1:1/2, 2:3/4}); f3210.5 + 0.75*x_2322sage: LF(f) is f323True324sage: LF_QQ(f)3251/2 + 3/4*x_2326sage: LF_QQ(f) is f327False328"""329if is_LinearFunction(x):330if x.parent() is self:331return x332else:333return LinearFunction(self, (<LinearFunction>x)._f)334return LinearFunction(self, x)335336cpdef _coerce_map_from_(self, R):337"""338Allow coercion of scalars into linear functions.339340INPUT:341342- ``R`` -- a ring.343344OUTPUT:345346Boolean. Whether there is a coercion map.347348EXAMPLES::349350sage: p = MixedIntegerLinearProgram()351sage: parent = p.linear_functions_parent()352sage: parent.coerce(int(2))3532354sage: parent._coerce_map_from_(int)355True356"""357if R in [int, float, long, complex]:358return True359from sage.rings.real_mpfr import mpfr_prec_min360from sage.rings.all import RealField361if RealField(mpfr_prec_min()).has_coerce_map_from(R):362return True363return False364365def _an_element_(self):366"""367Returns an element368369OUTPUT:370371A linear function.372373EXAMPLES::374375sage: p = MixedIntegerLinearProgram().linear_functions_parent()376sage: p._an_element_()3775*x_2 + 7*x_5378sage: p.an_element() # indirect doctest3795*x_2 + 7*x_5380"""381return self._element_constructor_({2:5, 5:7})382383384#*****************************************************************************385#386# Elements of linear functions387#388#*****************************************************************************389390cdef class LinearFunction(ModuleElement):391r"""392An elementary algebra to represent symbolic linear functions.393394.. warning::395396You should never instantiate :class:`LinearFunction`397manually. Use the element constructor in the parent398instead. For convenience, you can also call the399:class:`MixedIntegerLinearProgram` instance directly.400401EXAMPLES:402403For example, do this::404405sage: p = MixedIntegerLinearProgram()406sage: p({0 : 1, 3 : -8})407x_0 - 8*x_3408409or this::410411sage: parent = p.linear_functions_parent()412sage: parent({0 : 1, 3 : -8})413x_0 - 8*x_3414415instead of this::416417sage: from sage.numerical.linear_functions import LinearFunction418sage: LinearFunction(p.linear_functions_parent(), {0 : 1, 3 : -8})419x_0 - 8*x_3420"""421422def __init__(self, parent, f):423r"""424Constructor taking a dictionary or a numerical value as its argument.425426A linear function is represented as a dictionary. The427values are the coefficient of the variable represented428by the keys ( which are integers ). The key ``-1``429corresponds to the constant term.430431EXAMPLES:432433With a dictionary::434435sage: p = MixedIntegerLinearProgram()436sage: p({0 : 1, 3 : -8})437x_0 - 8*x_3438439Using the constructor with a numerical value::440441sage: p = MixedIntegerLinearProgram()442sage: p(25)44325444"""445ModuleElement.__init__(self, parent)446R = self.base_ring()447if isinstance(f, dict):448self._f = dict( (int(key), R(value)) for key, value in f.iteritems() )449else:450self._f = {-1: R(f)}451452cpdef iteritems(self):453"""454Iterate over the index, coefficient pairs455456OUTPUT:457458An iterator over the ``(key, coefficient)`` pairs. The keys459are integers indexing the variables. The key ``-1``460corresponds to the constant term.461462EXAMPLES::463464sage: p = MixedIntegerLinearProgram(solver = 'ppl')465sage: x = p.new_variable()466sage: f = 0.5 + 3/2*x[1] + 0.6*x[3]467sage: for id, coeff in f.iteritems():468... print 'id =', id, ' coeff =', coeff469id = 0 coeff = 3/2470id = 1 coeff = 3/5471id = -1 coeff = 1/2472"""473return self._f.iteritems()474475def dict(self):476r"""477Returns the dictionary corresponding to the Linear Function.478479OUTPUT:480481The linear function is represented as a dictionary. The value482are the coefficient of the variable represented by the keys (483which are integers ). The key ``-1`` corresponds to the484constant term.485486EXAMPLE::487488sage: p = MixedIntegerLinearProgram()489sage: lf = p({0 : 1, 3 : -8})490sage: lf.dict()491{0: 1.0, 3: -8.0}492"""493return dict(self._f)494495cpdef ModuleElement _add_(self, ModuleElement b):496r"""497Defining the + operator498499EXAMPLE::500501sage: p = MixedIntegerLinearProgram()502sage: p({0 : 1, 3 : -8}) + p({2 : 5, 3 : 2}) - 16503-16 + x_0 + 5*x_2 - 6*x_3504"""505e = dict(self._f)506for (id,coeff) in b.dict().iteritems():507e[id] = self._f.get(id,0) + coeff508P = self.parent()509return P(e)510511cpdef ModuleElement _neg_(self):512r"""513Defining the - operator (opposite).514515EXAMPLE::516517sage: p = MixedIntegerLinearProgram()518sage: - p({0 : 1, 3 : -8})519-1*x_0 + 8*x_3520"""521P = self.parent()522return P(dict([(id,-coeff) for (id, coeff) in self._f.iteritems()]))523524cpdef ModuleElement _sub_(self, ModuleElement b):525r"""526Defining the - operator (substraction).527528EXAMPLE::529530sage: p = MixedIntegerLinearProgram()531sage: p({2 : 5, 3 : 2}) - 3532-3 + 5*x_2 + 2*x_3533sage: p({0 : 1, 3 : -8}) - p({2 : 5, 3 : 2}) - 16534-16 + x_0 - 5*x_2 - 10*x_3535"""536e = dict(self._f)537for (id,coeff) in b.dict().iteritems():538e[id] = self._f.get(id,0) - coeff539P = self.parent()540return P(e)541542cpdef ModuleElement _rmul_(self, RingElement b):543r"""544Left multiplication by scalars545546EXAMPLE::547548sage: p = MixedIntegerLinearProgram()549sage: p({2 : 5, 3 : 2}) * 355015*x_2 + 6*x_3551"""552P = self.parent()553return P(dict([(id,b*coeff) for (id, coeff) in self._f.iteritems()]))554555cpdef ModuleElement _lmul_(self, RingElement b):556r"""557Right multiplication by scalars558559EXAMPLE::560561sage: p = MixedIntegerLinearProgram()562sage: 3 * p({2 : 5, 3 : 2})56315*x_2 + 6*x_3564"""565return self._rmul_(b)566567cpdef _acted_upon_(self, x, bint self_on_left):568"""569Act with scalars that do not have a natural coercion into570``self.base_ring()``571572EXAMPLES:573574Note that there is no coercion from ``RR`` to ``QQ``, but you575can explicitly convert them::576577sage: 1/2 * 0.65780.300000000000000579580If there were a coercion, then 0.6 would have been coerced into5816/10 and the result would have been rational. This is582undesirable, which is why there cannot be a coercion on the583level of the coefficient rings.584585By declaring an action of ``RR`` on the linear functions over586``QQ`` we make the following possible::587588sage: p = MixedIntegerLinearProgram(solver='ppl')589sage: p.base_ring()590Rational Field591sage: x = p.new_variable()592sage: x[0] * 0.65933/5*x_0594"""595R = self.base_ring()596try:597x_R = R(x)598except TypeError:599return None600return self._rmul_(x_R)601602def _coeff_formatter(self, coeff, constant_term=False):603"""604Pretty-print multiplicative coefficient ``x``605606OUTPUT:607608String, including a trailing space if the coefficient is not609one. Empty string otherwise.610611EXAMPLES::612613sage: p = MixedIntegerLinearProgram()614sage: f = p(1); type(f)615<type 'sage.numerical.linear_functions.LinearFunction'>616sage: f._coeff_formatter(1)617''618sage: f._coeff_formatter(1, constant_term=True)619'1'620sage: f._coeff_formatter(RDF(12.0))621'12*'622sage: f._coeff_formatter(RDF(12.3))623'12.3*'624625sage: q = MixedIntegerLinearProgram(solver='ppl')626sage: f = p(1)627sage: f._coeff_formatter(13/45)628'13/45*'629"""630R = self.base_ring()631if coeff == R.one() and not constant_term:632return ''633try:634from sage.rings.all import ZZ635coeff = ZZ(coeff) # print as integer if possible636except TypeError:637pass638if constant_term:639return str(coeff)640else:641return str(coeff) + self.parent()._get_multiplication_symbol()642643def _repr_(self):644r"""645Returns a string version of the linear function.646647EXAMPLE::648649sage: p = MixedIntegerLinearProgram(solver='GLPK')650sage: p({-1: -15, 2 : -5.1, 3 : 2/3})651-15 - 5.1*x_2 + 0.666666666667*x_3652sage: p = MixedIntegerLinearProgram(solver='ppl')653sage: p({-1: -15, 2 : -5.1, 3 : 2/3})654-15 - 51/10*x_2 + 2/3*x_3655"""656cdef dict d = dict(self._f)657cdef bint first = True658t = ""659660if d.has_key(-1):661coeff = d.pop(-1)662if coeff!=0:663t = self._coeff_formatter(coeff, constant_term=True)664first = False665666cdef list l = sorted(d.items())667for id,coeff in l:668sign = cmp(coeff,0)669if sign == 0:670continue671if not first:672if sign == -1:673t += ' - '674if sign == +1:675t += ' + '676t += self._coeff_formatter(abs(coeff)) + 'x_' + str(id)677else:678t += self._coeff_formatter(coeff) + 'x_' + str(id)679first = False680681if first:682return '0'683else:684return t685686cpdef is_zero(self):687"""688Test whether ``self`` is zero.689690OUTPUT:691692Boolean.693694EXAMPLES::695696sage: p = MixedIntegerLinearProgram()697sage: x = p.new_variable()698sage: (x[1] - x[1] + 0*x[2]).is_zero()699True700"""701for coeff in self._f.values():702if not coeff.is_zero():703return False704return True705706cpdef equals(LinearFunction left, LinearFunction right):707"""708Logically compare ``left`` and ``right``.709710OUTPUT:711712Boolean.713714EXAMPLES::715716sage: p = MixedIntegerLinearProgram()717sage: x = p.new_variable()718sage: (x[1] + 1).equals(3/3 + 1*x[1] + 0*x[2])719True720"""721return (left-right).is_zero()722723def __richcmp__(left, right, int op):724"""725Override the rich comparison.726727The Sage framework sometimes expects that rich comparison728results in a boolean value, but we want to return729:class:`~sage.numerical.linear_functions.LinearConstraint`730objects.731732EXAMPLES::733734sage: p = MixedIntegerLinearProgram()735sage: x = p.new_variable()736sage: x[0].__le__(x[1]) # indirect doctest737x_0 <= x_1738"""739return (<LinearFunction>left)._richcmp(right, op)740741cdef _richcmp(left, right, int op):742"""743Create a inequality or equality object.744745EXAMPLE::746747sage: p = MixedIntegerLinearProgram()748sage: from sage.numerical.linear_functions import LinearFunction749sage: p({2 : 5, 3 : 2}) <= p({2 : 3, 9 : 2})7505*x_2 + 2*x_3 <= 3*x_2 + 2*x_9751752sage: p({2 : 5, 3 : 2}) >= p({2 : 3, 9 : 2})7533*x_2 + 2*x_9 <= 5*x_2 + 2*x_3754755sage: p({2 : 5, 3 : 2}) == p({2 : 3, 9 : 2})7565*x_2 + 2*x_3 == 3*x_2 + 2*x_9757758sage: p({2 : 5, 3 : 2}) < p({2 : 3, 9 : 2})759Traceback (most recent call last):760...761ValueError: strict < is not allowed, use <= instead.762763sage: p({2 : 5, 3 : 2}) > p({2 : 3, 9 : 2})764Traceback (most recent call last):765...766ValueError: strict > is not allowed, use >= instead.767768TESTS::769770sage: cm = sage.structure.element.get_coercion_model()771sage: cm.explain(10, p(1), operator.le)772Coercion on left operand via773Conversion map:774From: Integer Ring775To: Linear functions over Real Double Field776Arithmetic performed after coercions.777Result lives in Linear functions over Real Double Field778Linear functions over Real Double Field779780sage: x = p.new_variable()781sage: operator.le(10, x[0])78210 <= x_0783sage: x[0] <= 1784x_0 <= 1785sage: x[0] >= 17861 <= x_0787sage: 1 <= x[0]7881 <= x_0789sage: 1 >= x[0]790x_0 <= 1791"""792LF = left.parent()793LC = LinearConstraintsParent(LF)794equality = (op == Py_EQ)795cdef LinearConstraint left_constraint = LC(left, equality=equality)796cdef LinearConstraint right_constraint = LC(right, equality=equality)797if op == Py_LT:798raise ValueError("strict < is not allowed, use <= instead.")799elif op == Py_EQ:800return left_constraint._richcmp(right_constraint, op)801elif op == Py_GT:802raise ValueError("strict > is not allowed, use >= instead.")803elif op == Py_LE:804return left_constraint._richcmp(right_constraint, op)805elif op == Py_NE:806raise ValueError("inequality != is not allowed, use one of <=, ==, >=.")807elif op == Py_GE:808return left_constraint._richcmp(right_constraint, op)809else:810assert(False) # unreachable811812def __hash__(self):813r"""814Return a hash.815816EXAMPLES::817818sage: p = MixedIntegerLinearProgram()819sage: f = p({2 : 5, 3 : 2})820sage: f.__hash__() # random output821103987752822sage: d = {}823sage: d[f] = 3824"""825# see _cmp_c_impl() if you want to change the hash function826return id(self) % LONG_MAX827828def __cmp__(left, right):829"""830Part of the comparison framework.831832EXAMPLES::833834sage: p = MixedIntegerLinearProgram()835sage: f = p({2 : 5, 3 : 2})836sage: cmp(f, f)8370838"""839return (<Element>left)._cmp(right)840841cdef int _cmp_c_impl(left, Element right) except -2:842"""843Implement comparison of two linear functions.844845EXAMPLES::846847sage: p = MixedIntegerLinearProgram()848sage: f = p({2 : 5, 3 : 2})849sage: cmp(f, f)8500851sage: abs(cmp(f, f+0)) # since we are comparing by id()8521853sage: abs(cmp(f, f+1))8541855sage: len(set([f, f]))8561857sage: len(set([f, f+0]))8582859sage: len(set([f, f+1]))8602861"""862# Note: if you want to implement smarter comparison, you also863# need to change __hash__(). The comparison function must864# satisfy cmp(x,y)==0 => hash(x)==hash(y)865return cmp(id(left), id(right))866867#*****************************************************************************868#869# Parent of linear constraints870#871#*****************************************************************************872873cdef class LinearConstraintsParent_class(Parent):874"""875Parent for :class:`LinearConstraint`876877.. warning::878879This class has no reason to be instanciated by the user, and880is meant to be used by instances of881:class:`MixedIntegerLinearProgram`. Also, use the882:func:`LinearConstraintsParent` factory function.883884INPUT/OUTPUT:885886See :func:`LinearFunctionsParent`887888EXAMPLES::889890sage: p = MixedIntegerLinearProgram()891sage: LC = p.linear_constraints_parent(); LC892Linear constraints over Real Double Field893sage: from sage.numerical.linear_functions import LinearConstraintsParent894sage: LinearConstraintsParent(p.linear_functions_parent()) is LC895True896"""897898def __init__(self, linear_functions_parent):899"""900The Python constructor901902INPUT/OUTPUT:903904See :func:`LinearFunctionsParent`905906TESTS::907908sage: from sage.numerical.linear_functions import LinearFunctionsParent909sage: LF = LinearFunctionsParent(RDF)910sage: from sage.numerical.linear_functions import LinearConstraintsParent911sage: LinearConstraintsParent(LF)912Linear constraints over Real Double Field913"""914Parent.__init__(self)915self._LF = linear_functions_parent916917def linear_functions_parent(self):918"""919Return the parent for the linear functions920921EXAMPLES::922923sage: LC = MixedIntegerLinearProgram().linear_constraints_parent()924sage: LC.linear_functions_parent()925Linear functions over Real Double Field926"""927return self._LF928929def _repr_(self):930"""931Return a string representation932933OUTPUT:934935String.936937EXAMPLES::938939sage: MixedIntegerLinearProgram().linear_constraints_parent()940Linear constraints over Real Double Field941"""942return 'Linear constraints over '+str(self.linear_functions_parent().base_ring())943944cpdef _element_constructor_(self, left, right=None, equality=False):945"""946Construt a :class:`LinearConstraint`.947948INPUT:949950- ``left`` -- a :class:`LinearFunction`, or something that can951be converted into one, a list/tuple of952:class:`LinearFunction`, or an existing953:class:`LinearConstraint`.954955- ``right`` -- a :class:`LinearFunction` or ``None``956(default).957958- ``equality`` -- boolean (default: ``True``). Whether to959construct an equation or an inequality.960961OUTPUT:962963The :class:`LinearConstraint` constructed from the input data.964965EXAMPLES::966967sage: p = MixedIntegerLinearProgram()968sage: LC = p.linear_constraints_parent()969sage: LC._element_constructor_(1, 2)9701 <= 2971972sage: x = p.new_variable()973sage: LC([x[0], x[1], x[2]])974x_0 <= x_1 <= x_2975976sage: LC([x[0], x[1], x[2]], equality=True)977x_0 == x_1 == x_2978979sage: type(_)980<type 'sage.numerical.linear_functions.LinearConstraint'>981982TESTS::983984sage: inequality = LC([x[0], 1/2*x[1], 3/4*x[2]]); inequality985x_0 <= 0.5*x_1 <= 0.75*x_2986sage: LC(inequality) is inequality987True988sage: p_QQ = MixedIntegerLinearProgram(solver='ppl')989sage: LC_QQ = p_QQ.linear_constraints_parent()990sage: LC_QQ(inequality)991x_0 <= 1/2*x_1 <= 3/4*x_2992sage: LC_QQ(inequality) is inequality993False994"""995if right is None and is_LinearConstraint(left):996if (left.parent() is self) and (left.is_equation() == equality):997return left998else:999return LinearConstraint(self, (<LinearConstraint>left).constraints,1000equality=equality)1001if right is None:1002if isinstance(left, (list,tuple)):1003return LinearConstraint(self, left, equality=equality)1004else:1005return LinearConstraint(self, [left], equality=equality)1006else:1007return LinearConstraint(self, [left, right], equality=equality)10081009cpdef _coerce_map_from_(self, R):1010"""1011Allow coercion of scalars into linear functions.10121013EXAMPLES::10141015sage: p = MixedIntegerLinearProgram()1016sage: parent = p.linear_constraints_parent()1017sage: parent.coerce(int(2))1018trivial constraint starting with 21019sage: parent._coerce_map_from_(int)1020True1021"""1022return self.linear_functions_parent().has_coerce_map_from(R)10231024def _an_element_(self):1025"""1026Returns an element10271028EXAMPLES::10291030sage: p = MixedIntegerLinearProgram().linear_functions_parent()1031sage: p._an_element_()10325*x_2 + 7*x_51033sage: p.an_element() # indirect doctest10345*x_2 + 7*x_51035"""1036LF = self.linear_functions_parent()1037return self(0) <= LF.an_element()1038103910401041#*****************************************************************************1042#1043# Elements of linear constraints1044#1045#*****************************************************************************10461047_chained_comparator_hack_search = None1048_chained_comparator_hack_replace = None10491050cdef class LinearConstraint(Element):1051"""1052A class to represent formal Linear Constraints.10531054A Linear Constraint being an inequality between1055two linear functions, this class lets the user1056write ``LinearFunction1 <= LinearFunction2``1057to define the corresponding constraint, which1058can potentially involve several layers of such1059inequalities (``(A <= B <= C``), or even equalities1060like ``A == B``.10611062Trivial constraints (meaning that they have only one term and no1063relation) are also allowed. They are required for the coercion1064system to work.10651066.. warning::10671068This class has no reason to be instanciated by the user, and1069is meant to be used by instances of1070:class:`MixedIntegerLinearProgram`.10711072INPUT:10731074- ``parent`` -- the parent, a :class:`LinearConstraintsParent_class`10751076- ``terms`` -- a list/tuple/iterable of two or more linear1077functions (or things that can be converted into linear1078functions).10791080- ``equality`` -- boolean (default: ``False``). Whether the terms1081are the entries of a chained less-or-equal (``<=``) inequality1082or a chained equality.10831084EXAMPLE::10851086sage: p = MixedIntegerLinearProgram()1087sage: b = p.new_variable()1088sage: b[2]+2*b[3] <= b[8]-51089x_0 + 2*x_1 <= -5 + x_21090"""10911092def __init__(self, parent, terms, equality=False):1093r"""1094Constructor for ``LinearConstraint``10951096INPUT:10971098See :class:`LinearConstraint`.10991100EXAMPLE::11011102sage: p = MixedIntegerLinearProgram()1103sage: b = p.new_variable()1104sage: b[2]+2*b[3] <= b[8]-51105x_0 + 2*x_1 <= -5 + x_21106"""1107assert len(terms) > 01108super(LinearConstraint, self).__init__(parent)1109self.equality = equality1110LF = parent.linear_functions_parent()1111self.constraints = [ LF(term) for term in terms ]11121113cpdef equals(LinearConstraint left, LinearConstraint right):1114"""1115Compare ``left`` and ``right``.11161117OUTPUT:11181119Boolean. Whether all terms of ``left`` and ``right`` are1120equal. Note that this is stronger than mathematical1121equivalence of the relations.11221123EXAMPLES::11241125sage: p = MixedIntegerLinearProgram()1126sage: x = p.new_variable()1127sage: (x[1] + 1 >= 2).equals(3/3 + 1*x[1] + 0*x[2] >= 8/4)1128True1129sage: (x[1] + 1 >= 2).equals(x[1] + 1-1 >= 1-1)1130False1131"""1132if len(left.constraints) != len(right.constraints):1133return False1134if left.equality != right.equality:1135return False1136cdef LinearFunction l, r1137for i in range(len(left.constraints)):1138l = <LinearFunction>(left.constraints[i])1139r = <LinearFunction>(right.constraints[i])1140if not l.equals(r):1141return False1142return True11431144cdef LinearConstraint _chained_comparator_hack_part1(LinearConstraint left, LinearConstraint right):1145"""1146Evil hack to allow chained constraints11471148Python translates ``x < y < z`` into:11491150.. code-block:: python11511152temp = x <= y # calls x.__richcmp__(y)1153if temp: # calls temp.__nonzero__()1154return y <= z # calls y.__richcmp__(z)1155else:1156return temp11571158but we would like ``x<=y<=z`` as output. The trick to make it1159work is to store ``y`` in the first call to ``__richcmp__()``1160and ``temp`` in the call to ``__nonzero__()``. Then we can1161replace ``y`` by ``x<=y`` in the second call to1162``__richcmp__``.11631164This function implements the first part of this hack, to be1165called from :meth:`__richcmp__`.1166"""1167# print '__richcmp__', left, ' compared with', right1168global _chained_comparator_hack_search1169global _chained_comparator_hack_replace1170cdef LinearConstraint search = _chained_comparator_hack_search1171cdef LinearConstraint replace = _chained_comparator_hack_replace1172_chained_comparator_hack_search = right1173if replace is None:1174return left1175assert search is not None1176if search.equals(left):1177_chained_comparator_hack_replace = None1178return replace1179else:1180return left11811182cdef _chained_comparator_hack_part2(self):1183"""1184This function implements the first part of this hack, to be1185called from :meth:`__nonzero__`.1186"""1187# print '__nonzero__', self.constraints1188global _chained_comparator_hack_replace1189_chained_comparator_hack_replace = self11901191def is_equation(self):1192"""1193Whether the constraint is a chained equation11941195OUTPUT:11961197Boolean.11981199EXAMPLES::12001201sage: p = MixedIntegerLinearProgram()1202sage: b = p.new_variable()1203sage: (b[0] == b[1]).is_equation()1204True1205sage: (b[0] <= b[1]).is_equation()1206False1207"""1208return self.equality12091210def is_less_or_equal(self):1211"""1212Whether the constraint is a chained less-or_equal inequality12131214OUTPUT:12151216Boolean.12171218EXAMPLES::12191220sage: p = MixedIntegerLinearProgram()1221sage: b = p.new_variable()1222sage: (b[0] == b[1]).is_less_or_equal()1223False1224sage: (b[0] <= b[1]).is_less_or_equal()1225True1226"""1227return not self.equality12281229def is_trivial(self):1230"""1231Test whether the constraint is trivial.12321233EXAMPLES::12341235sage: p = MixedIntegerLinearProgram()1236sage: LC = p.linear_constraints_parent()1237sage: ieq = LC(1,2); ieq12381 <= 21239sage: ieq.is_trivial()1240False12411242sage: ieq = LC(1); ieq1243trivial constraint starting with 11244sage: ieq.is_trivial()1245True1246"""1247return len(self.constraints) < 212481249def __iter__(self):1250"""1251Iterate over the terms of the chained (in)-equality12521253OUTPUT:12541255A generator yielding the individual terms of the constraint in1256left-to-right order.12571258EXAMPLES::12591260sage: p = MixedIntegerLinearProgram()1261sage: b = p.new_variable()1262sage: ieq = 1 <= b[0] <= b[2] <= 3 <= b[3]; ieq12631 <= x_0 <= x_1 <= 3 <= x_21264sage: list(ieq)1265[1, x_0, x_1, 3, x_2]1266sage: for term in ieq:1267... print term126811269x_01270x_1127131272x_21273"""1274for term in self.constraints:1275yield term12761277def equations(self):1278"""1279Iterate over the unchained(!) equations12801281OUTPUT:12821283An iterator over pairs ``(lhs, rhs)`` such that the individual1284equations are ``lhs == rhs``.12851286EXAMPLES::12871288sage: p = MixedIntegerLinearProgram()1289sage: b = p.new_variable()1290sage: eqns = 1 == b[0] == b[2] == 3 == b[3]; eqns12911 == x_0 == x_1 == 3 == x_212921293sage: for lhs, rhs in eqns.equations():1294... print str(lhs) + ' == ' + str(rhs)12951 == x_01296x_0 == x_11297x_1 == 312983 == x_21299"""1300if not self.is_equation() or self.is_trivial():1301raise StopIteration1302term_iter = iter(self)1303lhs = term_iter.next()1304rhs = term_iter.next()1305while True:1306yield (lhs, rhs)1307lhs = rhs1308rhs = term_iter.next()13091310def inequalities(self):1311"""1312Iterate over the unchained(!) inequalities13131314OUTPUT:13151316An iterator over pairs ``(lhs, rhs)`` such that the individual1317equations are ``lhs <= rhs``.13181319EXAMPLES::13201321sage: p = MixedIntegerLinearProgram()1322sage: b = p.new_variable()1323sage: ieq = 1 <= b[0] <= b[2] <= 3 <= b[3]; ieq13241 <= x_0 <= x_1 <= 3 <= x_213251326sage: for lhs, rhs in ieq.inequalities():1327... print str(lhs) + ' <= ' + str(rhs)13281 <= x_01329x_0 <= x_11330x_1 <= 313313 <= x_21332"""1333if not self.is_less_or_equal() or self.is_trivial():1334raise StopIteration1335term_iter = iter(self)1336lhs = term_iter.next()1337rhs = term_iter.next()1338while True:1339yield (lhs, rhs)1340lhs = rhs1341rhs = term_iter.next()13421343def _repr_(self):1344r"""1345Returns a string representation of the constraint.13461347OUTPUT:13481349String.13501351EXAMPLE::13521353sage: p = MixedIntegerLinearProgram()1354sage: b = p.new_variable()1355sage: b[3] <= b[8] + 91356x_0 <= 9 + x_113571358sage: LC = p.linear_constraints_parent()1359sage: LC(b[3], b[8] + 9)1360x_0 <= 9 + x_11361sage: LC(b[3])1362trivial constraint starting with x_01363"""1364comparator = ( ' == ' if self.equality else ' <= ' )1365result = comparator.join(map(str, self))1366if self.is_trivial():1367return 'trivial constraint starting with '+result1368return result13691370def __nonzero__(self):1371"""1372Part of the hack to allow chained (in)equalities13731374EXAMPLES::13751376sage: p = MixedIntegerLinearProgram()1377sage: b = p.new_variable()1378sage: ieq = (b[3] <= b[8] + 9)1379sage: ieq <= ieq <= ieq1380x_0 <= 9 + x_1 <= x_0 <= 9 + x_1 <= x_0 <= 9 + x_11381"""1382self._chained_comparator_hack_part2()1383return True13841385def __richcmp__(left, right, int op):1386"""1387Override the rich comparison.13881389The Sage framework sometimes expects that rich comparison1390results in a boolean value, but we want to return1391:class:`~sage.numerical.linear_functions.LinearConstraint`1392objects.13931394EXAMPLES::13951396sage: p = MixedIntegerLinearProgram()1397sage: b = p.new_variable()1398sage: b[0] <= b[1] <= b[2] <= b[3]1399x_0 <= x_1 <= x_2 <= x_31400sage: b[0] <= 1 <= b[1] <= 2 <= b[2] <= 31401x_0 <= 1 <= x_1 <= 2 <= x_2 <= 31402"""1403return (<LinearConstraint>left)._richcmp(right, op)14041405cdef _richcmp(py_left, py_right, int op):1406"""1407Chain (in)equalities1408"""1409# print 'richcmp', py_left, ', ', py_right1410LC = py_left.parent()1411if not is_LinearConstraint(py_right):1412py_right = LC(py_right, equality=py_left.is_equation())1413elif py_right.parent() is not LC:1414py_right = LC(py_right.constraints, equality=py_left.is_equation())1415cdef LinearConstraint right = <LinearConstraint>py_right1416cdef LinearConstraint left = py_left._chained_comparator_hack_part1(right)1417if op == Py_LT:1418raise ValueError("strict < is not allowed, use <= instead.")1419elif op == Py_EQ:1420if not (left.is_equation() and right.is_equation()):1421raise ValueError("can only chain together equations")1422return LC(left.constraints + right.constraints, equality=True)1423elif op == Py_GT:1424raise ValueError("strict > is not allowed, use >= instead.")1425elif op == Py_LE:1426if not (left.is_less_or_equal() and right.is_less_or_equal()):1427raise ValueError("can only chain together inequalities")1428return LC(left.constraints + right.constraints)1429elif op == Py_NE:1430raise ValueError("inequality != is not allowed, use one of <=, ==, >=.")1431elif op == Py_GE:1432if not (left.is_less_or_equal() and right.is_less_or_equal()):1433raise ValueError("can only chain together inequalities")1434return LC(right.constraints + left.constraints)1435else:1436assert(False) # unreachable143714381439