Advertisement
Guest User

Untitled

a guest
May 25th, 2019
166
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 78.94 KB | None | 0 0
  1. #! /usr/bin/env python
  2. # PuLP : Python LP Modeler
  3.  
  4.  
  5. # Copyright (c) 2002-2005, Jean-Sebastien Roy (js@jeannot.org)
  6. # Modifications Copyright (c) 2007- Stuart Anthony Mitchell (s.mitchell@auckland.ac.nz)
  7. # $Id: pulp.py 1791 2008-04-23 22:54:34Z smit023 $
  8.  
  9. # Permission is hereby granted, free of charge, to any person obtaining a
  10. # copy of this software and associated documentation files (the
  11. # "Software"), to deal in the Software without restriction, including
  12. # without limitation the rights to use, copy, modify, merge, publish,
  13. # distribute, sublicense, and/or sell copies of the Software, and to
  14. # permit persons to whom the Software is furnished to do so, subject to
  15. # the following conditions:
  16.  
  17. # The above copyright notice and this permission notice shall be included
  18. # in all copies or substantial portions of the Software.
  19.  
  20. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  21. # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  23. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  24. # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  25. # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  26. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27.  
  28. """
  29. PuLP is an LP modeler written in python. PuLP can generate MPS or LP files
  30. and call GLPK[1], COIN CLP/CBC[2], CPLEX[3], and GUROBI[4] to solve linear
  31. problems.
  32.  
  33. See the examples directory for examples.
  34.  
  35. PuLP requires Python >= 2.5.
  36.  
  37. The examples require at least a solver in your PATH or a shared library file.
  38.  
  39. Documentation is found on https://www.coin-or.org/PuLP/.
  40. A comprehensive wiki can be found at https://www.coin-or.org/PuLP/
  41.  
  42. Use LpVariable() to create new variables. To create a variable 0 <= x <= 3
  43. >>> x = LpVariable("x", 0, 3)
  44.  
  45. To create a variable 0 <= y <= 1
  46. >>> y = LpVariable("y", 0, 1)
  47.  
  48. Use LpProblem() to create new problems. Create "myProblem"
  49. >>> prob = LpProblem("myProblem", LpMinimize)
  50.  
  51. Combine variables to create expressions and constraints and add them to the
  52. problem.
  53. >>> prob += x + y <= 2
  54.  
  55. If you add an expression (not a constraint), it will
  56. become the objective.
  57. >>> prob += -4*x + y
  58.  
  59. Choose a solver and solve the problem. ex:
  60. >>> status = prob.solve(GLPK(msg = 0))
  61.  
  62. Display the status of the solution
  63. >>> LpStatus[status]
  64. 'Optimal'
  65.  
  66. You can get the value of the variables using value(). ex:
  67. >>> value(x)
  68. 2.0
  69.  
  70. Exported Classes:
  71.    - LpProblem -- Container class for a Linear programming problem
  72.    - LpVariable -- Variables that are added to constraints in the LP
  73.    - LpConstraint -- A constraint of the general form
  74.      a1x1+a2x2 ...anxn (<=, =, >=) b
  75.    - LpConstraintVar -- Used to construct a column of the model in column-wise
  76.      modelling
  77.  
  78. Exported Functions:
  79.    - value() -- Finds the value of a variable or expression
  80.    - lpSum() -- given a list of the form [a1*x1, a2x2, ..., anxn] will construct
  81.      a linear expression to be used as a constraint or variable
  82.    - lpDot() --given two lists of the form [a1, a2, ..., an] and
  83.      [ x1, x2, ..., xn] will construct a linear epression to be used
  84.      as a constraint or variable
  85.  
  86. Comments, bug reports, patches and suggestions are welcome.
  87. pulp-or-discuss@googlegroups.com
  88.  
  89. References:
  90. [1] http://www.gnu.org/software/glpk/glpk.html
  91. [2] http://www.coin-or.org/
  92. [3] http://www.cplex.com/
  93. [4] http://www.gurobi.com/
  94. """
  95.  
  96. import types
  97. import string
  98. import itertools
  99. import warnings
  100.  
  101. from .constants import *
  102. from .solvers import *
  103. from collections import Iterable
  104.  
  105. import logging
  106. log = logging.getLogger(__name__)
  107.  
  108. try:  # allow Python 2/3 compatibility
  109.     maketrans = str.maketrans
  110. except AttributeError:
  111.     from string import maketrans
  112.  
  113. _DICT_TYPE = dict
  114.  
  115. if sys.platform not in ['cli']:
  116.     # iron python does not like an OrderedDict
  117.     try:
  118.         from odict import OrderedDict
  119.         _DICT_TYPE = OrderedDict
  120.     except ImportError:
  121.         pass
  122.     try:
  123.         #python 2.7 or 3.1
  124.         from collections import OrderedDict
  125.         _DICT_TYPE = OrderedDict
  126.     except ImportError:
  127.         pass
  128.  
  129.  
  130. def setConfigInformation(**keywords):
  131.     """
  132.    set the data in the configuration file
  133.    at the moment will only edit things in [locations]
  134.    the keyword value pairs come from the keywords dictionary
  135.    """
  136.     #TODO: extend if we ever add another section in the config file
  137.     #read the old configuration
  138.     config = ConfigParser.SafeConfigParser()
  139.     config.read(config_filename)
  140.     #set the new keys
  141.     for (key,val) in keywords.items():
  142.         config.set("locations",key,val)
  143.     #write the new configuration
  144.     fp = open(config_filename,"w")
  145.     config.write(fp)
  146.     fp.close()
  147.  
  148.  
  149. # Default solver selection
  150. if PULP_CBC_CMD().available():
  151.     LpSolverDefault = PULP_CBC_CMD()
  152. elif GLPK_CMD().available():
  153.     LpSolverDefault = GLPK_CMD()
  154. elif COIN_CMD().available():
  155.     LpSolverDefault = COIN_CMD()
  156. else:
  157.     LpSolverDefault = None
  158.  
  159. class LpElement(object):
  160.     """Base class for LpVariable and LpConstraintVar
  161.    """
  162.     #to remove illegal characters from the names
  163.     trans = maketrans("-+[] ->/","________")
  164.     def setName(self,name):
  165.         if name:
  166.             self.__name = str(name).translate(self.trans)
  167.         else:
  168.             self.__name = None
  169.     def getName(self):
  170.         return self.__name
  171.     name = property(fget = getName,fset = setName)
  172.  
  173.     def __init__(self, name):
  174.         self.name = name
  175.          # self.hash MUST be different for each variable
  176.         # else dict() will call the comparison operators that are overloaded
  177.         self.hash = id(self)
  178.         self.modified = True
  179.  
  180.     def __hash__(self):
  181.         return self.hash
  182.  
  183.     def __str__(self):
  184.         return self.name
  185.     def __repr__(self):
  186.         return self.name
  187.  
  188.     def __neg__(self):
  189.         return - LpAffineExpression(self)
  190.  
  191.     def __pos__(self):
  192.         return self
  193.  
  194.     def __bool__(self):
  195.         return 1
  196.  
  197.     def __add__(self, other):
  198.         return LpAffineExpression(self) + other
  199.  
  200.     def __radd__(self, other):
  201.         return LpAffineExpression(self) + other
  202.  
  203.     def __sub__(self, other):
  204.         return LpAffineExpression(self) - other
  205.  
  206.     def __rsub__(self, other):
  207.         return other - LpAffineExpression(self)
  208.  
  209.     def __mul__(self, other):
  210.         return LpAffineExpression(self) * other
  211.  
  212.     def __rmul__(self, other):
  213.         return LpAffineExpression(self) * other
  214.  
  215.     def __div__(self, other):
  216.         return LpAffineExpression(self)/other
  217.  
  218.     def __rdiv__(self, other):
  219.         raise TypeError("Expressions cannot be divided by a variable")
  220.  
  221.     def __le__(self, other):
  222.         return LpAffineExpression(self) <= other
  223.  
  224.     def __ge__(self, other):
  225.         return LpAffineExpression(self) >= other
  226.  
  227.     def __eq__(self, other):
  228.         return LpAffineExpression(self) == other
  229.  
  230.     def __ne__(self, other):
  231.         if isinstance(other, LpVariable):
  232.             return self.name is not other.name
  233.         elif isinstance(other, LpAffineExpression):
  234.             if other.isAtomic():
  235.                 return self is not other.atom()
  236.             else:
  237.                 return 1
  238.         else:
  239.             return 1
  240.  
  241.  
  242. class LpVariable(LpElement):
  243.     """
  244.    This class models an LP Variable with the specified associated parameters
  245.  
  246.    :param name: The name of the variable used in the output .lp file
  247.    :param lowBound: The lower bound on this variable's range.
  248.        Default is negative infinity
  249.    :param upBound: The upper bound on this variable's range.
  250.        Default is positive infinity
  251.    :param cat: The category this variable is in, Integer, Binary or
  252.        Continuous(default)
  253.    :param e: Used for column based modelling: relates to the variable's
  254.        existence in the objective function and constraints
  255.    """
  256.     def __init__(self, name, lowBound = None, upBound = None,
  257.                   cat = LpContinuous, e = None):
  258.         LpElement.__init__(self,name)
  259.         self.lowBound = lowBound
  260.         self.upBound = upBound
  261.         self.cat = cat
  262.         self.varValue = None
  263.         self.dj = None
  264.         self.init = 0
  265.         #code to add a variable to constraints for column based
  266.         # modelling
  267.         if cat == LpBinary:
  268.             self.lowBound = 0
  269.             self.upBound = 1
  270.             self.cat = LpInteger
  271.         if e:
  272.             self.add_expression(e)
  273.  
  274.     def add_expression(self,e):
  275.         self.expression = e
  276.         self.addVariableToConstraints(e)
  277.  
  278.     def matrix(self, name, indexs, lowBound = None, upBound = None, cat = LpContinuous,
  279.             indexStart = []):
  280.         if not isinstance(indexs, tuple): indexs = (indexs,)
  281.         if "%" not in name: name += "_%s" * len(indexs)
  282.  
  283.         index = indexs[0]
  284.         indexs = indexs[1:]
  285.         if len(indexs) == 0:
  286.             return [LpVariable(name % tuple(indexStart + [i]),
  287.                                             lowBound, upBound, cat)
  288.                         for i in index]
  289.         else:
  290.             return [LpVariable.matrix(name, indexs, lowBound,
  291.                                         upBound, cat, indexStart + [i])
  292.                        for i in index]
  293.     matrix = classmethod(matrix)
  294.  
  295.     def dicts(self, name, indexs, lowBound = None, upBound = None, cat = LpContinuous,
  296.         indexStart = []):
  297.         """
  298.        Creates a dictionary of LP variables
  299.  
  300.        This function creates a dictionary of LP Variables with the specified
  301.            associated parameters.
  302.  
  303.        :param name: The prefix to the name of each LP variable created
  304.        :param indexs: A list of strings of the keys to the dictionary of LP
  305.            variables, and the main part of the variable name itself
  306.        :param lowBound: The lower bound on these variables' range. Default is
  307.            negative infinity
  308.        :param upBound: The upper bound on these variables' range. Default is
  309.            positive infinity
  310.        :param cat: The category these variables are in, Integer or
  311.            Continuous(default)
  312.  
  313.        :return: A dictionary of LP Variables
  314.        """
  315.         if not isinstance(indexs, tuple): indexs = (indexs,)
  316.         if "%" not in name: name += "_%s" * len(indexs)
  317.  
  318.         index = indexs[0]
  319.         indexs = indexs[1:]
  320.         d = {}
  321.         if len(indexs) == 0:
  322.             for i in index:
  323.                 d[i] = LpVariable(name % tuple(indexStart + [str(i)]), lowBound, upBound, cat)
  324.         else:
  325.             for i in index:
  326.                 d[i] = LpVariable.dicts(name, indexs, lowBound, upBound, cat, indexStart + [i])
  327.         return d
  328.     dicts = classmethod(dicts)
  329.  
  330.     def dict(self, name, indexs, lowBound = None, upBound = None, cat = LpContinuous):
  331.         if not isinstance(indexs, tuple): indexs = (indexs,)
  332.         if "%" not in name: name += "_%s" * len(indexs)
  333.  
  334.         lists = indexs
  335.  
  336.         if len(indexs)>1:
  337.             # Cartesian product
  338.             res = []
  339.             while len(lists):
  340.                 first = lists[-1]
  341.                 nres = []
  342.                 if res:
  343.                     if first:
  344.                         for f in first:
  345.                             nres.extend([[f]+r for r in res])
  346.                     else:
  347.                         nres = res
  348.                     res = nres
  349.                 else:
  350.                     res = [[f] for f in first]
  351.                 lists = lists[:-1]
  352.             index = [tuple(r) for r in res]
  353.         elif len(indexs) == 1:
  354.             index = indexs[0]
  355.         else:
  356.             return {}
  357.  
  358.         d = {}
  359.         for i in index:
  360.          d[i] = self(name % i, lowBound, upBound, cat)
  361.         return d
  362.     dict = classmethod(dict)
  363.  
  364.     def getLb(self):
  365.         return self.lowBound
  366.  
  367.     def getUb(self):
  368.         return self.upBound
  369.  
  370.     def bounds(self, low, up):
  371.         self.lowBound = low
  372.         self.upBound = up
  373.         self.modified = True
  374.  
  375.     def positive(self):
  376.         self.bounds(0, None)
  377.  
  378.     def value(self):
  379.         return self.varValue
  380.  
  381.     def round(self, epsInt = 1e-5, eps = 1e-7):
  382.         if self.varValue is not None:
  383.             if self.upBound != None and self.varValue > self.upBound and self.varValue <= self.upBound + eps:
  384.                 self.varValue = self.upBound
  385.             elif self.lowBound != None and self.varValue < self.lowBound and self.varValue >= self.lowBound - eps:
  386.                 self.varValue = self.lowBound
  387.             if self.cat == LpInteger and abs(round(self.varValue) - self.varValue) <= epsInt:
  388.                 self.varValue = round(self.varValue)
  389.  
  390.     def roundedValue(self, eps = 1e-5):
  391.         if self.cat == LpInteger and self.varValue != None \
  392.             and abs(self.varValue - round(self.varValue)) <= eps:
  393.             return round(self.varValue)
  394.         else:
  395.             return self.varValue
  396.  
  397.     def valueOrDefault(self):
  398.         if self.varValue != None:
  399.             return self.varValue
  400.         elif self.lowBound != None:
  401.             if self.upBound != None:
  402.                 if 0 >= self.lowBound and 0 <= self.upBound:
  403.                     return 0
  404.                 else:
  405.                     if self.lowBound >= 0:
  406.                         return self.lowBound
  407.                     else:
  408.                         return self.upBound
  409.             else:
  410.                 if 0 >= self.lowBound:
  411.                     return 0
  412.                 else:
  413.                     return self.lowBound
  414.         elif self.upBound != None:
  415.             if 0 <= self.upBound:
  416.                 return 0
  417.             else:
  418.                 return self.upBound
  419.         else:
  420.             return 0
  421.  
  422.     def valid(self, eps):
  423.         if self.varValue == None: return False
  424.         if self.upBound != None and self.varValue > self.upBound + eps:
  425.             return False
  426.         if self.lowBound != None and self.varValue < self.lowBound - eps:
  427.             return False
  428.         if self.cat == LpInteger and abs(round(self.varValue) - self.varValue) > eps:
  429.             return False
  430.         return True
  431.  
  432.     def infeasibilityGap(self, mip = 1):
  433.         if self.varValue == None: raise ValueError("variable value is None")
  434.         if self.upBound != None and self.varValue > self.upBound:
  435.             return self.varValue - self.upBound
  436.         if self.lowBound != None and self.varValue < self.lowBound:
  437.             return self.varValue - self.lowBound
  438.         if mip and self.cat == LpInteger and round(self.varValue) - self.varValue != 0:
  439.             return round(self.varValue) - self.varValue
  440.         return 0
  441.  
  442.     def isBinary(self):
  443.         return self.cat == LpInteger and self.lowBound == 0 and self.upBound == 1
  444.  
  445.     def isInteger(self):
  446.         return self.cat == LpInteger
  447.  
  448.     def isFree(self):
  449.         return self.lowBound == None and self.upBound == None
  450.  
  451.     def isConstant(self):
  452.         return self.lowBound != None and self.upBound == self.lowBound
  453.  
  454.     def isPositive(self):
  455.         return self.lowBound == 0 and self.upBound == None
  456.  
  457.     def asCplexLpVariable(self):
  458.         if self.isFree(): return self.name + " free"
  459.         if self.isConstant(): return self.name + " = %.12g" % self.lowBound
  460.         if self.lowBound == None:
  461.             s= "-inf <= "
  462.         # Note: XPRESS and CPLEX do not interpret integer variables without
  463.         # explicit bounds
  464.         elif (self.lowBound == 0 and self.cat == LpContinuous):
  465.             s = ""
  466.         else:
  467.             s= "%.12g <= " % self.lowBound
  468.         s += self.name
  469.         if self.upBound != None:
  470.             s += " <= %.12g" % self.upBound
  471.         return s
  472.  
  473.     def asCplexLpAffineExpression(self, name, constant = 1):
  474.         return LpAffineExpression(self).asCplexLpAffineExpression(name, constant)
  475.  
  476.     def __ne__(self, other):
  477.         if isinstance(other, LpElement):
  478.             return self.name is not other.name
  479.         elif isinstance(other, LpAffineExpression):
  480.             if other.isAtomic():
  481.                 return self is not other.atom()
  482.             else:
  483.                 return 1
  484.         else:
  485.             return 1
  486.  
  487.     def addVariableToConstraints(self,e):
  488.         """adds a variable to the constraints indicated by
  489.        the LpConstraintVars in e
  490.        """
  491.         for constraint, coeff in e.items():
  492.             constraint.addVariable(self,coeff)
  493.  
  494.     def setInitialValue(self,val):
  495.         """sets the initial value of the Variable to val
  496.        may of may not be supported by the solver
  497.        """
  498.         raise NotImplementedError
  499.  
  500.  
  501. class LpAffineExpression(_DICT_TYPE):
  502.     """
  503.    A linear combination of :class:`LpVariables<LpVariable>`.
  504.    Can be initialised with the following:
  505.  
  506.    #.   e = None: an empty Expression
  507.    #.   e = dict: gives an expression with the values being the coefficients of the keys (order of terms is undetermined)
  508.    #.   e = list or generator of 2-tuples: equivalent to dict.items()
  509.    #.   e = LpElement: an expression of length 1 with the coefficient 1
  510.    #.   e = other: the constant is initialised as e
  511.  
  512.    Examples:
  513.  
  514.       >>> f=LpAffineExpression(LpElement('x'))
  515.       >>> f
  516.       1*x + 0
  517.       >>> x_name = ['x_0', 'x_1', 'x_2']
  518.       >>> x = [LpVariable(x_name[i], lowBound = 0, upBound = 10) for i in range(3) ]
  519.       >>> c = LpAffineExpression([ (x[0],1), (x[1],-3), (x[2],4)])
  520.       >>> c
  521.       1*x_0 + -3*x_1 + 4*x_2 + 0
  522.    """
  523.     #to remove illegal characters from the names
  524.     trans = maketrans("-+[] ","_____")
  525.     def setName(self,name):
  526.         if name:
  527.             self.__name = str(name).translate(self.trans)
  528.         else:
  529.             self.__name = None
  530.  
  531.     def getName(self):
  532.         return self.__name
  533.  
  534.     name = property(fget=getName, fset=setName)
  535.  
  536.     def __init__(self, e = None, constant = 0, name = None):
  537.         self.name = name
  538.         #TODO remove isinstance usage
  539.         if e is None:
  540.             e = {}
  541.         if isinstance(e, LpAffineExpression):
  542.             # Will not copy the name
  543.             self.constant = e.constant
  544.             super(LpAffineExpression, self).__init__(list(e.items()))
  545.         elif isinstance(e, dict):
  546.             self.constant = constant
  547.             super(LpAffineExpression, self).__init__(list(e.items()))
  548.         elif isinstance(e, Iterable):
  549.             self.constant = constant
  550.             super(LpAffineExpression, self).__init__(e)
  551.         elif isinstance(e,LpElement):
  552.             self.constant = 0
  553.             super(LpAffineExpression, self).__init__( [(e, 1)])
  554.         else:
  555.             self.constant = e
  556.             super(LpAffineExpression, self).__init__()
  557.  
  558.     # Proxy functions for variables
  559.  
  560.     def isAtomic(self):
  561.         return len(self) == 1 and self.constant == 0 and list(self.values())[0] == 1
  562.  
  563.     def isNumericalConstant(self):
  564.         return len(self) == 0
  565.  
  566.     def atom(self):
  567.         return list(self.keys())[0]
  568.  
  569.     # Functions on expressions
  570.  
  571.     def __bool__(self):
  572.         return (float(self.constant) != 0.0) or (len(self) > 0)
  573.  
  574.     def value(self):
  575.         s = self.constant
  576.         for v,x in self.items():
  577.             if v.varValue is None:
  578.                 return None
  579.             s += v.varValue * x
  580.         return s
  581.  
  582.     def valueOrDefault(self):
  583.         s = self.constant
  584.         for v,x in self.items():
  585.             s += v.valueOrDefault() * x
  586.         return s
  587.  
  588.     def addterm(self, key, value):
  589.             y = self.get(key, 0)
  590.             if y:
  591.                 y += value
  592.                 self[key] = y
  593.             else:
  594.                 self[key] = value
  595.  
  596.     def emptyCopy(self):
  597.         return LpAffineExpression()
  598.  
  599.     def copy(self):
  600.         """Make a copy of self except the name which is reset"""
  601.         # Will not copy the name
  602.         return LpAffineExpression(self)
  603.  
  604.     def __str__(self, constant = 1):
  605.         s = ""
  606.         for v in self.sorted_keys():
  607.             val = self[v]
  608.             if val<0:
  609.                 if s != "": s += " - "
  610.                 else: s += "-"
  611.                 val = -val
  612.             elif s != "": s += " + "
  613.             if val == 1: s += str(v)
  614.             else: s += str(val) + "*" + str(v)
  615.         if constant:
  616.             if s == "":
  617.                 s = str(self.constant)
  618.             else:
  619.                 if self.constant < 0: s += " - " + str(-self.constant)
  620.                 elif self.constant > 0: s += " + " + str(self.constant)
  621.         elif s == "":
  622.             s = "0"
  623.         return s
  624.  
  625.     def sorted_keys(self):
  626.         """
  627.        returns the list of keys sorted by name
  628.        """
  629.         result = [(v.name, v) for v in self.keys()]
  630.         result.sort()
  631.         result = [v for _, v in result]
  632.         return result
  633.  
  634.     def __repr__(self):
  635.         l = [str(self[v]) + "*" + str(v)
  636.                         for v in self.sorted_keys()]
  637.         l.append(str(self.constant))
  638.         s = " + ".join(l)
  639.         return s
  640.  
  641.     @staticmethod
  642.     def _count_characters(line):
  643.         #counts the characters in a list of strings
  644.         return sum(len(t) for t in line)
  645.  
  646.     def asCplexVariablesOnly(self, name):
  647.         """
  648.        helper for asCplexLpAffineExpression
  649.        """
  650.         result = []
  651.         line = ["%s:" % name]
  652.         notFirst = 0
  653.         variables = self.sorted_keys()
  654.         for v in variables:
  655.             val = self[v]
  656.             if val < 0:
  657.                 sign = " -"
  658.                 val = -val
  659.             elif notFirst:
  660.                 sign = " +"
  661.             else:
  662.                 sign = ""
  663.             notFirst = 1
  664.             if val == 1:
  665.                 term = "%s %s" %(sign, v.name)
  666.             else:
  667.                 #adding zero to val to remove instances of negative zero
  668.                 term = "%s %.12g %s" % (sign, val + 0, v.name)
  669.  
  670.             if self._count_characters(line) + len(term) > LpCplexLPLineSize:
  671.                 result += ["".join(line)]
  672.                 line = [term]
  673.             else:
  674.                 line += [term]
  675.         return result, line
  676.  
  677.     def asCplexLpAffineExpression(self, name, constant = 1):
  678.         """
  679.        returns a string that represents the Affine Expression in lp format
  680.        """
  681.         #refactored to use a list for speed in iron python
  682.         result, line = self.asCplexVariablesOnly(name)
  683.         if not self:
  684.             term = " %s" % self.constant
  685.         else:
  686.             term = ""
  687.             if constant:
  688.                 if self.constant < 0:
  689.                     term = " - %s" % (-self.constant)
  690.                 elif self.constant > 0:
  691.                     term = " + %s" % self.constant
  692.         if self._count_characters(line) + len(term) > LpCplexLPLineSize:
  693.             result += ["".join(line)]
  694.             line = [term]
  695.         else:
  696.             line += [term]
  697.         result += ["".join(line)]
  698.         result = "%s\n" % "\n".join(result)
  699.         return result
  700.  
  701.     def addInPlace(self, other):
  702.         if other is 0: return self
  703.         if other is None: return self
  704.         if isinstance(other,LpElement):
  705.             self.addterm(other, 1)
  706.         elif isinstance(other,LpAffineExpression):
  707.             self.constant += other.constant
  708.             for v,x in other.items():
  709.                 self.addterm(v, x)
  710.         elif isinstance(other,dict):
  711.             for e in other.values():
  712.                 self.addInPlace(e)
  713.         elif (isinstance(other,list)
  714.               or isinstance(other, Iterable)):
  715.            for e in other:
  716.                 self.addInPlace(e)
  717.         else:
  718.             self.constant += other
  719.         return self
  720.  
  721.     def subInPlace(self, other):
  722.         if other is 0: return self
  723.         if other is None: return self
  724.         if isinstance(other,LpElement):
  725.             self.addterm(other, -1)
  726.         elif isinstance(other,LpAffineExpression):
  727.             self.constant -= other.constant
  728.             for v,x in other.items():
  729.                 self.addterm(v, -x)
  730.         elif isinstance(other,dict):
  731.             for e in other.values():
  732.                 self.subInPlace(e)
  733.         elif (isinstance(other,list)
  734.               or isinstance(other, Iterable)):
  735.             for e in other:
  736.                 self.subInPlace(e)
  737.         else:
  738.             self.constant -= other
  739.         return self
  740.  
  741.     def __neg__(self):
  742.         e = self.emptyCopy()
  743.         e.constant = - self.constant
  744.         for v,x in self.items():
  745.             e[v] = - x
  746.         return e
  747.  
  748.     def __pos__(self):
  749.         return self
  750.  
  751.     def __add__(self, other):
  752.         return self.copy().addInPlace(other)
  753.  
  754.     def __radd__(self, other):
  755.         return self.copy().addInPlace(other)
  756.  
  757.     def __iadd__(self, other):
  758.         return self.addInPlace(other)
  759.  
  760.     def __sub__(self, other):
  761.         return self.copy().subInPlace(other)
  762.  
  763.     def __rsub__(self, other):
  764.         return (-self).addInPlace(other)
  765.  
  766.     def __isub__(self, other):
  767.         return (self).subInPlace(other)
  768.  
  769.  
  770.     def __mul__(self, other):
  771.         e = self.emptyCopy()
  772.         if isinstance(other,LpAffineExpression):
  773.             e.constant = self.constant * other.constant
  774.             if len(other):
  775.                 if len(self):
  776.                     raise TypeError("Non-constant expressions cannot be multiplied")
  777.                 else:
  778.                     c = self.constant
  779.                     if c != 0:
  780.                         for v,x in other.items():
  781.                             e[v] = c * x
  782.             else:
  783.                 c = other.constant
  784.                 if c != 0:
  785.                     for v,x in self.items():
  786.                         e[v] = c * x
  787.         elif isinstance(other,LpVariable):
  788.             return self * LpAffineExpression(other)
  789.         else:
  790.             if other != 0:
  791.                 e.constant = self.constant * other
  792.                 for v,x in self.items():
  793.                     e[v] = other * x
  794.         return e
  795.  
  796.     def __rmul__(self, other):
  797.         return self * other
  798.  
  799.     def __div__(self, other):
  800.         if isinstance(other,LpAffineExpression) or isinstance(other,LpVariable):
  801.             if len(other):
  802.                 raise TypeError("Expressions cannot be divided by a non-constant expression")
  803.             other = other.constant
  804.         e = self.emptyCopy()
  805.         e.constant = self.constant / other
  806.         for v,x in self.items():
  807.             e[v] = x / other
  808.         return e
  809.  
  810.     def __truediv__(self, other):
  811.         if isinstance(other,LpAffineExpression) or isinstance(other,LpVariable):
  812.             if len(other):
  813.                 raise TypeError("Expressions cannot be divided by a non-constant expression")
  814.             other = other.constant
  815.         e = self.emptyCopy()
  816.         e.constant = self.constant / other
  817.         for v,x in self.items():
  818.             e[v] = x / other
  819.         return e
  820.  
  821.     def __rdiv__(self, other):
  822.         e = self.emptyCopy()
  823.         if len(self):
  824.             raise TypeError("Expressions cannot be divided by a non-constant expression")
  825.         c = self.constant
  826.         if isinstance(other,LpAffineExpression):
  827.             e.constant = other.constant / c
  828.             for v,x in other.items():
  829.                 e[v] = x / c
  830.         else:
  831.             e.constant = other / c
  832.         return e
  833.  
  834.     def __le__(self, other):
  835.         return LpConstraint(self - other, LpConstraintLE)
  836.  
  837.     def __ge__(self, other):
  838.         return LpConstraint(self - other, LpConstraintGE)
  839.  
  840.     def __eq__(self, other):
  841.         return LpConstraint(self - other, LpConstraintEQ)
  842.  
  843. class LpConstraint(LpAffineExpression):
  844.     """An LP constraint"""
  845.     def __init__(self, e = None, sense = LpConstraintEQ,
  846.                   name = None, rhs = None):
  847.         """
  848.        :param e: an instance of :class:`LpAffineExpression`
  849.        :param sense: one of :data:`~pulp.constants.LpConstraintEQ`, :data:`~pulp.constants.LpConstraintGE`, :data:`~pulp.constants.LpConstraintLE` (0, 1, -1 respectively)
  850.        :param name: identifying string
  851.        :param rhs: numerical value of constraint target
  852.        """
  853.         LpAffineExpression.__init__(self, e, name = name)
  854.         if rhs is not None:
  855.             self.constant -= rhs
  856.         self.sense = sense
  857.         self.pi = None
  858.         self.slack = None
  859.         self.modified = True
  860.  
  861.     def getLb(self):
  862.         if ( (self.sense == LpConstraintGE) or
  863.              (self.sense == LpConstraintEQ) ):
  864.             return -self.constant
  865.         else:
  866.             return None
  867.  
  868.     def getUb(self):
  869.         if ( (self.sense == LpConstraintLE) or
  870.              (self.sense == LpConstraintEQ) ):
  871.             return -self.constant
  872.         else:
  873.             return None
  874.  
  875.     def __str__(self):
  876.         s = LpAffineExpression.__str__(self, 0)
  877.         if self.sense is not None:
  878.             s += " " + LpConstraintSenses[self.sense] + " " + str(-self.constant)
  879.         return s
  880.  
  881.     def asCplexLpConstraint(self, name):
  882.         """
  883.        Returns a constraint as a string
  884.        """
  885.         result, line = self.asCplexVariablesOnly(name)
  886.         if not list(self.keys()):
  887.             line += ["0"]
  888.         c = -self.constant
  889.         if c == 0:
  890.             c = 0 # Supress sign
  891.         term = " %s %.12g" % (LpConstraintSenses[self.sense], c)
  892.         if self._count_characters(line)+len(term) > LpCplexLPLineSize:
  893.             result += ["".join(line)]
  894.             line = [term]
  895.         else:
  896.             line += [term]
  897.         result += ["".join(line)]
  898.         result = "%s\n" % "\n".join(result)
  899.         return result
  900.  
  901.     def changeRHS(self, RHS):
  902.         """
  903.        alters the RHS of a constraint so that it can be modified in a resolve
  904.        """
  905.         self.constant = -RHS
  906.         self.modified = True
  907.  
  908.     def __repr__(self):
  909.         s = LpAffineExpression.__repr__(self)
  910.         if self.sense is not None:
  911.             s += " " + LpConstraintSenses[self.sense] + " 0"
  912.         return s
  913.  
  914.     def copy(self):
  915.         """Make a copy of self"""
  916.         return LpConstraint(self, self.sense)
  917.  
  918.     def emptyCopy(self):
  919.         return LpConstraint(sense = self.sense)
  920.  
  921.     def addInPlace(self, other):
  922.         if isinstance(other,LpConstraint):
  923.             if self.sense * other.sense >= 0:
  924.                 LpAffineExpression.addInPlace(self, other)
  925.                 self.sense |= other.sense
  926.             else:
  927.                 LpAffineExpression.subInPlace(self, other)
  928.                 self.sense |= - other.sense
  929.         elif isinstance(other,list):
  930.             for e in other:
  931.                 self.addInPlace(e)
  932.         else:
  933.             LpAffineExpression.addInPlace(self, other)
  934.             #raise TypeError, "Constraints and Expressions cannot be added"
  935.         return self
  936.  
  937.     def subInPlace(self, other):
  938.         if isinstance(other,LpConstraint):
  939.             if self.sense * other.sense <= 0:
  940.                 LpAffineExpression.subInPlace(self, other)
  941.                 self.sense |= - other.sense
  942.             else:
  943.                 LpAffineExpression.addInPlace(self, other)
  944.                 self.sense |= other.sense
  945.         elif isinstance(other,list):
  946.             for e in other:
  947.                 self.subInPlace(e)
  948.         else:
  949.             LpAffineExpression.subInPlace(self, other)
  950.             #raise TypeError, "Constraints and Expressions cannot be added"
  951.         return self
  952.  
  953.     def __neg__(self):
  954.         c = LpAffineExpression.__neg__(self)
  955.         c.sense = - c.sense
  956.         return c
  957.  
  958.     def __add__(self, other):
  959.         return self.copy().addInPlace(other)
  960.  
  961.     def __radd__(self, other):
  962.         return self.copy().addInPlace(other)
  963.  
  964.     def __sub__(self, other):
  965.         return self.copy().subInPlace(other)
  966.  
  967.     def __rsub__(self, other):
  968.         return (-self).addInPlace(other)
  969.  
  970.     def __mul__(self, other):
  971.         if isinstance(other,LpConstraint):
  972.             c = LpAffineExpression.__mul__(self, other)
  973.             if c.sense == 0:
  974.                 c.sense = other.sense
  975.             elif other.sense != 0:
  976.                 c.sense *= other.sense
  977.             return c
  978.         else:
  979.             return LpAffineExpression.__mul__(self, other)
  980.  
  981.     def __rmul__(self, other):
  982.         return self * other
  983.  
  984.     def __div__(self, other):
  985.         if isinstance(other,LpConstraint):
  986.             c = LpAffineExpression.__div__(self, other)
  987.             if c.sense == 0:
  988.                 c.sense = other.sense
  989.             elif other.sense != 0:
  990.                 c.sense *= other.sense
  991.             return c
  992.         else:
  993.             return LpAffineExpression.__mul__(self, other)
  994.  
  995.     def __rdiv__(self, other):
  996.         if isinstance(other,LpConstraint):
  997.             c = LpAffineExpression.__rdiv__(self, other)
  998.             if c.sense == 0:
  999.                 c.sense = other.sense
  1000.             elif other.sense != 0:
  1001.                 c.sense *= other.sense
  1002.             return c
  1003.         else:
  1004.             return LpAffineExpression.__mul__(self, other)
  1005.  
  1006.     def valid(self, eps = 0):
  1007.         val = self.value()
  1008.         if self.sense == LpConstraintEQ: return abs(val) <= eps
  1009.         else: return val * self.sense >= - eps
  1010.  
  1011.     def makeElasticSubProblem(self, *args, **kwargs):
  1012.         """
  1013.        Builds an elastic subproblem by adding variables to a hard constraint
  1014.  
  1015.        uses FixedElasticSubProblem
  1016.        """
  1017.         return FixedElasticSubProblem(self, *args, **kwargs)
  1018.  
  1019. class LpFractionConstraint(LpConstraint):
  1020.     """
  1021.    Creates a constraint that enforces a fraction requirement a/b = c
  1022.    """
  1023.     def __init__(self, numerator, denominator = None, sense = LpConstraintEQ,
  1024.                  RHS = 1.0, name = None,
  1025.                  complement = None):
  1026.         """
  1027.        creates a fraction Constraint to model constraints of
  1028.        the nature
  1029.        numerator/denominator {==, >=, <=} RHS
  1030.        numerator/(numerator + complement) {==, >=, <=} RHS
  1031.  
  1032.        :param numerator: the top of the fraction
  1033.        :param denominator: as described above
  1034.        :param sense: the sense of the relation of the constraint
  1035.        :param RHS: the target fraction value
  1036.        :param complement: as described above
  1037.        """
  1038.         self.numerator = numerator
  1039.         if denominator is None and complement is not None:
  1040.             self.complement = complement
  1041.             self.denominator = numerator + complement
  1042.         elif denominator is not None and complement is None:
  1043.             self.denominator = denominator
  1044.             self.complement = denominator - numerator
  1045.         else:
  1046.             self.denominator = denominator
  1047.             self.complement = complement
  1048.         lhs = self.numerator - RHS * self.denominator
  1049.         LpConstraint.__init__(self, lhs,
  1050.               sense = sense, rhs = 0, name = name)
  1051.         self.RHS = RHS
  1052.  
  1053.     def findLHSValue(self):
  1054.         """
  1055.        Determines the value of the fraction in the constraint after solution
  1056.        """
  1057.         if abs(value(self.denominator))>= EPS:
  1058.             return value(self.numerator)/value(self.denominator)
  1059.         else:
  1060.             if abs(value(self.numerator))<= EPS:
  1061.                 #zero divided by zero will return 1
  1062.                 return 1.0
  1063.             else:
  1064.                 raise ZeroDivisionError
  1065.  
  1066.     def makeElasticSubProblem(self, *args, **kwargs):
  1067.         """
  1068.        Builds an elastic subproblem by adding variables and splitting the
  1069.        hard constraint
  1070.  
  1071.        uses FractionElasticSubProblem
  1072.        """
  1073.         return FractionElasticSubProblem(self, *args, **kwargs)
  1074.  
  1075. class LpConstraintVar(LpElement):
  1076.     """A Constraint that can be treated as a variable when constructing
  1077.    a LpProblem by columns
  1078.    """
  1079.     def __init__(self, name = None ,sense = None,
  1080.                  rhs = None, e = None):
  1081.         LpElement.__init__(self,name)
  1082.         self.constraint = LpConstraint(name = self.name, sense = sense,
  1083.                                        rhs = rhs , e = e)
  1084.  
  1085.     def addVariable(self, var, coeff):
  1086.         """
  1087.        Adds a variable to the constraint with the
  1088.        activity coeff
  1089.        """
  1090.         self.constraint.addterm(var, coeff)
  1091.  
  1092.     def value(self):
  1093.         return self.constraint.value()
  1094.  
  1095. class LpProblem(object):
  1096.     """An LP Problem"""
  1097.     def __init__(self, name = "NoName", sense = LpMinimize):
  1098.         """
  1099.        Creates an LP Problem
  1100.  
  1101.        This function creates a new LP Problem  with the specified associated parameters
  1102.  
  1103.        :param name: name of the problem used in the output .lp file
  1104.        :param sense: of the LP problem objective.  \
  1105.                Either :data:`~pulp.constants.LpMinimize` (default) \
  1106.                or :data:`~pulp.constants.LpMaximize`.
  1107.        :return: An LP Problem
  1108.        """
  1109.         self.objective = None
  1110.         self.constraints = _DICT_TYPE()
  1111.         self.name = name
  1112.         self.sense = sense
  1113.         self.sos1 = {}
  1114.         self.sos2 = {}
  1115.         self.status = LpStatusNotSolved
  1116.         self.noOverlap = 1
  1117.         self.solver = None
  1118.         self.initialValues = {}
  1119.         self.modifiedVariables = []
  1120.         self.modifiedConstraints = []
  1121.         self.resolveOK = False
  1122.         self._variables = []
  1123.         self._variable_ids = {}  #old school using dict.keys() for a set
  1124.         self.dummyVar = None
  1125.  
  1126.  
  1127.         # locals
  1128.         self.lastUnused = 0
  1129.  
  1130.     def __repr__(self):
  1131.         s = self.name+":\n"
  1132.         if self.sense == 1:
  1133.             s += "MINIMIZE\n"
  1134.         else:
  1135.             s += "MAXIMIZE\n"
  1136.         s += repr(self.objective) +"\n"
  1137.  
  1138.         if self.constraints:
  1139.             s += "SUBJECT TO\n"
  1140.             for n, c in self.constraints.items():
  1141.                 s += c.asCplexLpConstraint(n) +"\n"
  1142.         s += "VARIABLES\n"
  1143.         for v in self.variables():
  1144.             s += v.asCplexLpVariable() + " " + LpCategories[v.cat] + "\n"
  1145.         return s
  1146.  
  1147.     def __getstate__(self):
  1148.         # Remove transient data prior to pickling.
  1149.         state = self.__dict__.copy()
  1150.         del state['_variable_ids']
  1151.         return state
  1152.  
  1153.     def __setstate__(self, state):
  1154.         # Update transient data prior to unpickling.
  1155.         self.__dict__.update(state)
  1156.         self._variable_ids = {}
  1157.         for v in self._variables:
  1158.             self._variable_ids[id(v)] = v
  1159.  
  1160.     def copy(self):
  1161.         """Make a copy of self. Expressions are copied by reference"""
  1162.         lpcopy = LpProblem(name = self.name, sense = self.sense)
  1163.         lpcopy.objective = self.objective
  1164.         lpcopy.constraints = self.constraints.copy()
  1165.         lpcopy.sos1 = self.sos1.copy()
  1166.         lpcopy.sos2 = self.sos2.copy()
  1167.         return lpcopy
  1168.  
  1169.     def deepcopy(self):
  1170.         """Make a copy of self. Expressions are copied by value"""
  1171.         lpcopy = LpProblem(name = self.name, sense = self.sense)
  1172.         if self.objective is not None:
  1173.             lpcopy.objective = self.objective.copy()
  1174.         lpcopy.constraints = {}
  1175.         for k,v in self.constraints.items():
  1176.             lpcopy.constraints[k] = v.copy()
  1177.         lpcopy.sos1 = self.sos1.copy()
  1178.         lpcopy.sos2 = self.sos2.copy()
  1179.         return lpcopy
  1180.  
  1181.     def normalisedNames(self):
  1182.         constraintsNames = {}
  1183.         i = 0
  1184.         for k in self.constraints:
  1185.             constraintsNames[k] = "C%07d" % i
  1186.             i += 1
  1187.         variablesNames = {}
  1188.         i = 0
  1189.         for k in self.variables():
  1190.             variablesNames[k.name] = "X%07d" % i
  1191.             i += 1
  1192.         return constraintsNames, variablesNames, "OBJ"
  1193.  
  1194.     def isMIP(self):
  1195.         for v in self.variables():
  1196.             if v.cat == LpInteger: return 1
  1197.         return 0
  1198.  
  1199.     def roundSolution(self, epsInt = 1e-5, eps = 1e-7):
  1200.         """
  1201.        Rounds the lp variables
  1202.  
  1203.        Inputs:
  1204.            - none
  1205.  
  1206.        Side Effects:
  1207.            - The lp variables are rounded
  1208.        """
  1209.         for v in self.variables():
  1210.             v.round(epsInt, eps)
  1211.  
  1212.     def unusedConstraintName(self):
  1213.         self.lastUnused += 1
  1214.         while 1:
  1215.             s = "_C%d" % self.lastUnused
  1216.             if s not in self.constraints: break
  1217.             self.lastUnused += 1
  1218.         return s
  1219.  
  1220.     def valid(self, eps = 0):
  1221.         for v in self.variables():
  1222.             if not v.valid(eps): return False
  1223.         for c in self.constraints.values():
  1224.             if not c.valid(eps): return False
  1225.         else:
  1226.             return True
  1227.  
  1228.     def infeasibilityGap(self, mip = 1):
  1229.         gap = 0
  1230.         for v in self.variables():
  1231.             gap = max(abs(v.infeasibilityGap(mip)), gap)
  1232.         for c in self.constraints.values():
  1233.             if not c.valid(0):
  1234.                 gap = max(abs(c.value()), gap)
  1235.         return gap
  1236.  
  1237.     def addVariable(self, variable):
  1238.         """
  1239.        Adds a variable to the problem before a constraint is added
  1240.  
  1241.        @param variable: the variable to be added
  1242.        """
  1243.         if id(variable) not in self._variable_ids:
  1244.             self._variables.append(variable)
  1245.             self._variable_ids[id(variable)] = variable
  1246.  
  1247.     def addVariables(self, variables):
  1248.         """
  1249.        Adds variables to the problem before a constraint is added
  1250.  
  1251.        @param variables: the variables to be added
  1252.        """
  1253.         for v in variables:
  1254.             self.addVariable(v)
  1255.  
  1256.     def variables(self):
  1257.         """
  1258.        Returns a list of the problem variables
  1259.  
  1260.        Inputs:
  1261.            - none
  1262.  
  1263.        Returns:
  1264.            - A list of the problem variables
  1265.        """
  1266.         if self.objective:
  1267.             self.addVariables(list(self.objective.keys()))
  1268.         for c in self.constraints.values():
  1269.             self.addVariables(list(c.keys()))
  1270.         variables = self._variables
  1271.         #sort the varibles DSU
  1272.         variables = [[v.name, v] for v in variables]
  1273.         variables.sort()
  1274.         variables = [v for _, v in variables]
  1275.         return variables
  1276.  
  1277.     def variablesDict(self):
  1278.         variables = {}
  1279.         if self.objective:
  1280.             for v in self.objective:
  1281.                 variables[v.name] = v
  1282.         for c in list(self.constraints.values()):
  1283.             for v in c:
  1284.                 variables[v.name] = v
  1285.         return variables
  1286.  
  1287.     def add(self, constraint, name = None):
  1288.         self.addConstraint(constraint, name)
  1289.  
  1290.     def addConstraint(self, constraint, name = None):
  1291.         if not isinstance(constraint, LpConstraint):
  1292.             raise TypeError("Can only add LpConstraint objects")
  1293.         if name:
  1294.             constraint.name = name
  1295.         try:
  1296.             if constraint.name:
  1297.                 name = constraint.name
  1298.             else:
  1299.                 name = self.unusedConstraintName()
  1300.         except AttributeError:
  1301.             raise TypeError("Can only add LpConstraint objects")
  1302.             #removed as this test fails for empty constraints
  1303. #        if len(constraint) == 0:
  1304. #            if not constraint.valid():
  1305. #                raise ValueError, "Cannot add false constraints"
  1306.         if name in self.constraints:
  1307.             if self.noOverlap:
  1308.                 raise PulpError("overlapping constraint names: " + name)
  1309.             else:
  1310.                 print("Warning: overlapping constraint names:", name)
  1311.         self.constraints[name] = constraint
  1312.         self.modifiedConstraints.append(constraint)
  1313.         self.addVariables(list(constraint.keys()))
  1314.  
  1315.     def setObjective(self,obj):
  1316.         """
  1317.        Sets the input variable as the objective function. Used in Columnwise Modelling
  1318.  
  1319.        :param obj: the objective function of type :class:`LpConstraintVar`
  1320.  
  1321.        Side Effects:
  1322.            - The objective function is set
  1323.        """
  1324.         if isinstance(obj, LpVariable):
  1325.             # allows the user to add a LpVariable as an objective
  1326.             obj = obj + 0.0
  1327.         try:
  1328.             obj = obj.constraint
  1329.             name = obj.name
  1330.         except AttributeError:
  1331.             name = None
  1332.         self.objective = obj
  1333.         self.objective.name = name
  1334.         self.resolveOK = False
  1335.  
  1336.     def __iadd__(self, other):
  1337.         if isinstance(other, tuple):
  1338.             other, name = other
  1339.         else:
  1340.             name = None
  1341.         if other is True:
  1342.             return self
  1343.         if isinstance(other, LpConstraintVar):
  1344.             self.addConstraint(other.constraint)
  1345.         elif isinstance(other, LpConstraint):
  1346.             self.addConstraint(other, name)
  1347.         elif isinstance(other, LpAffineExpression):
  1348.             if self.objective is not None:
  1349.                 warnings.warn("Overwriting previously set objective.")
  1350.             self.objective = other
  1351.             self.objective.name = name
  1352.         elif isinstance(other, LpVariable) or isinstance(other, (int, float)):
  1353.             if self.objective is not None:
  1354.                 warnings.warn("Overwriting previously set objective.")
  1355.             self.objective = LpAffineExpression(other)
  1356.             self.objective.name = name
  1357.         else:
  1358.             raise TypeError("Can only add LpConstraintVar, LpConstraint, LpAffineExpression or True objects")
  1359.         return self
  1360.  
  1361.     def extend(self, other, use_objective = True):
  1362.         """
  1363.        extends an LpProblem by adding constraints either from a dictionary
  1364.        a tuple or another LpProblem object.
  1365.  
  1366.        @param use_objective: determines whether the objective is imported from
  1367.        the other problem
  1368.  
  1369.        For dictionaries the constraints will be named with the keys
  1370.        For tuples an unique name will be generated
  1371.        For LpProblems the name of the problem will be added to the constraints
  1372.        name
  1373.        """
  1374.         if isinstance(other, dict):
  1375.             for name in other:
  1376.                 self.constraints[name] = other[name]
  1377.         elif isinstance(other, LpProblem):
  1378.             for v in set(other.variables()).difference(self.variables()):
  1379.                 v.name = other.name + v.name
  1380.             for name,c in other.constraints.items():
  1381.                 c.name = other.name + name
  1382.                 self.addConstraint(c)
  1383.             if use_objective:
  1384.                 self.objective += other.objective
  1385.         else:
  1386.             for c in other:
  1387.                 if isinstance(c,tuple):
  1388.                     name = c[0]
  1389.                     c = c[1]
  1390.                 else:
  1391.                     name = None
  1392.                 if not name: name = c.name
  1393.                 if not name: name = self.unusedConstraintName()
  1394.                 self.constraints[name] = c
  1395.  
  1396.     def coefficients(self, translation = None):
  1397.         coefs = []
  1398.         if translation == None:
  1399.             for c in self.constraints:
  1400.                 cst = self.constraints[c]
  1401.                 coefs.extend([(v.name, c, cst[v]) for v in cst])
  1402.         else:
  1403.             for c in self.constraints:
  1404.                 ctr = translation[c]
  1405.                 cst = self.constraints[c]
  1406.                 coefs.extend([(translation[v.name], ctr, cst[v]) for v in cst])
  1407.         return coefs
  1408.  
  1409.     def writeMPS(self, filename, mpsSense = 0, rename = 0, mip = 1):
  1410.         wasNone, dummyVar = self.fixObjective()
  1411.         f = open(filename, "w")
  1412.         if mpsSense == 0: mpsSense = self.sense
  1413.         cobj = self.objective
  1414.         if mpsSense != self.sense:
  1415.             n = cobj.name
  1416.             cobj = - cobj
  1417.             cobj.name = n
  1418.         if rename:
  1419.             constraintsNames, variablesNames, cobj.name = self.normalisedNames()
  1420.         f.write("*SENSE:"+LpSenses[mpsSense]+"\n")
  1421.         n = self.name
  1422.         if rename: n = "MODEL"
  1423.         f.write("NAME          "+n+"\n")
  1424.         vs = self.variables()
  1425.         # constraints
  1426.         f.write("ROWS\n")
  1427.         objName = cobj.name
  1428.         if not objName: objName = "OBJ"
  1429.         f.write(" N  %s\n" % objName)
  1430.         mpsConstraintType = {LpConstraintLE:"L", LpConstraintEQ:"E", LpConstraintGE:"G"}
  1431.         for k,c in self.constraints.items():
  1432.             if rename: k = constraintsNames[k]
  1433.             f.write(" "+mpsConstraintType[c.sense]+"  "+k+"\n")
  1434.         # matrix
  1435.         f.write("COLUMNS\n")
  1436.         # Creation of a dict of dict:
  1437.         # coefs[nomVariable][nomContrainte] = coefficient
  1438.         coefs = {}
  1439.         for k,c in self.constraints.items():
  1440.             if rename: k = constraintsNames[k]
  1441.             for v in c:
  1442.                 n = v.name
  1443.                 if rename: n = variablesNames[n]
  1444.                 if n in coefs:
  1445.                     coefs[n][k] = c[v]
  1446.                 else:
  1447.                     coefs[n] = {k:c[v]}
  1448.  
  1449.         for v in vs:
  1450.             if mip and v.cat == LpInteger:
  1451.                 f.write("    MARK      'MARKER'                 'INTORG'\n")
  1452.             n = v.name
  1453.             if rename: n = variablesNames[n]
  1454.             if n in coefs:
  1455.                 cv = coefs[n]
  1456.                 # Most of the work is done here
  1457.                 for k in cv: f.write("    %-8s  %-8s  % .12e\n" % (n,k,cv[k]))
  1458.  
  1459.             # objective function
  1460.             if v in cobj: f.write("    %-8s  %-8s  % .12e\n" % (n,objName,cobj[v]))
  1461.             if mip and v.cat == LpInteger:
  1462.                 f.write("    MARK      'MARKER'                 'INTEND'\n")
  1463.         # right hand side
  1464.         f.write("RHS\n")
  1465.         for k,c in self.constraints.items():
  1466.             c = -c.constant
  1467.             if rename: k = constraintsNames[k]
  1468.             if c == 0: c = 0
  1469.             f.write("    RHS       %-8s  % .12e\n" % (k,c))
  1470.         # bounds
  1471.         f.write("BOUNDS\n")
  1472.         for v in vs:
  1473.             n = v.name
  1474.             if rename: n = variablesNames[n]
  1475.             if v.lowBound != None and v.lowBound == v.upBound:
  1476.                 f.write(" FX BND       %-8s  % .12e\n" % (n, v.lowBound))
  1477.             elif v.lowBound == 0 and v.upBound == 1 and mip and v.cat == LpInteger:
  1478.                 f.write(" BV BND       %-8s\n" % n)
  1479.             else:
  1480.                 if v.lowBound != None:
  1481.                     # In MPS files, variables with no bounds (i.e. >= 0)
  1482.                     # are assumed BV by COIN and CPLEX.
  1483.                     # So we explicitly write a 0 lower bound in this case.
  1484.                     if v.lowBound != 0 or (mip and v.cat == LpInteger and v.upBound == None):
  1485.                         f.write(" LO BND       %-8s  % .12e\n" % (n, v.lowBound))
  1486.                 else:
  1487.                     if v.upBound != None:
  1488.                         f.write(" MI BND       %-8s\n" % n)
  1489.                     else:
  1490.                         f.write(" FR BND       %-8s\n" % n)
  1491.                 if v.upBound != None:
  1492.                     f.write(" UP BND       %-8s  % .12e\n" % (n, v.upBound))
  1493.         f.write("ENDATA\n")
  1494.         f.close()
  1495.         self.restoreObjective(wasNone, dummyVar)
  1496.         # returns the variables, in writing order
  1497.         if rename == 0:
  1498.             return vs
  1499.         else:
  1500.             return vs, variablesNames, constraintsNames, cobj.name
  1501.  
  1502.     def writeLP(self, filename, writeSOS = 1, mip = 1):
  1503.         """
  1504.        Write the given Lp problem to a .lp file.
  1505.  
  1506.        This function writes the specifications (objective function,
  1507.        constraints, variables) of the defined Lp problem to a file.
  1508.  
  1509.        :param filename:  the name of the file to be created.
  1510.  
  1511.        Side Effects:
  1512.            - The file is created.
  1513.        """
  1514.         f = open(filename, "w")
  1515.         f.write("\\* "+self.name+" *\\\n")
  1516.         if self.sense == 1:
  1517.             f.write("Minimize\n")
  1518.         else:
  1519.             f.write("Maximize\n")
  1520.         wasNone, objectiveDummyVar = self.fixObjective()
  1521.         objName = self.objective.name
  1522.         if not objName: objName = "OBJ"
  1523.         f.write(self.objective.asCplexLpAffineExpression(objName, constant = 0))
  1524.         f.write("Subject To\n")
  1525.         ks = list(self.constraints.keys())
  1526.         ks.sort()
  1527.         dummyWritten = False
  1528.         for k in ks:
  1529.             constraint = self.constraints[k]
  1530.             if not list(constraint.keys()):
  1531.                 #empty constraint add the dummyVar
  1532.                 dummyVar = self.get_dummyVar()
  1533.                 constraint += dummyVar
  1534.                 #set this dummyvar to zero so infeasible problems are not made feasible
  1535.                 if not dummyWritten:
  1536.                     f.write((dummyVar == 0.0).asCplexLpConstraint("_dummy"))
  1537.                     dummyWritten = True
  1538.             f.write(constraint.asCplexLpConstraint(k))
  1539.         vs = self.variables()
  1540.         # check if any names are longer than 100 characters
  1541.         long_names = [v.name for v in vs if len(v.name) > 100]
  1542.         if long_names:
  1543.             raise PulpError('Variable names too long for Lp format\n'
  1544.                                 + str(long_names))
  1545.         # check for repeated names
  1546.         repeated_names = {}
  1547.         for v in vs:
  1548.             repeated_names[v.name] = repeated_names.get(v.name, 0) + 1
  1549.         repeated_names = [(key, value) for key, value in list(repeated_names.items())
  1550.                             if value >= 2]
  1551.         if repeated_names:
  1552.             raise PulpError('Repeated variable names in Lp format\n'
  1553.                                 + str(repeated_names))
  1554.         # Bounds on non-"positive" variables
  1555.         # Note: XPRESS and CPLEX do not interpret integer variables without
  1556.         # explicit bounds
  1557.         if mip:
  1558.             vg = [v for v in vs if not (v.isPositive() and v.cat == LpContinuous) \
  1559.                 and not v.isBinary()]
  1560.         else:
  1561.             vg = [v for v in vs if not v.isPositive()]
  1562.         if vg:
  1563.             f.write("Bounds\n")
  1564.             for v in vg:
  1565.                 f.write("%s\n" % v.asCplexLpVariable())
  1566.         # Integer non-binary variables
  1567.         if mip:
  1568.             vg = [v for v in vs if v.cat == LpInteger and not v.isBinary()]
  1569.             if vg:
  1570.                 f.write("Generals\n")
  1571.                 for v in vg: f.write("%s\n" % v.name)
  1572.             # Binary variables
  1573.             vg = [v for v in vs if v.isBinary()]
  1574.             if vg:
  1575.                 f.write("Binaries\n")
  1576.                 for v in vg: f.write("%s\n" % v.name)
  1577.         # Special Ordered Sets
  1578.         if writeSOS and (self.sos1 or self.sos2):
  1579.             f.write("SOS\n")
  1580.             if self.sos1:
  1581.                 for sos in self.sos1.values():
  1582.                     f.write("S1:: \n")
  1583.                     for v,val in sos.items():
  1584.                         f.write(" %s: %.12g\n" % (v.name, val))
  1585.             if self.sos2:
  1586.                 for sos in self.sos2.values():
  1587.                     f.write("S2:: \n")
  1588.                     for v,val in sos.items():
  1589.                         f.write(" %s: %.12g\n" % (v.name, val))
  1590.         f.write("End\n")
  1591.         f.close()
  1592.         self.restoreObjective(wasNone, objectiveDummyVar)
  1593.  
  1594.     def assignVarsVals(self, values):
  1595.         variables = self.variablesDict()
  1596.         for name in values:
  1597.             if name != '__dummy':
  1598.                 variables[name].varValue = values[name]
  1599.  
  1600.     def assignVarsDj(self,values):
  1601.         variables = self.variablesDict()
  1602.         for name in values:
  1603.             if name != '__dummy':
  1604.                 variables[name].dj = values[name]
  1605.  
  1606.     def assignConsPi(self, values):
  1607.         for name in values:
  1608.             try:
  1609.                 self.constraints[name].pi = values[name]
  1610.             except KeyError:
  1611.                 pass
  1612.  
  1613.     def assignConsSlack(self, values, activity=False):
  1614.         for name in values:
  1615.             try:
  1616.                 if activity:
  1617.                     #reports the activitynot the slack
  1618.                     self.constraints[name].slack = -1 * (
  1619.                             self.constraints[name].constant + float(values[name]))
  1620.                 else:
  1621.                     self.constraints[name].slack = float(values[name])
  1622.             except KeyError:
  1623.                 pass
  1624.  
  1625.     def get_dummyVar(self):
  1626.         if self.dummyVar is None:
  1627.             self.dummyVar = LpVariable("__dummy", 0, 0)
  1628.         return self.dummyVar
  1629.  
  1630.     def fixObjective(self):
  1631.         if self.objective is None:
  1632.             self.objective = 0
  1633.             wasNone = 1
  1634.         else:
  1635.             wasNone = 0
  1636.         if not isinstance(self.objective, LpAffineExpression):
  1637.             self.objective = LpAffineExpression(self.objective)
  1638.         if self.objective.isNumericalConstant():
  1639.             dummyVar = self.get_dummyVar()
  1640.             self.objective += dummyVar
  1641.         else:
  1642.             dummyVar = None
  1643.         return wasNone, dummyVar
  1644.  
  1645.     def restoreObjective(self, wasNone, dummyVar):
  1646.         if wasNone:
  1647.             self.objective = None
  1648.         elif not dummyVar is None:
  1649.             self.objective -= dummyVar
  1650.  
  1651.     def solve(self, solver = None, **kwargs):
  1652.         """
  1653.        Solve the given Lp problem.
  1654.  
  1655.        This function changes the problem to make it suitable for solving
  1656.        then calls the solver.actualSolve() method to find the solution
  1657.  
  1658.        :param solver:  Optional: the specific solver to be used, defaults to the
  1659.              default solver.
  1660.  
  1661.        Side Effects:
  1662.            - The attributes of the problem object are changed in
  1663.              :meth:`~pulp.solver.LpSolver.actualSolve()` to reflect the Lp solution
  1664.        """
  1665.  
  1666.         if not(solver): solver = self.solver
  1667.         if not(solver): solver = LpSolverDefault
  1668.         wasNone, dummyVar = self.fixObjective()
  1669.         #time it
  1670.         self.solutionTime = -clock()
  1671.         status = solver.actualSolve(self, **kwargs)
  1672.         self.solutionTime += clock()
  1673.         self.restoreObjective(wasNone, dummyVar)
  1674.         self.solver = solver
  1675.         return status
  1676.  
  1677.     def sequentialSolve(self, objectives, absoluteTols = None,
  1678.                         relativeTols = None, solver = None, debug = False):
  1679.         """
  1680.        Solve the given Lp problem with several objective functions.
  1681.  
  1682.        This function sequentially changes the objective of the problem
  1683.        and then adds the objective function as a constraint
  1684.  
  1685.        :param objectives: the list of objectives to be used to solve the problem
  1686.        :param absoluteTols: the list of absolute tolerances to be applied to
  1687.           the constraints should be +ve for a minimise objective
  1688.        :param relativeTols: the list of relative tolerances applied to the constraints
  1689.        :param solver: the specific solver to be used, defaults to the default solver.
  1690.  
  1691.        """
  1692.         #TODO Add a penalty variable to make problems elastic
  1693.         #TODO add the ability to accept different status values i.e. infeasible etc
  1694.  
  1695.         if not(solver): solver = self.solver
  1696.         if not(solver): solver = LpSolverDefault
  1697.         if not(absoluteTols):
  1698.             absoluteTols = [0] * len(objectives)
  1699.         if not(relativeTols):
  1700.             relativeTols  = [1] * len(objectives)
  1701.         #time it
  1702.         self.solutionTime = -clock()
  1703.         statuses = []
  1704.         for i,(obj,absol,rel) in enumerate(zip(objectives,
  1705.                                                absoluteTols, relativeTols)):
  1706.             self.setObjective(obj)
  1707.             status = solver.actualSolve(self)
  1708.             statuses.append(status)
  1709.             if debug: self.writeLP("%sSequence.lp"%i)
  1710.             if self.sense == LpMinimize:
  1711.                 self += obj <= value(obj)*rel + absol,"%s_Sequence_Objective"%i
  1712.             elif self.sense == LpMaximize:
  1713.                 self += obj >= value(obj)*rel + absol,"%s_Sequence_Objective"%i
  1714.         self.solutionTime += clock()
  1715.         self.solver = solver
  1716.         return statuses
  1717.  
  1718.     def resolve(self, solver = None, **kwargs):
  1719.         """
  1720.        resolves an Problem using the same solver as previously
  1721.        """
  1722.         if not(solver): solver = self.solver
  1723.         if self.resolveOK:
  1724.             return self.solver.actualResolve(self, **kwargs)
  1725.         else:
  1726.             return self.solve(solver = solver, **kwargs)
  1727.  
  1728.     def setSolver(self,solver = LpSolverDefault):
  1729.         """Sets the Solver for this problem useful if you are using
  1730.        resolve
  1731.        """
  1732.         self.solver = solver
  1733.  
  1734.     def setInitial(self,values):
  1735.         self.initialValues = values
  1736.  
  1737.     def numVariables(self):
  1738.         return len(self._variable_ids)
  1739.  
  1740.     def numConstraints(self):
  1741.         return len(self.constraints)
  1742.  
  1743.     def getSense(self):
  1744.         return self.sense
  1745.  
  1746. class FixedElasticSubProblem(LpProblem):
  1747.     """
  1748.    Contains the subproblem generated by converting a fixed constraint
  1749.    :math:`\sum_{i}a_i x_i = b` into an elastic constraint.
  1750.  
  1751.    :param constraint: The LpConstraint that the elastic constraint is based on
  1752.    :param penalty: penalty applied for violation (+ve or -ve) of the constraints
  1753.    :param proportionFreeBound:
  1754.        the proportional bound (+ve and -ve) on
  1755.        constraint violation that is free from penalty
  1756.    :param proportionFreeBoundList: the proportional bound on \
  1757.        constraint violation that is free from penalty, expressed as a list\
  1758.        where [-ve, +ve]
  1759.    """
  1760.     def __init__(self, constraint, penalty = None,
  1761.                                         proportionFreeBound = None,
  1762.                                         proportionFreeBoundList = None):
  1763.         subProblemName =  "%s_elastic_SubProblem" % constraint.name
  1764.         LpProblem.__init__(self, subProblemName, LpMinimize)
  1765.         self.objective = LpAffineExpression()
  1766.         self.constraint = constraint
  1767.         self.constant = constraint.constant
  1768.         self.RHS = - constraint.constant
  1769.         self.objective = LpAffineExpression()
  1770.         self += constraint, "_Constraint"
  1771.         #create and add these variables but disabled
  1772.         self.freeVar = LpVariable("_free_bound",
  1773.                                   upBound = 0, lowBound = 0)
  1774.         self.upVar = LpVariable("_pos_penalty_var",
  1775.                                 upBound = 0, lowBound = 0)
  1776.         self.lowVar = LpVariable("_neg_penalty_var",
  1777.                                  upBound = 0, lowBound = 0)
  1778.         constraint.addInPlace(self.freeVar + self.lowVar + self.upVar)
  1779.         if proportionFreeBound:
  1780.             proportionFreeBoundList = [proportionFreeBound, proportionFreeBound]
  1781.         if proportionFreeBoundList:
  1782.             #add a costless variable
  1783.             self.freeVar.upBound = abs(constraint.constant *
  1784.                                         proportionFreeBoundList[0])
  1785.             self.freeVar.lowBound = -abs(constraint.constant *
  1786.                                        proportionFreeBoundList[1])
  1787.             # Note the reversal of the upbound and lowbound due to the nature of the
  1788.             # variable
  1789.         if penalty is not None:
  1790.             #activate these variables
  1791.             self.upVar.upBound = None
  1792.             self.lowVar.lowBound = None
  1793.             self.objective = penalty*self.upVar - penalty*self.lowVar
  1794.  
  1795.     def _findValue(self, attrib):
  1796.         """
  1797.        safe way to get the value of a variable that may not exist
  1798.        """
  1799.         var = getattr(self, attrib, 0)
  1800.         if var:
  1801.             if value(var) is not None:
  1802.                 return value(var)
  1803.             else:
  1804.                 return 0.0
  1805.         else:
  1806.             return 0.0
  1807.  
  1808.     def isViolated(self):
  1809.         """
  1810.        returns true if the penalty variables are non-zero
  1811.        """
  1812.         upVar = self._findValue("upVar")
  1813.         lowVar = self._findValue("lowVar")
  1814.         freeVar = self._findValue("freeVar")
  1815.         result = abs(upVar + lowVar) >= EPS
  1816.         if result:
  1817.             log.debug("isViolated %s, upVar %s, lowVar %s, freeVar %s result %s"%(
  1818.                         self.name, upVar, lowVar, freeVar, result))
  1819.             log.debug("isViolated value lhs %s constant %s"%(
  1820.                          self.findLHSValue(), self.RHS))
  1821.         return result
  1822.  
  1823.     def findDifferenceFromRHS(self):
  1824.         """
  1825.        The amount the actual value varies from the RHS (sense: LHS - RHS)
  1826.        """
  1827.         return self.findLHSValue() - self.RHS
  1828.  
  1829.  
  1830.     def findLHSValue(self):
  1831.         """
  1832.        for elastic constraints finds the LHS value of the constraint without
  1833.        the free variable and or penalty variable assumes the constant is on the
  1834.        rhs
  1835.        """
  1836.         upVar = self._findValue("upVar")
  1837.         lowVar = self._findValue("lowVar")
  1838.         freeVar = self._findValue("freeVar")
  1839.         return self.constraint.value() - self.constant - \
  1840.                 upVar - lowVar - freeVar
  1841.  
  1842.     def deElasticize(self):
  1843.         """ de-elasticize constraint """
  1844.         self.upVar.upBound = 0
  1845.         self.lowVar.lowBound = 0
  1846.  
  1847.     def reElasticize(self):
  1848.         """
  1849.        Make the Subproblem elastic again after deElasticize
  1850.        """
  1851.         self.upVar.lowBound = 0
  1852.         self.upVar.upBound = None
  1853.         self.lowVar.upBound = 0
  1854.         self.lowVar.lowBound = None
  1855.  
  1856.     def alterName(self, name):
  1857.         """
  1858.        Alters the name of anonymous parts of the problem
  1859.  
  1860.        """
  1861.         self.name = "%s_elastic_SubProblem" % name
  1862.         if hasattr(self, 'freeVar'):
  1863.             self.freeVar.name = self.name + "_free_bound"
  1864.         if hasattr(self, 'upVar'):
  1865.             self.upVar.name = self.name + "_pos_penalty_var"
  1866.         if hasattr(self, 'lowVar'):
  1867.             self.lowVar.name = self.name + "_neg_penalty_var"
  1868.  
  1869.  
  1870. class FractionElasticSubProblem(FixedElasticSubProblem):
  1871.     """
  1872.    Contains the subproblem generated by converting a Fraction constraint
  1873.    numerator/(numerator+complement) = b
  1874.    into an elastic constraint
  1875.  
  1876.    :param name: The name of the elastic subproblem
  1877.    :param penalty: penalty applied for violation (+ve or -ve) of the constraints
  1878.    :param proportionFreeBound: the proportional bound (+ve and -ve) on
  1879.        constraint violation that is free from penalty
  1880.    :param proportionFreeBoundList: the proportional bound on
  1881.        constraint violation that is free from penalty, expressed as a list
  1882.        where [-ve, +ve]
  1883.    """
  1884.     def __init__(self, name, numerator, RHS, sense,
  1885.                                         complement = None,
  1886.                                         denominator = None,
  1887.                                         penalty = None,
  1888.                                         proportionFreeBound = None,
  1889.                                         proportionFreeBoundList = None):
  1890.         subProblemName = "%s_elastic_SubProblem" % name
  1891.         self.numerator = numerator
  1892.         if denominator is None and complement is not None:
  1893.             self.complement = complement
  1894.             self.denominator = numerator + complement
  1895.         elif denominator is not None and complement is None:
  1896.             self.denominator = denominator
  1897.             self.complement = denominator - numerator
  1898.         else:
  1899.             raise PulpError('only one of denominator and complement must be specified')
  1900.         self.RHS = RHS
  1901.         self.lowTarget = self.upTarget = None
  1902.         LpProblem.__init__(self, subProblemName, LpMinimize)
  1903.         self.freeVar = LpVariable("_free_bound",
  1904.                                   upBound = 0, lowBound = 0)
  1905.         self.upVar = LpVariable("_pos_penalty_var",
  1906.                                 upBound = 0, lowBound = 0)
  1907.         self.lowVar = LpVariable("_neg_penalty_var",
  1908.                                  upBound = 0, lowBound = 0)
  1909.         if proportionFreeBound:
  1910.             proportionFreeBoundList = [proportionFreeBound, proportionFreeBound]
  1911.         if proportionFreeBoundList:
  1912.             upProportionFreeBound, lowProportionFreeBound = \
  1913.                     proportionFreeBoundList
  1914.         else:
  1915.             upProportionFreeBound, lowProportionFreeBound = (0, 0)
  1916.         #create an objective
  1917.         self += LpAffineExpression()
  1918.         #There are three cases if the constraint.sense is ==, <=, >=
  1919.         if sense in [LpConstraintEQ, LpConstraintLE]:
  1920.             #create a constraint the sets the upper bound of target
  1921.             self.upTarget = RHS + upProportionFreeBound
  1922.             self.upConstraint = LpFractionConstraint(self.numerator,
  1923.                                     self.complement,
  1924.                                     LpConstraintLE,
  1925.                                     self.upTarget,
  1926.                                     denominator = self.denominator)
  1927.             if penalty is not None:
  1928.                 self.lowVar.lowBound = None
  1929.                 self.objective += -1* penalty * self.lowVar
  1930.                 self.upConstraint += self.lowVar
  1931.             self += self.upConstraint, '_upper_constraint'
  1932.         if sense in [LpConstraintEQ, LpConstraintGE]:
  1933.             #create a constraint the sets the lower bound of target
  1934.             self.lowTarget = RHS - lowProportionFreeBound
  1935.             self.lowConstraint = LpFractionConstraint(self.numerator,
  1936.                                                  self.complement,
  1937.                                                 LpConstraintGE,
  1938.                                                 self.lowTarget,
  1939.                                                 denominator = self.denominator)
  1940.             if penalty is not None:
  1941.                 self.upVar.upBound = None
  1942.                 self.objective += penalty * self.upVar
  1943.                 self.lowConstraint += self.upVar
  1944.             self += self.lowConstraint, '_lower_constraint'
  1945.  
  1946.     def findLHSValue(self):
  1947.         """
  1948.        for elastic constraints finds the LHS value of the constraint without
  1949.        the free variable and or penalty variable assumes the constant is on the
  1950.        rhs
  1951.        """
  1952.         # uses code from LpFractionConstraint
  1953.         if abs(value(self.denominator))>= EPS:
  1954.             return value(self.numerator)/value(self.denominator)
  1955.         else:
  1956.             if abs(value(self.numerator))<= EPS:
  1957.                 #zero divided by zero will return 1
  1958.                 return 1.0
  1959.             else:
  1960.                 raise ZeroDivisionError
  1961.  
  1962.     def isViolated(self):
  1963.         """
  1964.        returns true if the penalty variables are non-zero
  1965.        """
  1966.         if abs(value(self.denominator))>= EPS:
  1967.             if self.lowTarget is not None:
  1968.                 if self.lowTarget > self.findLHSValue():
  1969.                     return True
  1970.             if self.upTarget is not None:
  1971.                 if self.findLHSValue() > self.upTarget:
  1972.                     return True
  1973.         else:
  1974.             #if the denominator is zero the constraint is satisfied
  1975.             return False
  1976.  
  1977. class LpVariableDict(dict):
  1978.     """An LP variable generator"""
  1979.     def __init__(self, name, data = {}, lowBound = None, upBound = None, cat = LpContinuous):
  1980.         self.name = name
  1981.         dict.__init__(self, data)
  1982.  
  1983.     def __getitem__(self, key):
  1984.         if key in self:
  1985.             return dict.__getitem__(self, key)
  1986.         else:
  1987.             self[key] = LpVariable(self.name % key, lowBound, upBound, cat)
  1988.             return self[key]
  1989.  
  1990. # Utility functions
  1991.  
  1992. def lpSum(vector):
  1993.     """
  1994.    Calculate the sum of a list of linear expressions
  1995.  
  1996.    :param vector: A list of linear expressions
  1997.    """
  1998.     return LpAffineExpression().addInPlace(vector)
  1999.  
  2000. def lpDot(v1, v2):
  2001.     """Calculate the dot product of two lists of linear expressions"""
  2002.     if not isiterable(v1) and not isiterable(v2):
  2003.         return v1 * v2
  2004.     elif not isiterable(v1):
  2005.         return lpDot([v1]*len(v2),v2)
  2006.     elif not isiterable(v2):
  2007.         return lpDot(v1,[v2]*len(v1))
  2008.     else:
  2009.         return lpSum([lpDot(e1,e2) for e1,e2 in zip(v1,v2)])
  2010.  
  2011. def isNumber(x):
  2012.     """Returns true if x is an int or a float"""
  2013.     return isinstance(x, (int, float))
  2014.  
  2015. def value(x):
  2016.     """Returns the value of the variable/expression x, or x if it is a number"""
  2017.     if isNumber(x): return x
  2018.     else: return x.value()
  2019.  
  2020. def valueOrDefault(x):
  2021.     """Returns the value of the variable/expression x, or x if it is a number
  2022.    Variable without value (None) are affected a possible value (within their
  2023.    bounds)."""
  2024.     if isNumber(x): return x
  2025.     else: return x.valueOrDefault()
  2026.  
  2027. def combination(orgset, k = None):
  2028.     """
  2029.    returns an iterator that lists the combinations of orgset of
  2030.    length k
  2031.  
  2032.    :param orgset: the list to be iterated
  2033.    :param k: the cardinality of the subsets
  2034.  
  2035.    :return: an iterator of the subsets
  2036.  
  2037.    example:
  2038.  
  2039.    >>> c = combination([1,2,3,4],2)
  2040.    >>> for s in c:
  2041.    ...     print(s)
  2042.    (1, 2)
  2043.    (1, 3)
  2044.    (1, 4)
  2045.    (2, 3)
  2046.    (2, 4)
  2047.    (3, 4)
  2048.    """
  2049.     try:
  2050.         from itertools import combination as _it_combination
  2051.         return _it_combination(orgset, k)
  2052.     except ImportError:
  2053.         return __combination(orgset,k)
  2054.  
  2055. def __combination(orgset, k):
  2056.     """
  2057.    fall back if probstat is not installed note it is GPL so cannot
  2058.    be included
  2059.    """
  2060.     if k == 1:
  2061.         for i in orgset:
  2062.             yield (i,)
  2063.     elif k>1:
  2064.         for i,x in enumerate(orgset):
  2065.             #iterates though to near the end
  2066.             for s in __combination(orgset[i+1:],k-1):
  2067.                 yield (x,) + s
  2068.  
  2069. def permutation(orgset, k = None):
  2070.     """
  2071.    returns an iterator that lists the permutations of orgset of
  2072.    length k
  2073.  
  2074.    :param orgset: the list to be iterated
  2075.    :param k: the cardinality of the subsets
  2076.  
  2077.    :return: an iterator of the subsets
  2078.  
  2079.    example:
  2080.  
  2081.    >>> c = permutation([1,2,3,4],2)
  2082.    >>> for s in c:
  2083.    ...     print(s)
  2084.    (1, 2)
  2085.    (1, 3)
  2086.    (1, 4)
  2087.    (2, 1)
  2088.    (2, 3)
  2089.    (2, 4)
  2090.    (3, 1)
  2091.    (3, 2)
  2092.    (3, 4)
  2093.    (4, 1)
  2094.    (4, 2)
  2095.    (4, 3)
  2096.    """
  2097.     try:
  2098.         from itertools import permutation as _it_permutation
  2099.         return _it_permutation(orgset, k)
  2100.     except ImportError:
  2101.         return __permutation(orgset, k)
  2102.  
  2103. def __permutation(orgset, k):
  2104.     """
  2105.    fall back if probstat is not installed note it is GPL so cannot
  2106.    be included
  2107.    """
  2108.     if k == 1:
  2109.         for i in orgset:
  2110.             yield (i,)
  2111.     elif k>1:
  2112.         for i,x in enumerate(orgset):
  2113.             #iterates though to near the end
  2114.             for s in __permutation(orgset[:i] + orgset[i+1:],k-1):
  2115.                 yield (x,)+ s
  2116.  
  2117. def allpermutations(orgset, k):
  2118.     """
  2119.    returns all permutations of orgset with up to k items
  2120.  
  2121.    :param orgset: the list to be iterated
  2122.    :param k: the maxcardinality of the subsets
  2123.  
  2124.    :return: an iterator of the subsets
  2125.  
  2126.    example:
  2127.  
  2128.    >>> c = allpermutations([1,2,3,4],2)
  2129.    >>> for s in c:
  2130.    ...     print(s)
  2131.    (1,)
  2132.    (2,)
  2133.    (3,)
  2134.    (4,)
  2135.    (1, 2)
  2136.    (1, 3)
  2137.    (1, 4)
  2138.    (2, 1)
  2139.    (2, 3)
  2140.    (2, 4)
  2141.    (3, 1)
  2142.    (3, 2)
  2143.    (3, 4)
  2144.    (4, 1)
  2145.    (4, 2)
  2146.    (4, 3)
  2147.    """
  2148.     return itertools.chain(*[permutation(orgset,i) for i in range(1,k+1)])
  2149.  
  2150. def allcombinations(orgset, k):
  2151.     """
  2152.    returns all combinations of orgset with up to k items
  2153.  
  2154.    :param orgset: the list to be iterated
  2155.    :param k: the maxcardinality of the subsets
  2156.  
  2157.    :return: an iterator of the subsets
  2158.  
  2159.    example:
  2160.  
  2161.    >>> c = allcombinations([1,2,3,4],2)
  2162.    >>> for s in c:
  2163.    ...     print(s)
  2164.    (1,)
  2165.    (2,)
  2166.    (3,)
  2167.    (4,)
  2168.    (1, 2)
  2169.    (1, 3)
  2170.    (1, 4)
  2171.    (2, 3)
  2172.    (2, 4)
  2173.    (3, 4)
  2174.    """
  2175.     return itertools.chain(*[combination(orgset,i) for i in range(1,k+1)])
  2176.  
  2177. def makeDict(headers, array, default = None):
  2178.     """
  2179.    makes a list into a dictionary with the headings given in headings
  2180.    headers is a list of header lists
  2181.    array is a list with the data
  2182.    """
  2183.     result, defdict = __makeDict(headers, array, default)
  2184.     return result
  2185.  
  2186. def __makeDict(headers, array, default = None):
  2187.     #this is a recursive function so end the recursion as follows
  2188.     result ={}
  2189.     returndefaultvalue = None
  2190.     if len(headers) == 1:
  2191.         result.update(dict(zip(headers[0],array)))
  2192.         defaultvalue = default
  2193.     else:
  2194.         for i,h in enumerate(headers[0]):
  2195.             result[h],defaultvalue = __makeDict(headers[1:],array[i],default)
  2196.     if default != None:
  2197.         f = lambda :defaultvalue
  2198.         defresult = collections.defaultdict(f)
  2199.         defresult.update(result)
  2200.         result = defresult
  2201.         returndefaultvalue = collections.defaultdict(f)
  2202.     return result, returndefaultvalue
  2203.  
  2204. def splitDict(Data):
  2205.     """
  2206.    Split a dictionary with lists as the data, into smaller dictionaries
  2207.  
  2208.    :param Data: A dictionary with lists as the values
  2209.  
  2210.    :return: A tuple of dictionaries each containing the data separately,
  2211.            with the same dictionary keys
  2212.    """
  2213.     # find the maximum number of items in the dictionary
  2214.     maxitems = max([len(values) for values in Data.values()])
  2215.     output =[dict() for i in range(maxitems)]
  2216.     for key, values in Data.items():
  2217.         for i, val in enumerate(values):
  2218.             output[i][key] = val
  2219.  
  2220.     return tuple(output)
  2221.  
  2222. def read_table(data, coerce_type, transpose=False):
  2223.     '''
  2224.    Reads in data from a simple table and forces it to be a particular type
  2225.  
  2226.    This is a helper function that allows data to be easily constained in a
  2227.    simple script
  2228.    ::return: a dictionary of with the keys being a tuple of the strings
  2229.       in the first row and colum of the table
  2230.    ::param data: the multiline string containing the table data
  2231.    ::param coerce_type: the type that the table data is converted to
  2232.    ::param transpose: reverses the data if needed
  2233.  
  2234.    Example:
  2235.    >>> table_data = """
  2236.    ...         L1      L2      L3      L4      L5      L6
  2237.    ... C1      6736    42658   70414   45170   184679  111569
  2238.    ... C2      217266  227190  249640  203029  153531  117487
  2239.    ... C3      35936   28768   126316  2498    130317  74034
  2240.    ... C4      73446   52077   108368  75011   49827   62850
  2241.    ... C5      174664  177461  151589  153300  59916   135162
  2242.    ... C6      186302  189099  147026  164938  149836  286307
  2243.    ... """
  2244.    >>> table = read_table(table_data, int)
  2245.    >>> table[("C1","L1")]
  2246.    6736
  2247.    >>> table[("C6","L5")]
  2248.    149836
  2249.    '''
  2250.     lines = data.splitlines()
  2251.     headings = lines[1].split()
  2252.     result = {}
  2253.     for row in lines[2:]:
  2254.         items = row.split()
  2255.         for i, item in enumerate(items[1:]):
  2256.             if transpose:
  2257.                 key = (headings[i], items[0])
  2258.             else:
  2259.                 key = (items[0], headings[i])
  2260.             result[key] = coerce_type(item)
  2261.     return result
  2262.  
  2263. def configSolvers():
  2264.     """
  2265.    Configure the path the the solvers on the command line
  2266.  
  2267.    Designed to configure the file locations of the solvers from the
  2268.    command line after installation
  2269.    """
  2270.     configlist = [(cplex_dll_path,"cplexpath","CPLEX: "),
  2271.                   (coinMP_path, "coinmppath","CoinMP dll (windows only): ")]
  2272.     print("Please type the full path including filename and extension \n" +
  2273.            "for each solver available")
  2274.     configdict = {}
  2275.     for (default, key, msg) in configlist:
  2276.         value = input(msg + "[" + str(default) +"]")
  2277.         if value:
  2278.             configdict[key] = value
  2279.     setConfigInformation(**configdict)
  2280.  
  2281.  
  2282. def pulpTestAll():
  2283.     from .tests import pulpTestSolver
  2284.     solvers = [PULP_CBC_CMD,
  2285.                CPLEX_DLL,
  2286.                CPLEX_CMD,
  2287.                CPLEX_PY,
  2288.                COIN_CMD,
  2289.                COINMP_DLL,
  2290.                GLPK_CMD,
  2291.                XPRESS,
  2292.                GUROBI,
  2293.                GUROBI_CMD,
  2294.                PYGLPK,
  2295.                YAPOSIB
  2296.                ]
  2297.  
  2298.     failed = False
  2299.     for s in solvers:
  2300.         if s().available():
  2301.             try:
  2302.                 pulpTestSolver(s)
  2303.                 print("* Solver %s passed." % s)
  2304.             except Exception as e:
  2305.                 print(e)
  2306.                 print("* Solver", s, "failed.")
  2307.                 failed = True
  2308.         else:
  2309.             print("Solver %s unavailable" % s)
  2310.     if failed:
  2311.         raise PulpError("Tests Failed")
  2312.  
  2313. def pulpDoctest():
  2314.     """
  2315.    runs all doctests
  2316.    """
  2317.     import doctest
  2318.     if __name__ != '__main__':
  2319.         from . import pulp
  2320.         doctest.testmod(pulp)
  2321.     else:
  2322.         doctest.testmod()
  2323.  
  2324.  
  2325. if __name__ == '__main__':
  2326.     # Tests
  2327.     pulpTestAll()
  2328.     pulpDoctest()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement