changes to make iron python faster, and work with repeated names

This commit is contained in:
Stuart Mitchell
2012-01-24 23:43:31 +13:00
3 changed files with 187 additions and 81 deletions

View File

@@ -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."

View File

@@ -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

View File

@@ -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,