Ticket #7311: trac_7311.patch
File trac_7311.patch, 18.2 KB (added by , 11 years ago) 


sage/numerical/mip.pxd
diff r 46b1ee5997ac r 5db77e513bf7 sage/numerical/mip.pxd
a b 1 1 cdef extern from *: 2 2 ctypedef double* const_double_ptr "const double*" 3 4 
sage/numerical/mip.pyx
diff r 46b1ee5997ac r 5db77e513bf7 sage/numerical/mip.pyx
a b 4 4 5 5 include "../ext/stdsage.pxi" 6 6 include "../ext/interrupt.pxi" 7 from copy import copy7 from copy import deepcopy 8 8 9 9 class MixedIntegerLinearProgram: 10 10 r""" … … 612 612 # and do not care about any function being optimal. 613 613 614 614 if obj != None: 615 f = obj. f615 f = obj.dict() 616 616 else: 617 617 return None 618 618 … … 624 624 625 625 def add_constraint(self, linear_function, max=None, min=None, name=None): 626 626 r""" 627 Adds a constraint to the ``MixedIntegerLinearProgram``.627 Adds a constraint to self. 628 628 629 629 INPUT: 630 630 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 632 640  ``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 635 646  ``name``  A name for the constraint. 636 647 637 648 EXAMPLE: … … 647 658 x is Real (min = 0, max = None) 648 659 y is Real (min = 0, max = None) 649 660 650 This linear programcan be solved as follows::661 It can be solved as follows:: 651 662 652 663 sage: p = MixedIntegerLinearProgram(maximization=True) 653 664 sage: x = p.new_variable() … … 657 668 sage: p.solve() # optional  requires Glpk or COINOR/CBC 658 669 6.6666666666666661 659 670 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 doublebounds 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 COINOR/CBC 699 6.6666666666666661 700 701 660 702 TESTS:: 661 703 662 704 sage: p=MixedIntegerLinearProgram() 663 705 sage: p.add_constraint(sum([]),min=2) 664 706 """ 665 666 667 if linear_function==0: 707 if linear_function is None or linear_function is 0: 668 708 return None 669 709 670 f = linear_function.f710 if isinstance(linear_function, LinearFunction): 671 711 672 self._constraints_name.append(name)712 f = linear_function.dict() 673 713 674 constant_coefficient = f.pop(1,0)714 self._constraints_name.append(name) 675 715 676 # We do not want to ignore the constant coefficient 677 max = (maxconstant_coefficient) if max != None else None 678 min = (minconstant_coefficient) if min != None else None 716 constant_coefficient = f.get(1,0) 679 717 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 680 721 681 c=len(self._constraints_bounds_min)722 c = len(self._constraints_bounds_min) 682 723 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) 687 729 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) 690 732 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) 691 738 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) 692 745 693 746 def set_binary(self, e): 694 747 r""" … … 1284 1337 sage: v._update_variables_name() 1285 1338 """ 1286 1339 1287 if prefix ==None:1288 prefix =self._name1340 if prefix == None: 1341 prefix = self._name 1289 1342 1290 if self._dim ==1:1343 if self._dim == 1: 1291 1344 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) + "]" 1293 1346 else: 1294 1347 for v in self._dict.itervalues(): 1295 v._update_variables_name(prefix=prefix +"["+str(k)+"]")1348 v._update_variables_name(prefix=prefix + "[" + str(k) + "]") 1296 1349 1297 1350 1298 1351 … … 1311 1364 sage: v 1312 1365 MIPVariable of dimension 3. 1313 1366 """ 1314 return "MIPVariable of dimension " +str(self._dim)+"."1367 return "MIPVariable of dimension " + str(self._dim) + "." 1315 1368 1316 1369 def keys(self): 1317 1370 r""" … … 1377 1430 1378 1431 def __init__(self,f): 1379 1432 r""" 1380 Constructor taking a dictionary as its argument.1433 Constructor taking a dictionary or a numerical value as its argument. 1381 1434 1382 1435 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. 1385 1439 1386 EXAMPLE:: 1440 EXAMPLES: 1441 1442 With a dictionary:: 1387 1443 1388 1444 sage: from sage.numerical.mip import LinearFunction 1389 1445 sage: LinearFunction({0 : 1, 3 : 8}) 1390 1446 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 1391 1453 """ 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 1393 1476 1394 1477 def __add__(self,b): 1395 1478 r""" … … 1401 1484 sage: LinearFunction({0 : 1, 3 : 8}) + LinearFunction({2 : 5, 3 : 2})  16 1402 1485 16 +x_0 +5 x_2 6 x_3 1403 1486 """ 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) + coeff1487 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 1408 1491 return LinearFunction(e) 1409 1492 else: 1410 el = copy(self)1411 el. f[1] = el.f.get(1,0) + b1493 el = deepcopy(self) 1494 el.dict()[1] = el.dict().get(1,0) + b 1412 1495 return el 1413 1496 1414 1497 def __neg__(self): … … 1421 1504 sage: LinearFunction({0 : 1, 3 : 8}) 1422 1505 1 x_0 +8 x_3 1423 1506 """ 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()])) 1425 1508 1426 1509 def __sub__(self,b): 1427 1510 r""" … … 1435 1518 sage: LinearFunction({0 : 1, 3 : 8})  LinearFunction({2 : 5, 3 : 2})  16 1436 1519 16 +x_0 5 x_2 10 x_3 1437 1520 """ 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)  coeff1521 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 1442 1525 return LinearFunction(e) 1443 1526 else: 1444 el = copy(self)1445 el. f[1] = self.f.get(1,0)  b1527 el = deepcopy(self) 1528 el.dict()[1] = self._f.get(1,0)  b 1446 1529 return el 1447 1530 1448 1531 def __radd__(self,b): … … 1485 1568 sage: LinearFunction({2 : 5, 3 : 2}) * 3 1486 1569 15 x_2 +6 x_3 1487 1570 """ 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()])) 1489 1572 1490 1573 def __rmul__(self,b): 1491 1574 r""" … … 1497 1580 sage: 3 * LinearFunction({2 : 5, 3 : 2}) 1498 1581 15 x_2 +6 x_3 1499 1582 """ 1500 1501 1583 return self.__mul__(b) 1502 1584 1503 1585 def __repr__(self): … … 1510 1592 sage: LinearFunction({2 : 5, 3 : 2}) 1511 1593 5 x_2 +2 x_3 1512 1594 """ 1513 1514 cdef dict d = copy(self.f) 1595 cdef dict d = deepcopy(self._f) 1515 1596 cdef bool first = True 1516 1597 t = "" 1517 1598 … … 1525 1606 for id,coeff in l: 1526 1607 if coeff!=0: 1527 1608 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) 1530 1611 first = False 1531 1612 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 1692 class 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