changes from Christophe-Marie Duquesne supporting PYGLPK and YAPOSIB

This commit is contained in:
Stuart Mitchell
2013-01-07 22:01:15 +13:00
3 changed files with 111 additions and 71 deletions

View File

@@ -1,2 +1,3 @@
Roy, J.S
Mitchell, Stuart A
Christophe-Marie Duquesne (PYGLPK and YAPOSIB bindings)

View File

@@ -956,7 +956,7 @@ else:
"""
Solve a well formulated lp problem
creates a gurobi model, variables and constraints and attaches
creates a cplex model, variables and constraints and attaches
them to the lp model which it then solves
"""
self.buildSolverModel(lp)
@@ -1900,6 +1900,8 @@ class PYGLPK(LpSolver):
"""
The glpk LP/MIP solver (via its python interface)
Copyright Christophe-Marie Duquesne 2012
The glpk variables are available (after a solve) in var.solverVar
The glpk constraints are available in constraint.solverConstraint
The Model is in prob.solverModel
@@ -1907,7 +1909,7 @@ class PYGLPK(LpSolver):
try:
#import the model into the global scope
global glpk
import glpk
import glpk.glpkpi as glpk
except ImportError:
def available(self):
"""True if the solver is available"""
@@ -1927,42 +1929,42 @@ class PYGLPK(LpSolver):
@param mip: if False the solver will solve a MIP as an LP
@param msg: displays information from the solver to stdout
@param timeLimit: not handled by glpk
@param epgap: sets the integer bound gap
@param timeLimit: not handled
@param epgap: not handled
@param solverParams: not handled
"""
LpSolver.__init__(self, mip, msg)
self.timeLimit = timeLimit # time limits are not handled
self.epgap = epgap
if not self.msg:
glpk.env.term_on = False
glpk.glp_term_out(glpk.GLP_OFF)
def findSolutionValues(self, lp):
model = lp.solverModel
solutionStatus = model.status
glpkLpStatus = {"opt": LpStatusOptimal,
"undef": LpStatusUndefined,
"feas": LpStatusNotSolved,
"infeas": LpStatusInfeasible,
"nofeas": LpStatusInfeasible,
"unbnd": LpStatusUnbounded
prob = lp.solverModel
if self.mip and self.hasMIPConstraints(lp.solverModel):
solutionStatus = glpk.glp_mip_status(prob)
else:
solutionStatus = glpk.glp_get_status(prob)
glpkLpStatus = {glpk.GLP_OPT: LpStatusOptimal,
glpk.GLP_UNDEF: LpStatusUndefined,
glpk.GLP_FEAS: LpStatusNotSolved,
glpk.GLP_INFEAS: LpStatusInfeasible,
glpk.GLP_NOFEAS: LpStatusInfeasible,
glpk.GLP_UNBND: LpStatusUnbounded
}
#populate pulp solution values
for var in lp.variables():
var.varValue = var.solverVar.primal
try:
var.dj = var.solverVar.dual
except RuntimeError:
var.dj = None
if self.mip and self.hasMIPConstraints(lp.solverModel):
var.varValue = glpk.glp_mip_col_val(prob, var.glpk_index)
else:
var.varValue = glpk.glp_get_col_prim(prob, var.glpk_index)
var.dj = glpk.glp_get_col_dual(prob, var.glpk_index)
#put pi and slack variables against the constraints
for constr in lp.constraints.values():
try:
constr.pi = constr.solverConstraint.dual
except RuntimeError:
constr.pi = None
constr.slack = constr.solverConstraint.primal
if self.msg:
print "glpk status=", solutionStatus
if self.mip and self.hasMIPConstraints(lp.solverModel):
row_val = glpk.glp_mip_row_val(prob, constr.glpk_index)
else:
row_val = glpk.glp_get_row_prim(prob, constr.glpk_index)
constr.slack = -constr.constant - row_val
constr.pi = glpk.glp_get_row_dual(prob, constr.glpk_index)
lp.resolveOK = True
for var in lp.variables():
var.isModified = False
@@ -1974,17 +1976,20 @@ class PYGLPK(LpSolver):
"""True if the solver is available"""
return True
def hasMIPConstraints(self, solverModel):
return (glpk.glp_get_num_int(solverModel) > 0 or
glpk.glp_get_num_bin(solverModel) > 0)
def callSolver(self, lp, callback = None):
"""Solves the problem with glpk
"""
self.solveTime = -clock()
lp.solverModel.simplex()
if self.mip:
if (lp.solverModel.status != "infeas"
and lp.solverModel.status != "nofeas"
and lp.solverModel.status != "unbnd"
):
lp.solverModel.integer()
glpk.glp_adv_basis(lp.solverModel, 0)
glpk.glp_simplex(lp.solverModel, None)
if self.mip and self.hasMIPConstraints(lp.solverModel):
status = glpk.glp_get_status(lp.solverModel)
if status in (glpk.GLP_OPT, glpk.GLP_UNDEF, glpk.GLP_FEAS):
glpk.glp_intopt(lp.solverModel, None)
self.solveTime += clock()
def buildSolverModel(self, lp):
@@ -1992,45 +1997,69 @@ class PYGLPK(LpSolver):
Takes the pulp lp model and translates it into a glpk model
"""
log.debug("create the glpk model")
lp.solverModel = glpk.LPX()
lp.solverModel.name = lp.name
prob = glpk.glp_create_prob()
glpk.glp_set_prob_name(prob, lp.name)
log.debug("set the sense of the problem")
if lp.sense == LpMaximize:
lp.solverModel.obj.maximize = True
log.debug("add the Constraints to the problem")
lp.solverModel.rows.add(len(lp.constraints.keys()))
i = 0
for name, constraint in lp.constraints.items():
row = lp.solverModel.rows[i]
row.name = name
glpk.glp_set_obj_dir(prob, glpk.GLP_MAX)
log.debug("add the constraints to the problem")
glpk.glp_add_rows(prob, len(lp.constraints.keys()))
for i, v in enumerate(lp.constraints.items(), start=1):
name, constraint = v
glpk.glp_set_row_name(prob, i, name)
if constraint.sense == LpConstraintLE:
row.bounds = None,-constraint.constant
glpk.glp_set_row_bnds(prob, i, glpk.GLP_UP,
0.0, -constraint.constant)
elif constraint.sense == LpConstraintGE:
row.bounds = -constraint.constant, None
glpk.glp_set_row_bnds(prob, i, glpk.GLP_LO,
-constraint.constant, 0.0)
elif constraint.sense == LpConstraintEQ:
row.bounds = -constraint.constant,-constraint.constant
glpk.glp_set_row_bnds(prob, i, glpk.GLP_FX,
-constraint.constant, -constraint.constant)
else:
raise PulpSolverError, 'Detected an invalid constraint type'
i += 1
constraint.solverConstraint = row
constraint.glpk_index = i
log.debug("add the variables to the problem")
lp.solverModel.cols.add(len(lp.variables()))
j = 0
for var in lp.variables():
col = lp.solverModel.cols[j]
col.name = var.name
col.bounds = var.lowBound,var.upBound
glpk.glp_add_cols(prob, len(lp.variables()))
for j, var in enumerate(lp.variables(), start=1):
glpk.glp_set_col_name(prob, j, var.name)
lb = 0.0
ub = 0.0
t = glpk.GLP_FR
if not var.lowBound is None:
lb = var.lowBound
t = glpk.GLP_LO
if not var.upBound is None:
ub = var.upBound
t = glpk.GLP_UP
if not var.upBound is None and not var.lowBound is None:
if ub == lb:
t = glpk.GLP_FX
else:
t = glpk.GLP_DB
glpk.glp_set_col_bnds(prob, j, t, lb, ub)
if var.cat == LpInteger:
col.kind = int
var.solverVar = col
j += 1
glpk.glp_set_col_kind(prob, j, glpk.GLP_IV)
assert glpk.glp_get_col_kind(prob, j) == glpk.GLP_IV
var.glpk_index = j
log.debug("set the objective function")
lp.solverModel.obj[:] = [lp.objective.get(var, 0.0) for var in
lp.variables()]
for var in lp.variables():
value = lp.objective.get(var)
if value:
glpk.glp_set_obj_coef(prob, var.glpk_index, value)
log.debug("set the problem matrix")
for name,constraint in lp.constraints.items():
constraint.solverConstraint.matrix =[(var.solverVar.index,
value ) for var, value in constraint.items()]
for constraint in lp.constraints.values():
l = len(constraint.items())
ind = glpk.intArray(l + 1)
val = glpk.doubleArray(l + 1)
for j, v in enumerate(constraint.items(), start=1):
var, value = v
ind[j] = var.glpk_index
val[j] = value
glpk.glp_set_mat_row(prob, constraint.glpk_index, l, ind,
val)
lp.solverModel = prob
#glpk.glp_write_lp(prob, None, "glpk.lp")
def actualSolve(self, lp, callback = None):
"""
@@ -2060,14 +2089,17 @@ class PYGLPK(LpSolver):
"""
log.debug("Resolve the Model using glpk")
for constraint in lp.constraints.values():
row = constraint.solverConstraint
i = constraint.glpk_index
if constraint.modified:
if constraint.sense == LpConstraintLE:
row.bounds = None,-constraint.constant
glpk.glp_set_row_bnds(prob, i, glpk.GLP_UP,
0.0, -constraint.constant)
elif constraint.sense == LpConstraintGE:
row.bounds = -constraint.constant, None
glpk.glp_set_row_bnds(prob, i, glpk.GLP_LO,
-constraint.constant, 0.0)
elif constraint.sense == LpConstraintEQ:
row.bounds = -constraint.constant,-constraint.constant
glpk.glp_set_row_bnds(prob, i, glpk.GLP_FX,
-constraint.constant, -constraint.constant)
else:
raise PulpSolverError, 'Detected an invalid constraint type'
self.callSolver(lp, callback = callback)
@@ -2084,6 +2116,8 @@ class YAPOSIB(LpSolver):
"""
COIN OSI (via its python interface)
Copyright Christophe-Marie Duquesne 2012
The yaposib variables are available (after a solve) in var.solverVar
The yaposib constraints are available in constraint.solverConstraint
The Model is in prob.solverModel
@@ -2105,7 +2139,7 @@ class YAPOSIB(LpSolver):
msg = True,
timeLimit = None,
epgap = None,
solverName = "Clp",
solverName = None,
**solverParams):
"""
Initializes the yaposib solver.
@@ -2119,7 +2153,10 @@ class YAPOSIB(LpSolver):
@param solverParams: not supported
"""
LpSolver.__init__(self, mip, msg)
self.solverName = solverName
if solverName:
self.solverName = solverName
else:
self.solverName = yaposib.available_solvers()[0]
def findSolutionValues(self, lp):
model = lp.solverModel
@@ -2137,7 +2174,7 @@ class YAPOSIB(LpSolver):
#put pi and slack variables against the constraints
for constr in lp.constraints.values():
constr.pi = constr.solverConstraint.dual
constr.slack = constr.solverConstraint.activity
constr.slack = -constr.constant - constr.solverConstraint.activity
if self.msg:
print "yaposib status=", solutionStatus
lp.resolveOK = True

View File

@@ -357,7 +357,8 @@ def pulpTest075(solver):
x = LpVariable("x", 0, 4, LpContinuous, obj + b)
y = LpVariable("y", -1, 1, LpContinuous, 4*obj - c)
z = LpVariable("z", 0, None, LpContinuous, 9*obj + b + c)
if solver.__class__ in [CPLEX_DLL, CPLEX_CMD, COINMP_DLL]:
if solver.__class__ in [CPLEX_DLL, CPLEX_CMD, COINMP_DLL, YAPOSIB,
PYGLPK]:
print "\t Testing column based modelling with empty constraints"
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6})
@@ -378,7 +379,8 @@ def pulpTest080(solver):
prob += c2,"c2"
prob += c3,"c3"
if solver.__class__ in [CPLEX_DLL, CPLEX_CMD, COINMP_DLL, PULP_CBC_CMD]:
if solver.__class__ in [CPLEX_DLL, CPLEX_CMD, COINMP_DLL,
PULP_CBC_CMD, YAPOSIB, PYGLPK]:
print "\t Testing dual variables and slacks reporting"
pulpTestCheck(prob, solver, [LpStatusOptimal],
sol = {x:4, y:-1, z:6},