Ticket #7311: trac_7311.patch

File trac_7311.patch, 18.2 KB (added by ncohen, 11 years ago)
  • sage/numerical/mip.pxd

    diff -r 46b1ee5997ac -r 5db77e513bf7 sage/numerical/mip.pxd
    a b  
    11cdef extern from *:
    22    ctypedef double* const_double_ptr "const double*"
     3
     4
  • sage/numerical/mip.pyx

    diff -r 46b1ee5997ac -r 5db77e513bf7 sage/numerical/mip.pyx
    a b  
    44
    55include "../ext/stdsage.pxi"
    66include "../ext/interrupt.pxi"
    7 from copy import copy
     7from copy import deepcopy
    88
    99class MixedIntegerLinearProgram:
    1010    r"""
     
    612612        # and do not care about any function being optimal.
    613613
    614614        if obj != None:
    615             f = obj.f
     615            f = obj.dict()
    616616        else:
    617617            return None
    618618       
     
    624624
    625625    def add_constraint(self, linear_function, max=None, min=None, name=None):
    626626        r"""
    627         Adds a constraint to the ``MixedIntegerLinearProgram``.
     627        Adds a constraint to self.
    628628
    629629        INPUT:
    630630
    631         - ``consraint`` -- A linear function.
     631        - ``linear_function`` -- Two different types of arguments are possible:
     632
     633            - A linear function. In this case, arguments ``min`` or ``max``
     634              have to be specified.
     635
     636            - A linear constraint of the form ``A <= B``, ``A >= B``,
     637              ``A <= B <= C``, ``A >= B >= C`` or ``A == B``. In this
     638              case, arguments ``min`` and ``max`` will be ignored.
     639
    632640        - ``max`` -- An upper bound on the constraint (set to ``None``
    633           by default).
    634         - ``min`` -- A lower bound on the constraint.
     641          by default). This must be a numerical value.
     642
     643        - ``min`` -- A lower bound on the constraint.  This must be a
     644          numerical value
     645
    635646        - ``name`` -- A name for the constraint.
    636647
    637648        EXAMPLE:
     
    647658              x is Real (min = 0, max = None)
    648659              y is Real (min = 0, max = None)
    649660
    650         This linear program can be solved as follows::
     661        It can be solved as follows::
    651662
    652663            sage: p = MixedIntegerLinearProgram(maximization=True)
    653664            sage: x = p.new_variable()
     
    657668            sage: p.solve()     # optional - requires Glpk or COIN-OR/CBC
    658669            6.6666666666666661
    659670
     671        There are two different ways to add the constraint
     672        ``x[5] + 3*x[7] <= x[6] + 3`` to self.
     673
     674        The first one consists in giving ``add_constraint`` this
     675        very expression::
     676
     677            sage: p.add_constraint( x[5] + 3*x[7] <= x[6] + 3 )
     678
     679        The second (slightly more efficient) one is to use the
     680        arguments ``min`` or ``max``, which can only be numerical
     681        values::
     682
     683            sage: p.add_constraint( x[5] + 3*x[7] - x[6], max = 3 )
     684
     685        One can also define double-bounds or equality using symbols
     686        ``<=``, ``>=`` and ``==``::
     687
     688            sage: p.add_constraint( x[5] + 3*x[7] == x[6] + 3 )
     689            sage: p.add_constraint( x[5] + 3*x[7] <= x[6] + 3 <= x[8] + 27 )
     690
     691        The previous program can be rewritten::
     692
     693            sage: p = MixedIntegerLinearProgram(maximization=True)
     694            sage: x = p.new_variable()
     695            sage: p.set_objective(x[1] + 5*x[2])
     696            sage: p.add_constraint(x[1] + 0.2*x[2] <= 4)
     697            sage: p.add_constraint(1.5*x[1] + 3*x[2] <= 4)
     698            sage: p.solve()     # optional - requires Glpk or COIN-OR/CBC
     699            6.6666666666666661
     700
     701
    660702        TESTS::
    661703
    662704            sage: p=MixedIntegerLinearProgram()
    663705            sage: p.add_constraint(sum([]),min=2)
    664706        """
    665 
    666 
    667         if linear_function==0:
     707        if linear_function is None or linear_function is 0:
    668708            return None
    669709
    670         f = linear_function.f
     710        if isinstance(linear_function, LinearFunction):
    671711
    672         self._constraints_name.append(name)
     712            f = linear_function.dict()
    673713
    674         constant_coefficient = f.pop(-1,0)
     714            self._constraints_name.append(name)
    675715
    676         # We do not want to ignore the constant coefficient
    677         max = (max-constant_coefficient) if max != None else None
    678         min = (min-constant_coefficient) if min != None else None
     716            constant_coefficient = f.get(-1,0)
    679717
     718            # We do not want to ignore the constant coefficient
     719            max = (max - constant_coefficient) if max != None else None
     720            min = (min - constant_coefficient) if min != None else None
    680721
    681         c=len(self._constraints_bounds_min)
     722            c = len(self._constraints_bounds_min)
    682723
    683         for (v,coeff) in f.iteritems():
    684             self._constraints_matrix_i.append(c)
    685             self._constraints_matrix_j.append(v)
    686             self._constraints_matrix_values.append(coeff)
     724            for (v,coeff) in f.iteritems():
     725                if v!=-1:
     726                    self._constraints_matrix_i.append(c)
     727                    self._constraints_matrix_j.append(v)
     728                    self._constraints_matrix_values.append(coeff)
    687729
    688         self._constraints_bounds_max.append(max)
    689         self._constraints_bounds_min.append(min)
     730            self._constraints_bounds_max.append(max)
     731            self._constraints_bounds_min.append(min)
    690732
     733        elif isinstance(linear_function,LinearConstraint):
     734            functions = linear_function.constraints
     735           
     736            if linear_function.equality:
     737                self.add_constraint(functions[0] - functions[1], min=0, max=0, name=name)
    691738
     739            elif len(functions) == 2:
     740                self.add_constraint(functions[0] - functions[1], max=0, name=name)
     741
     742            else:
     743                self.add_constraint(functions[0] - functions[1], max=0, name=name)
     744                self.add_constraint(functions[1] - functions[2], max=0, name=name)
    692745
    693746    def set_binary(self, e):
    694747        r"""
     
    12841337            sage: v._update_variables_name()
    12851338        """
    12861339
    1287         if prefix==None:
    1288             prefix=self._name
     1340        if prefix == None:
     1341            prefix = self._name
    12891342
    1290         if self._dim==1:
     1343        if self._dim == 1:
    12911344            for (k,v) in self._dict.iteritems():
    1292                 self._p._variables_name[self._p._variables[v]]=prefix+"["+str(k)+"]"
     1345                self._p._variables_name[self._p._variables[v]]=prefix + "[" + str(k) + "]"
    12931346        else:
    12941347            for v in self._dict.itervalues():
    1295                 v._update_variables_name(prefix=prefix+"["+str(k)+"]")
     1348                v._update_variables_name(prefix=prefix + "[" + str(k) + "]")
    12961349               
    12971350               
    12981351
     
    13111364            sage: v
    13121365            MIPVariable of dimension 3.
    13131366        """
    1314         return "MIPVariable of dimension "+str(self._dim)+"."
     1367        return "MIPVariable of dimension " + str(self._dim) + "."
    13151368
    13161369    def keys(self):
    13171370        r"""
     
    13771430
    13781431    def __init__(self,f):
    13791432        r"""
    1380         Constructor taking a dictionary as its argument.
     1433        Constructor taking a dictionary or a numerical value as its argument.
    13811434
    13821435        A linear function is represented as a dictionary. The
    1383         value are the coefficient of the variable represented
    1384         by the keys ( which are integers ).
     1436        values are the coefficient of the variable represented
     1437        by the keys ( which are integers ). The key ``-1``
     1438        corresponds to the constant term.
    13851439
    1386         EXAMPLE::
     1440        EXAMPLES:
     1441
     1442        With a dictionary::
    13871443
    13881444            sage: from sage.numerical.mip import LinearFunction
    13891445            sage: LinearFunction({0 : 1, 3 : -8})
    13901446            x_0 -8 x_3
     1447
     1448        Using the constructor with a numerical value::
     1449
     1450            sage: from sage.numerical.mip import LinearFunction
     1451            sage: LinearFunction(25)
     1452            25
    13911453        """
    1392         self.f = f
     1454        if isinstance(f, dict):
     1455            self._f = f
     1456        else:
     1457            self._f = {-1:f}
     1458
     1459    def dict(self):
     1460        r"""
     1461        Returns the dictionary corresponding to the Linear Function.
     1462
     1463        A linear function is represented as a dictionary. The
     1464        value are the coefficient of the variable represented
     1465        by the keys ( which are integers ). The key ``-1``
     1466        corresponds to the constant term.
     1467
     1468        EXAMPLE:
     1469
     1470            sage: from sage.numerical.mip import LinearFunction
     1471            sage: lf = LinearFunction({0 : 1, 3 : -8})
     1472            sage: lf.dict()
     1473            {0: 1, 3: -8}
     1474        """
     1475        return self._f
    13931476
    13941477    def __add__(self,b):
    13951478        r"""
     
    14011484            sage: LinearFunction({0 : 1, 3 : -8}) + LinearFunction({2 : 5, 3 : 2}) - 16
    14021485            -16 +x_0 +5 x_2 -6 x_3
    14031486        """
    1404         if isinstance(b,LinearFunction):
    1405             e = copy(self.f)
    1406             for (id,coeff) in b.f.iteritems():
    1407                 e[id] = self.f.get(id,0) + coeff
     1487        if isinstance(b, LinearFunction):
     1488            e = deepcopy(self._f)
     1489            for (id,coeff) in b.dict().iteritems():
     1490                e[id] = self._f.get(id,0) + coeff
    14081491            return LinearFunction(e)
    14091492        else:
    1410             el = copy(self)
    1411             el.f[-1] = el.f.get(-1,0) + b
     1493            el = deepcopy(self)
     1494            el.dict()[-1] = el.dict().get(-1,0) + b
    14121495            return el
    14131496
    14141497    def __neg__(self):
     
    14211504            sage: -LinearFunction({0 : 1, 3 : -8})
    14221505            -1 x_0 +8 x_3
    14231506        """
    1424         return LinearFunction(dict([(id,-coeff) for (id, coeff) in self.f.iteritems()]))
     1507        return LinearFunction(dict([(id,-coeff) for (id, coeff) in self._f.iteritems()]))
    14251508
    14261509    def __sub__(self,b):
    14271510        r"""
     
    14351518            sage: LinearFunction({0 : 1, 3 : -8}) - LinearFunction({2 : 5, 3 : 2}) - 16
    14361519            -16 +x_0 -5 x_2 -10 x_3
    14371520        """
    1438         if isinstance(b,LinearFunction):
    1439             e = copy(self.f)
    1440             for (id,coeff) in b.f.iteritems():
    1441                 e[id] = self.f.get(id,0) - coeff
     1521        if isinstance(b, LinearFunction):
     1522            e = deepcopy(self._f)
     1523            for (id,coeff) in b.dict().iteritems():
     1524                e[id] = self._f.get(id,0) - coeff
    14421525            return LinearFunction(e)
    14431526        else:
    1444             el = copy(self)
    1445             el.f[-1] = self.f.get(-1,0) - b
     1527            el = deepcopy(self)
     1528            el.dict()[-1] = self._f.get(-1,0) - b
    14461529            return el
    14471530
    14481531    def __radd__(self,b):
     
    14851568            sage: LinearFunction({2 : 5, 3 : 2}) * 3
    14861569            15 x_2 +6 x_3
    14871570        """
    1488         return LinearFunction(dict([(id,b*coeff) for (id, coeff) in self.f.iteritems()]))
     1571        return LinearFunction(dict([(id,b*coeff) for (id, coeff) in self._f.iteritems()]))
    14891572
    14901573    def __rmul__(self,b):
    14911574        r"""
     
    14971580            sage: 3 * LinearFunction({2 : 5, 3 : 2})
    14981581            15 x_2 +6 x_3
    14991582        """
    1500 
    15011583        return self.__mul__(b)
    15021584
    15031585    def __repr__(self):
     
    15101592            sage: LinearFunction({2 : 5, 3 : 2})
    15111593            5 x_2 +2 x_3
    15121594        """
    1513 
    1514         cdef dict d = copy(self.f)
     1595        cdef dict d = deepcopy(self._f)
    15151596        cdef bool first = True
    15161597        t = ""
    15171598
     
    15251606        for id,coeff in l:
    15261607            if coeff!=0:
    15271608                if not first:
    1528                     t+=" "
    1529                 t += ("+" if (not first and coeff>=0) else "")+(str(coeff)+" " if coeff!=1 else "")+"x_"+str(id)
     1609                    t += " "
     1610                t += ("+" if (not first and coeff >= 0) else "") + (str(coeff) + " " if coeff != 1 else "") + "x_" + str(id)
    15301611                first = False
    15311612        return t
     1613
     1614    def __le__(self,other):
     1615        r"""
     1616        Defines the <= operator
     1617
     1618        EXAMPLE::
     1619
     1620            sage: from sage.numerical.mip import LinearFunction
     1621            sage: LinearFunction({2 : 5, 3 : 2}) <= LinearFunction({2 : 3, 9 : 2})
     1622            5 x_2 +2 x_3 <= 3 x_2 +2 x_9
     1623        """
     1624        return LinearConstraint(self).__le__(other)
     1625
     1626    def __lt__(self,other):
     1627        r"""
     1628        Defines the < operator
     1629
     1630        EXAMPLE::
     1631
     1632            sage: from sage.numerical.mip import LinearFunction
     1633            sage: LinearFunction({2 : 5, 3 : 2}) < LinearFunction({2 : 3, 9 : 2})
     1634            Traceback (most recent call last):
     1635            ...
     1636            ValueError: The strict operators are not defined. Use <= and >= instead.
     1637        """
     1638        return LinearConstraint(self).__lt__(other)
     1639
     1640    def __gt__(self,other):
     1641        r"""
     1642        Defines the > operator
     1643
     1644        EXAMPLE::
     1645
     1646            sage: from sage.numerical.mip import LinearFunction
     1647            sage: LinearFunction({2 : 5, 3 : 2}) > LinearFunction({2 : 3, 9 : 2})
     1648            Traceback (most recent call last):
     1649            ...
     1650            ValueError: The strict operators are not defined. Use <= and >= instead.
     1651        """
     1652        return LinearConstraint(self).__gt__(other)
     1653
     1654
     1655    def __ge__(self,other):
     1656        r"""
     1657        Defines the >= operator
     1658
     1659        EXAMPLE::
     1660
     1661            sage: from sage.numerical.mip import LinearFunction
     1662            sage: LinearFunction({2 : 5, 3 : 2}) >= LinearFunction({2 : 3, 9 : 2})
     1663            3 x_2 +2 x_9 <= 5 x_2 +2 x_3
     1664        """
     1665        return LinearConstraint(self).__ge__(other)
     1666
     1667    def __hash__(self):
     1668        r"""
     1669        Defining a ``__hash__`` function
     1670
     1671        EXAMPLE::
     1672
     1673            sage: from sage.numerical.mip import LinearFunction
     1674            sage: d = {}
     1675            sage: d[LinearFunction({2 : 5, 3 : 2})] = 3
     1676        """
     1677        return id(self)
     1678
     1679    def __eq__(self,other):
     1680        r"""
     1681        Defines the == operator
     1682
     1683        EXAMPLE::
     1684
     1685            sage: from sage.numerical.mip import LinearFunction
     1686            sage: LinearFunction({2 : 5, 3 : 2}) == LinearFunction({2 : 3, 9 : 2})
     1687            5 x_2 +2 x_3 = 3 x_2 +2 x_9
     1688        """
     1689        return LinearConstraint(self).__eq__(other)
     1690
     1691
     1692class LinearConstraint:
     1693    """
     1694    A class to represent formal Linear Constraints.
     1695
     1696    A Linear Constraint being an inequality between
     1697    two linear functions, this class lets the user
     1698    write ``LinearFunction1 <= LinearFunction2``
     1699    to define the corresponding constraint, which
     1700    can potentially involve several layers of such
     1701    inequalities (``(A <= B <= C``), or even equalities
     1702    like ``A == B``.
     1703
     1704    This class has no reason to be instanciated by the
     1705    user, and is meant to be used by instances of
     1706    MixedIntegerLinearProgram.
     1707
     1708    INPUT:
     1709   
     1710    - ``c`` -- A ``LinearFunction``
     1711   
     1712    EXAMPLE::
     1713   
     1714        sage: p = MixedIntegerLinearProgram()
     1715        sage: b = p.new_variable()
     1716        sage: b[2]+2*b[3] <= b[8]-5
     1717        x_0 +2 x_1 <= -5 +x_2
     1718    """
     1719
     1720    def __init__(self, c):
     1721        r"""
     1722        Constructor for ``LinearConstraint``
     1723
     1724        INPUT:
     1725       
     1726        - ``c`` -- A ``LinearFunction``
     1727       
     1728        EXAMPLE::
     1729       
     1730            sage: p = MixedIntegerLinearProgram()
     1731            sage: b = p.new_variable()
     1732            sage: b[2]+2*b[3] <= b[8]-5
     1733            x_0 +2 x_1 <= -5 +x_2
     1734        """
     1735        self.equality = False
     1736        self.constraints = []
     1737        if isinstance(c, LinearFunction):
     1738            self.constraints.append(c)
     1739        else:
     1740            self.constraints.append(LinearFunction(c))
     1741
     1742    def __repr__(self):
     1743        r"""
     1744        Returns a description of the constraint
     1745       
     1746        EXAMPLE::
     1747
     1748            sage: p = MixedIntegerLinearProgram()
     1749            sage: b = p.new_variable()
     1750            sage: print b[3] <= b[8] + 9
     1751            x_0 <= 9 +x_1
     1752        """
     1753        if self.equality:
     1754            return str(self.constraints[0]) + " = " + str(self.constraints[1])
     1755        else:
     1756            first = True
     1757            s = ""
     1758            for c in self.constraints:
     1759                s += (" <= " if not first else "") + c.__repr__()
     1760                first = False
     1761            return s
     1762
     1763    def __eq__(self,other):
     1764        r"""
     1765        Defines the == operator
     1766
     1767        EXAMPLE::
     1768
     1769            sage: p = MixedIntegerLinearProgram()
     1770            sage: b = p.new_variable()
     1771            sage: print b[3] == b[8] + 9
     1772            x_0 = 9 +x_1
     1773        """
     1774        if not isinstance(other, LinearConstraint):
     1775            other = LinearConstraint(other)
     1776
     1777        if len(self.constraints) == 1 and len(other.constraints) == 1:
     1778            self.constraints.extend(other.constraints)
     1779            self.equality = True
     1780            return self
     1781        else:
     1782            raise ValueError("Impossible to mix equality and inequality in the same equation")
     1783
     1784    def __le__(self,other):
     1785        r"""
     1786        Defines the <= operator
     1787
     1788        EXAMPLE::
     1789
     1790            sage: p = MixedIntegerLinearProgram()
     1791            sage: b = p.new_variable()
     1792            sage: print b[3] <= b[8] + 9
     1793            x_0 <= 9 +x_1
     1794        """
     1795
     1796        if not isinstance(other, LinearConstraint):
     1797            other = LinearConstraint(other)
     1798
     1799        if self.equality or other.equality:
     1800            raise ValueError("Impossible to mix equality and inequality in the same equation")
     1801
     1802        self.constraints.extend(other.constraints)
     1803        return self
     1804
     1805    def __lt__(self, other):
     1806        r"""
     1807        Prevents the use of the stricts operators ``<,>``
     1808       
     1809        EXAMPLE::
     1810
     1811            sage: p = MixedIntegerLinearProgram()
     1812            sage: b = p.new_variable()
     1813            sage: print b[3] < b[8] + 9
     1814            Traceback (most recent call last):
     1815            ...
     1816            ValueError: The strict operators are not defined. Use <= and >= instead.
     1817            sage: print b[3] > b[8] + 9
     1818            Traceback (most recent call last):
     1819            ...
     1820            ValueError: The strict operators are not defined. Use <= and >= instead.
     1821        """
     1822        raise ValueError("The strict operators are not defined. Use <= and >= instead.")
     1823
     1824    __gt__ = __lt__
     1825
     1826    def __ge__(self,other):
     1827        r"""
     1828        Defines the >= operator
     1829
     1830        EXAMPLE::
     1831
     1832            sage: p = MixedIntegerLinearProgram()
     1833            sage: b = p.new_variable()
     1834            sage: print b[3] >= b[8] + 9
     1835            9 +x_1 <= x_0
     1836        """
     1837        if not isinstance(other, LinearConstraint):
     1838            other = LinearConstraint(other)
     1839
     1840        if self.equality or other.equality:
     1841            raise ValueError("Impossible to mix equality and inequality in the same equation")
     1842
     1843        self.constraints = other.constraints + self.constraints
     1844        return self