changes to make iron python faster, and work with repeated names
This commit is contained in:
188
src/pulp/pulp.py
188
src/pulp/pulp.py
@@ -103,17 +103,19 @@ from types import GeneratorType
|
|||||||
|
|
||||||
_DICT_TYPE = dict
|
_DICT_TYPE = dict
|
||||||
|
|
||||||
#try:
|
if sys.platform not in ['cli']:
|
||||||
# from odict import OrderedDict
|
# iron python does not like an OrderedDict
|
||||||
# _DICT_TYPE = OrderedDict
|
try:
|
||||||
#except ImportError:
|
from odict import OrderedDict
|
||||||
# pass
|
_DICT_TYPE = OrderedDict
|
||||||
#try:
|
except ImportError:
|
||||||
# #python 2.7 or 3.1
|
pass
|
||||||
# from collections import OrderedDict
|
try:
|
||||||
# _DICT_TYPE = OrderedDict
|
#python 2.7 or 3.1
|
||||||
#except ImportError:
|
from collections import OrderedDict
|
||||||
# pass
|
_DICT_TYPE = OrderedDict
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def setConfigInformation(**keywords):
|
def setConfigInformation(**keywords):
|
||||||
@@ -349,6 +351,15 @@ class LpVariable(LpElement):
|
|||||||
return d
|
return d
|
||||||
dict = classmethod(dict)
|
dict = classmethod(dict)
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def getLb(self):
|
||||||
|
return self.lowBound
|
||||||
|
|
||||||
|
def getUb(self):
|
||||||
|
return self.upBound
|
||||||
|
|
||||||
def bounds(self, low, up):
|
def bounds(self, low, up):
|
||||||
self.lowBound = low
|
self.lowBound = low
|
||||||
self.upBound = up
|
self.upBound = up
|
||||||
@@ -424,6 +435,9 @@ class LpVariable(LpElement):
|
|||||||
def isBinary(self):
|
def isBinary(self):
|
||||||
return self.cat == LpInteger and self.lowBound == 0 and self.upBound == 1
|
return self.cat == LpInteger and self.lowBound == 0 and self.upBound == 1
|
||||||
|
|
||||||
|
def isInteger(self):
|
||||||
|
return self.cat == LpInteger
|
||||||
|
|
||||||
def isFree(self):
|
def isFree(self):
|
||||||
return self.lowBound == None and self.upBound == None
|
return self.lowBound == None and self.upBound == None
|
||||||
|
|
||||||
@@ -501,16 +515,16 @@ class LpAffineExpression(_DICT_TYPE):
|
|||||||
"""
|
"""
|
||||||
#to remove illegal characters from the names
|
#to remove illegal characters from the names
|
||||||
trans = string.maketrans("-+[] ","_____")
|
trans = string.maketrans("-+[] ","_____")
|
||||||
def setname(self,name):
|
def setName(self,name):
|
||||||
if name:
|
if name:
|
||||||
self.__name = str(name).translate(self.trans)
|
self.__name = str(name).translate(self.trans)
|
||||||
else:
|
else:
|
||||||
self.__name = None
|
self.__name = None
|
||||||
|
|
||||||
def getname(self):
|
def getName(self):
|
||||||
return self.__name
|
return self.__name
|
||||||
|
|
||||||
name = property(fget = getname,fset = setname)
|
name = property(fget=getName, fset=setName)
|
||||||
|
|
||||||
def __init__(self, e = None, constant = 0, name = None):
|
def __init__(self, e = None, constant = 0, name = None):
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -617,41 +631,64 @@ class LpAffineExpression(_DICT_TYPE):
|
|||||||
s = " + ".join(l)
|
s = " + ".join(l)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def asCplexLpAffineExpression(self, name, constant = 1):
|
@staticmethod
|
||||||
# Ugly.
|
def _count_characters(line):
|
||||||
s = ""
|
#counts the characters in a list of strings
|
||||||
sl = name + ":"
|
return sum(len(t) for t in line)
|
||||||
|
|
||||||
|
def asCplexVariablesOnly(self, name):
|
||||||
|
"""
|
||||||
|
helper for asCplexLpAffineExpression
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
line = ["%s:" % name]
|
||||||
notFirst = 0
|
notFirst = 0
|
||||||
variables = self.sorted_keys()
|
variables = self.sorted_keys()
|
||||||
for v in variables:
|
for v in variables:
|
||||||
val = self[v]
|
val = self[v]
|
||||||
if val<0:
|
if val < 0:
|
||||||
ns = " - "
|
sign = " -"
|
||||||
val = -val
|
val = -val
|
||||||
elif notFirst:
|
elif notFirst:
|
||||||
ns = " + "
|
sign = " +"
|
||||||
else:
|
else:
|
||||||
ns = " "
|
sign = ""
|
||||||
notFirst = 1
|
notFirst = 1
|
||||||
if val == 1: ns += v.name
|
if val == 1:
|
||||||
else: ns += "%.12g %s" % (val, v.name)
|
term = "%s %s" %(sign, v.name)
|
||||||
if len(sl)+len(ns) > LpCplexLPLineSize:
|
|
||||||
s += sl+"\n"
|
|
||||||
sl = ns
|
|
||||||
else:
|
else:
|
||||||
sl += ns
|
term = "%s %.12g %s" % (sign, val, v.name)
|
||||||
|
|
||||||
|
if self._count_characters(line) + len(term) > LpCplexLPLineSize:
|
||||||
|
result += ["".join(line)]
|
||||||
|
line = [term]
|
||||||
|
else:
|
||||||
|
line += [term]
|
||||||
|
return result, line
|
||||||
|
|
||||||
|
def asCplexLpAffineExpression(self, name, constant = 1):
|
||||||
|
"""
|
||||||
|
returns a string that represents the Affine Expression in lp format
|
||||||
|
"""
|
||||||
|
#refactored to use a list for speed in iron python
|
||||||
|
result, line = self.asCplexVariablesOnly(name)
|
||||||
if not self:
|
if not self:
|
||||||
ns = " " + str(self.constant)
|
term = " %s" % self.constant
|
||||||
else:
|
else:
|
||||||
ns = ""
|
term = ""
|
||||||
if constant:
|
if constant:
|
||||||
if self.constant < 0: ns = " - " + str(-self.constant)
|
if self.constant < 0:
|
||||||
elif self.constant > 0: ns = " + " + str(self.constant)
|
term = " - %s" % (-self.constant)
|
||||||
if len(sl)+len(ns) > LpCplexLPLineSize:
|
elif self.constant > 0:
|
||||||
s += sl+"\n"+ns+"\n"
|
term = " + %s" % self.constant
|
||||||
|
if self._count_characters(line) + len(term) > LpCplexLPLineSize:
|
||||||
|
result += ["".join(line)]
|
||||||
|
line = [term]
|
||||||
else:
|
else:
|
||||||
s += sl+ns+"\n"
|
line += [term]
|
||||||
return s
|
result += ["".join(line)]
|
||||||
|
result = "%s\n" % "\n".join(result)
|
||||||
|
return result
|
||||||
|
|
||||||
def addInPlace(self, other):
|
def addInPlace(self, other):
|
||||||
if other is 0: return self
|
if other is 0: return self
|
||||||
@@ -793,6 +830,23 @@ class LpConstraint(LpAffineExpression):
|
|||||||
self.sense = sense
|
self.sense = sense
|
||||||
self.modified = True
|
self.modified = True
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def getLb(self):
|
||||||
|
if ( (self.sense == LpConstraintGE) or
|
||||||
|
(self.sense == LpConstraintEQ) ):
|
||||||
|
return -self.constant
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getUb(self):
|
||||||
|
if ( (self.sense == LpConstraintLE) or
|
||||||
|
(self.sense == LpConstraintEQ) ):
|
||||||
|
return -self.constant
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
s = LpAffineExpression.__str__(self, 0)
|
s = LpAffineExpression.__str__(self, 0)
|
||||||
if self.sense:
|
if self.sense:
|
||||||
@@ -800,37 +854,24 @@ class LpConstraint(LpAffineExpression):
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
def asCplexLpConstraint(self, name):
|
def asCplexLpConstraint(self, name):
|
||||||
# Immonde.
|
"""
|
||||||
s = ""
|
Returns a constraint as a string
|
||||||
sl = name + ":"
|
"""
|
||||||
notFirst = 0
|
result, line = self.asCplexVariablesOnly(name)
|
||||||
variables = self.sorted_keys()
|
if not self.keys():
|
||||||
for v in variables:
|
line += ["0"]
|
||||||
val = self[v]
|
|
||||||
if val<0:
|
|
||||||
ns = " - "
|
|
||||||
val = -val
|
|
||||||
elif notFirst:
|
|
||||||
ns = " + "
|
|
||||||
else:
|
|
||||||
ns = " "
|
|
||||||
notFirst = 1
|
|
||||||
if val == 1: ns += v.name
|
|
||||||
else: ns += "%.12g %s" % (val , v.name)
|
|
||||||
if len(sl)+len(ns) > LpCplexLPLineSize:
|
|
||||||
s += sl+"\n"
|
|
||||||
sl = ns
|
|
||||||
else:
|
|
||||||
sl += ns
|
|
||||||
if not self.keys(): sl += "0"
|
|
||||||
c = -self.constant
|
c = -self.constant
|
||||||
if c == 0: c = 0 # Supress sign
|
if c == 0:
|
||||||
ns = " %s %.12g" % (LpConstraintSenses[self.sense], c)
|
c = 0 # Supress sign
|
||||||
if len(sl)+len(ns) > LpCplexLPLineSize:
|
term = " %s %.12g" % (LpConstraintSenses[self.sense], c)
|
||||||
s += sl + "\n" + ns + "\n"
|
if self._count_characters(line)+len(term) > LpCplexLPLineSize:
|
||||||
|
line = "".join(line)
|
||||||
|
line = [term]
|
||||||
else:
|
else:
|
||||||
s += sl + ns + "\n"
|
line += [term]
|
||||||
return s
|
result += ["".join(line)]
|
||||||
|
result = "%s\n" % "\n".join(result)
|
||||||
|
return result
|
||||||
|
|
||||||
def changeRHS(self, RHS):
|
def changeRHS(self, RHS):
|
||||||
"""
|
"""
|
||||||
@@ -1054,7 +1095,7 @@ class LpProblem(object):
|
|||||||
self.modifiedConstraints = []
|
self.modifiedConstraints = []
|
||||||
self.resolveOK = False
|
self.resolveOK = False
|
||||||
self._variables = []
|
self._variables = []
|
||||||
self._variable_names = {} #old school using dict.keys() for a set
|
self._variable_ids = {} #old school using dict.keys() for a set
|
||||||
self.dummyVar = None
|
self.dummyVar = None
|
||||||
|
|
||||||
|
|
||||||
@@ -1161,9 +1202,9 @@ class LpProblem(object):
|
|||||||
|
|
||||||
@param variable: the variable to be added
|
@param variable: the variable to be added
|
||||||
"""
|
"""
|
||||||
if variable.name not in self._variable_names:
|
if id(variable) not in self._variable_ids:
|
||||||
self._variables.append(variable)
|
self._variables.append(variable)
|
||||||
self._variable_names[variable.name] = variable
|
self._variable_ids[id(variable)] = variable
|
||||||
|
|
||||||
def addVariables(self, variables):
|
def addVariables(self, variables):
|
||||||
"""
|
"""
|
||||||
@@ -1242,6 +1283,9 @@ class LpProblem(object):
|
|||||||
Side Effects:
|
Side Effects:
|
||||||
- The objective function is set
|
- The objective function is set
|
||||||
"""
|
"""
|
||||||
|
if isinstance(obj, LpVariable):
|
||||||
|
# allows the user to add a LpVariable as an objective
|
||||||
|
obj = obj + 0.0
|
||||||
try:
|
try:
|
||||||
obj = obj.constraint
|
obj = obj.constraint
|
||||||
name = obj.name
|
name = obj.name
|
||||||
@@ -2185,12 +2229,12 @@ def pulpTestAll():
|
|||||||
|
|
||||||
for s in solvers:
|
for s in solvers:
|
||||||
if s().available():
|
if s().available():
|
||||||
try:
|
#~ try:
|
||||||
pulpTestSolver(s)
|
pulpTestSolver(s)
|
||||||
print "* Solver", s, "passed."
|
print "* Solver", s, "passed."
|
||||||
except Exception, e:
|
#~ except Exception, e:
|
||||||
print e
|
#~ print e
|
||||||
print "* Solver", s, "failed."
|
#~ print "* Solver", s, "failed."
|
||||||
else:
|
else:
|
||||||
print "Solver", s, "unavailable."
|
print "Solver", s, "unavailable."
|
||||||
|
|
||||||
|
|||||||
@@ -1230,7 +1230,7 @@ class COIN_CMD(LpSolver_CMD):
|
|||||||
"""True if the solver is available"""
|
"""True if the solver is available"""
|
||||||
return self.executable(self.path)
|
return self.executable(self.path)
|
||||||
|
|
||||||
def solve_CBC(self, lp):
|
def solve_CBC(self, lp, use_mps=True):
|
||||||
"""Solve a MIP problem using CBC"""
|
"""Solve a MIP problem using CBC"""
|
||||||
if not self.executable(self.path):
|
if not self.executable(self.path):
|
||||||
raise PulpSolverError, "Pulp: cannot execute %s cwd: %s"%(self.path,
|
raise PulpSolverError, "Pulp: cannot execute %s cwd: %s"%(self.path,
|
||||||
@@ -1238,12 +1238,21 @@ class COIN_CMD(LpSolver_CMD):
|
|||||||
if not self.keepFiles:
|
if not self.keepFiles:
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
tmpLp = os.path.join(self.tmpDir, "%d-pulp.lp" % pid)
|
tmpLp = os.path.join(self.tmpDir, "%d-pulp.lp" % pid)
|
||||||
|
tmpMps = os.path.join(self.tmpDir, "%d-pulp.mps" % pid)
|
||||||
tmpSol = os.path.join(self.tmpDir, "%d-pulp.sol" % pid)
|
tmpSol = os.path.join(self.tmpDir, "%d-pulp.sol" % pid)
|
||||||
else:
|
else:
|
||||||
tmpLp = lp.name+"-pulp.lp"
|
tmpLp = lp.name+"-pulp.lp"
|
||||||
|
tmpMps = lp.name+"-pulp.mps"
|
||||||
tmpSol = lp.name+"-pulp.sol"
|
tmpSol = lp.name+"-pulp.sol"
|
||||||
lp.writeLP(tmpLp)
|
if use_mps:
|
||||||
cmds = ' '+tmpLp+" "
|
vs, variablesNames, constraintsNames, objectiveName = lp.writeMPS(
|
||||||
|
tmpMps, rename = 1)
|
||||||
|
cmds = ' '+tmpMps+" "
|
||||||
|
if lp.sense == LpMaximize:
|
||||||
|
cmds += 'max '
|
||||||
|
else:
|
||||||
|
lp.writeLP(tmpLp)
|
||||||
|
cmds = ' '+tmpLp+" "
|
||||||
if self.threads:
|
if self.threads:
|
||||||
cmds += "threads %s "%self.threads
|
cmds += "threads %s "%self.threads
|
||||||
if self.fracGap is not None:
|
if self.fracGap is not None:
|
||||||
@@ -1278,7 +1287,11 @@ class COIN_CMD(LpSolver_CMD):
|
|||||||
self.path
|
self.path
|
||||||
if not os.path.exists(tmpSol):
|
if not os.path.exists(tmpSol):
|
||||||
raise PulpSolverError, "Pulp: Error while executing "+self.path
|
raise PulpSolverError, "Pulp: Error while executing "+self.path
|
||||||
lp.status, values = self.readsol_CBC(tmpSol, lp, lp.variables())
|
if use_mps:
|
||||||
|
lp.status, values = self.readsol_MPS(tmpSol, lp, lp.variables(),
|
||||||
|
variablesNames, constraintsNames, objectiveName)
|
||||||
|
else:
|
||||||
|
lp.status, values = self.readsol_LP(tmpSol, lp, lp.variables())
|
||||||
lp.assignVarsVals(values)
|
lp.assignVarsVals(values)
|
||||||
if not self.keepFiles:
|
if not self.keepFiles:
|
||||||
try:
|
try:
|
||||||
@@ -1291,8 +1304,40 @@ class COIN_CMD(LpSolver_CMD):
|
|||||||
pass
|
pass
|
||||||
return lp.status
|
return lp.status
|
||||||
|
|
||||||
def readsol_CBC(self, filename, lp, vs):
|
def readsol_MPS(self, filename, lp, vs, variablesNames, constraintsNames,
|
||||||
"""Read a CBC solution file"""
|
objectiveName):
|
||||||
|
"""
|
||||||
|
Read a CBC solution file generated from an mps file (different names)
|
||||||
|
"""
|
||||||
|
values = {}
|
||||||
|
|
||||||
|
reverseVn = {}
|
||||||
|
for k,n in variablesNames.iteritems():
|
||||||
|
reverseVn[n] = k
|
||||||
|
|
||||||
|
for v in vs:
|
||||||
|
values[v.name] = 0.0
|
||||||
|
|
||||||
|
cbcStatus = {'Optimal': LpStatusOptimal,
|
||||||
|
'Infeasible': LpStatusInfeasible,
|
||||||
|
'Unbounded': LpStatusUnbounded,
|
||||||
|
'Stopped': LpStatusNotSolved}
|
||||||
|
f = file(filename)
|
||||||
|
statusstr = f.readline().split()[0]
|
||||||
|
status = cbcStatus.get(statusstr, LpStatusUndefined)
|
||||||
|
for l in f:
|
||||||
|
if len(l)<=2:
|
||||||
|
break
|
||||||
|
l = l.split()
|
||||||
|
vn = l[1]
|
||||||
|
if vn in reverseVn:
|
||||||
|
values[reverseVn[vn]] = float(l[2])
|
||||||
|
return status, values
|
||||||
|
|
||||||
|
def readsol_LP(self, filename, lp, vs):
|
||||||
|
"""
|
||||||
|
Read a CBC solution file generated from an lp (good names)
|
||||||
|
"""
|
||||||
values = {}
|
values = {}
|
||||||
for v in vs:
|
for v in vs:
|
||||||
values[v.name] = 0.0
|
values[v.name] = 0.0
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ def pulpTest013(solver):
|
|||||||
prob += -y+z == 7, "c3"
|
prob += -y+z == 7, "c3"
|
||||||
prob += w >= 0, "c4"
|
prob += w >= 0, "c4"
|
||||||
print "\t Testing Long Names"
|
print "\t Testing Long Names"
|
||||||
if solver.__class__ in [COIN_CMD, PULP_CBC_CMD, CPLEX_CMD, GLPK_CMD, GUROBI_CMD]:
|
if solver.__class__ in [CPLEX_CMD, GLPK_CMD, GUROBI_CMD]:
|
||||||
try:
|
try:
|
||||||
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6, w:0})
|
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6, w:0})
|
||||||
except PulpError:
|
except PulpError:
|
||||||
@@ -199,6 +199,23 @@ def pulpTest016(solver):
|
|||||||
print "\t Testing zero objective"
|
print "\t Testing zero objective"
|
||||||
pulpTestCheck(prob, solver, [LpStatusOptimal])
|
pulpTestCheck(prob, solver, [LpStatusOptimal])
|
||||||
|
|
||||||
|
def pulpTest017(solver):
|
||||||
|
# variable as objective
|
||||||
|
prob = LpProblem("test016", LpMinimize)
|
||||||
|
x = LpVariable("x", 0, 4)
|
||||||
|
y = LpVariable("y", -1, 1)
|
||||||
|
z = LpVariable("z", 0)
|
||||||
|
w = LpVariable("w", 0)
|
||||||
|
prob.setObjective(x)
|
||||||
|
prob += x+y <= 5, "c1"
|
||||||
|
prob += x+z >= 10, "c2"
|
||||||
|
prob += -y+z == 7, "c3"
|
||||||
|
prob += w >= 0, "c4"
|
||||||
|
prob += lpSum([0, 0]) <= 0, "c5"
|
||||||
|
print "\t Testing LpVariable (not LpAffineExpression) objective"
|
||||||
|
pulpTestCheck(prob, solver, [LpStatusOptimal])
|
||||||
|
|
||||||
|
|
||||||
def pulpTest020(solver):
|
def pulpTest020(solver):
|
||||||
# MIP
|
# MIP
|
||||||
prob = LpProblem("test020", LpMinimize)
|
prob = LpProblem("test020", LpMinimize)
|
||||||
@@ -504,8 +521,8 @@ def pulpTest123(solver):
|
|||||||
|
|
||||||
def pulpTestSolver(solver, msg = 0):
|
def pulpTestSolver(solver, msg = 0):
|
||||||
tests = [pulpTest001,
|
tests = [pulpTest001,
|
||||||
pulpTest010, pulpTest011, pulpTest012, pulpTest013, #pulpTest014,
|
pulpTest010, pulpTest011, pulpTest012, pulpTest013, pulpTest014,
|
||||||
pulpTest015, pulpTest016,
|
pulpTest015, pulpTest016, pulpTest017,
|
||||||
pulpTest020,
|
pulpTest020,
|
||||||
pulpTest030,
|
pulpTest030,
|
||||||
pulpTest040,
|
pulpTest040,
|
||||||
|
|||||||
Reference in New Issue
Block a user