Files
deb-python-pulp/src/pulp/tests.py
2015-03-18 00:05:04 +13:00

597 lines
20 KiB
Python

"""
Tests for pulp
"""
from .pulp import *
def pulpTestCheck(prob, solver, okstatus, sol = {},
reducedcosts = None,
duals = None,
slacks = None,
eps = 10**-3,
status = None,
**kwargs):
if status is None:
status = prob.solve(solver, **kwargs)
if status not in okstatus:
prob.writeLP("debug.lp")
prob.writeMPS("debug.mps")
print("Failure: status ==", status, "not in", okstatus)
print("Failure: status ==", LpStatus[status], "not in", \
[LpStatus[s] for s in okstatus])
raise PulpError("Tests failed for solver %s"%solver)
if sol:
for v,x in sol.items():
if abs(v.varValue - x) > eps:
prob.writeLP("debug.lp")
prob.writeMPS("debug.mps")
print("Test failed: var", v, "==", v.varValue, "!=", x)
raise PulpError("Tests failed for solver %s"%solver)
if reducedcosts:
for v,dj in reducedcosts.items():
if abs(v.dj - dj) > eps:
prob.writeLP("debug.lp")
prob.writeMPS("debug.mps")
print("Test failed: var.dj", v, "==", v.dj, "!=", dj)
raise PulpError("Tests failed for solver %s"%solver)
if duals:
for cname,p in duals.items():
c = prob.constraints[cname]
if abs(c.pi - p) > eps:
prob.writeLP("debug.lp")
prob.writeMPS("debug.mps")
print("Test failed: constraint.pi", cname , "==", c.pi, "!=", p)
raise PulpError("Tests failed for solver %s"%solver)
if slacks:
for cname,slack in slacks.items():
c = prob.constraints[cname]
if abs(c.slack - slack) > eps:
prob.writeLP("debug.lp")
prob.writeMPS("debug.mps")
print(("Test failed: constraint.slack", cname , "==",
c.slack, "!=", slack))
raise PulpError("Tests failed for solver %s"%solver)
def pulpTest001(solver):
"""
Test that a variable is deleted when it is suptracted to 0
"""
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
c1 = x+y <= 5
c2 = c1 + z -z
print("\t Testing zero subtraction")
assert str(c2) #will raise an exception
def pulpTest009(solver):
# infeasible
prob = LpProblem("test09", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w", 0)
prob += x + 4*y + 9*z, "obj"
prob += lpSum([v for v in [x] if False]) >= 5, "c1" #this is a 0 >=5 constraint
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob += w >= 0, "c4"
print("\t Testing inconsistant lp solution")
#this was a problem with use_mps=false
if solver.__class__ in [PULP_CBC_CMD, COIN_CMD]:
pulpTestCheck(prob, solver, [LpStatusInfeasible], {x:4, y:-1, z:6, w:0},
use_mps = False)
else:
pulpTestCheck(prob, solver, [LpStatusInfeasible, LpStatusNotSolved,
LpStatusUndefined])
def pulpTest010(solver):
# Continuous
prob = LpProblem("test010", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w", 0)
prob += x + 4*y + 9*z, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob += w >= 0, "c4"
print("\t Testing continuous LP solution")
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6, w:0})
def pulpTest011(solver):
# Continuous Maximisation
prob = LpProblem("test011", LpMaximize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w", 0)
prob += x + 4*y + 9*z, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob += w >= 0, "c4"
print("\t Testing maximize continuous LP solution")
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:1, z:8, w:0})
def pulpTest012(solver):
# Unbounded
prob = LpProblem("test012", LpMaximize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w", 0)
prob += x + 4*y + 9*z + w, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob += w >= 0, "c4"
print("\t Testing unbounded continuous LP solution")
if solver.__class__ in [GUROBI, CPLEX_CMD, YAPOSIB, CPLEX_PY]:
# These solvers report infeasible or unbounded
pulpTestCheck(prob, solver, [LpStatusInfeasible])
elif solver.__class__ in [COINMP_DLL,]:
# COINMP_DLL is just plain wrong
print('\t\t Error in CoinMP it reports Optimal')
pulpTestCheck(prob, solver, [LpStatusOptimal])
elif solver.__class__ is GLPK_CMD:
# GLPK_CMD Does not report unbounded problems, correctly
pulpTestCheck(prob, solver, [LpStatusUndefined])
elif solver.__class__ in [CPLEX_DLL, GUROBI_CMD]:
# CPLEX_DLL Does not report unbounded problems, correctly
# GUROBI_CMD has a very simple interface
pulpTestCheck(prob, solver, [LpStatusNotSolved])
else:
pulpTestCheck(prob, solver, [LpStatusUnbounded])
def pulpTest013(solver):
# Long name
prob = LpProblem("test013", LpMinimize)
x = LpVariable("x"*120, 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w", 0)
prob += x + 4*y + 9*z, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob += w >= 0, "c4"
print("\t Testing Long Names")
if solver.__class__ in [CPLEX_CMD, GLPK_CMD, GUROBI_CMD]:
try:
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6, w:0})
except PulpError:
#these solvers should raise an error'
pass
else:
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6, w:0})
def pulpTest014(solver):
# repeated name
prob = LpProblem("test014", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("x", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w", 0)
prob += x + 4*y + 9*z, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob += w >= 0, "c4"
print("\t Testing repeated Names")
if solver.__class__ in [COIN_CMD, PULP_CBC_CMD, CPLEX_CMD, CPLEX_PY,
GLPK_CMD, GUROBI_CMD]:
try:
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6, w:0})
except PulpError:
#these solvers should raise an error'
pass
else:
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6, w:0})
def pulpTest015(solver):
# zero constraint
prob = LpProblem("test015", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w", 0)
prob += x + 4*y + 9*z, "obj"
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 zero constraint")
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6, w:0})
def pulpTest016(solver):
# zero objective
prob = LpProblem("test016", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w", 0)
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 zero objective")
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 pulpTest018(solver):
# Long name in lp
prob = LpProblem("test013", LpMinimize)
x = LpVariable("x"*90, 0, 4)
y = LpVariable("y"*90, -1, 1)
z = LpVariable("z"*90, 0)
w = LpVariable("w"*90, 0)
prob += x + 4*y + 9*z, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob += w >= 0, "c4"
if solver.__class__ in [PULP_CBC_CMD, COIN_CMD]:
print("\t Testing Long lines in LP")
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6, w:0},
use_mps=False)
def pulpTest019(solver):
# divide
prob = LpProblem("test019", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w", 0)
prob += x + 4*y + 9*z, "obj"
prob += (2 * x + 2 * y).__div__(2.0) <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob += w >= 0, "c4"
print("\t Testing LpAffineExpression divide")
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6, w:0})
def pulpTest020(solver):
# MIP
prob = LpProblem("test020", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0, None, LpInteger)
prob += x + 4*y + 9*z, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7.5, "c3"
print("\t Testing MIP solution")
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:3, y:-0.5, z:7})
def pulpTest030(solver):
# relaxed MIP
prob = LpProblem("test030", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0, None, LpInteger)
prob += x + 4*y + 9*z, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7.5, "c3"
solver.mip = 0
print("\t Testing MIP relaxation")
if solver.__class__ in [GUROBI_CMD]:
#gurobi command does not let the problem be relaxed
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:3.0, y:-0.5, z:7})
else:
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:3.5, y:-1, z:6.5})
def pulpTest040(solver):
# Feasibility only
prob = LpProblem("test040", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0, None, LpInteger)
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7.5, "c3"
print("\t Testing feasibility problem (no objective)")
pulpTestCheck(prob, solver, [LpStatusOptimal])
def pulpTest050(solver):
# Infeasible
prob = LpProblem("test050", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0, 10)
prob += x+y <= 5.2, "c1"
prob += x+z >= 10.3, "c2"
prob += -y+z == 17.5, "c3"
print("\t Testing an infeasible problem")
if solver.__class__ is GLPK_CMD:
# GLPK_CMD return codes are not informative enough
pulpTestCheck(prob, solver, [LpStatusUndefined])
elif solver.__class__ in [CPLEX_DLL, GUROBI_CMD]:
# CPLEX_DLL Does not solve the problem
pulpTestCheck(prob, solver, [LpStatusNotSolved])
else:
pulpTestCheck(prob, solver, [LpStatusInfeasible])
def pulpTest060(solver):
# Integer Infeasible
prob = LpProblem("test060", LpMinimize)
x = LpVariable("x", 0, 4, LpInteger)
y = LpVariable("y", -1, 1, LpInteger)
z = LpVariable("z", 0, 10, LpInteger)
prob += x+y <= 5.2, "c1"
prob += x+z >= 10.3, "c2"
prob += -y+z == 7.4, "c3"
print("\t Testing an integer infeasible problem")
if solver.__class__ in [GLPK_CMD, COIN_CMD, PULP_CBC_CMD]:
# GLPK_CMD returns InfeasibleOrUnbounded
pulpTestCheck(prob, solver, [LpStatusInfeasible, LpStatusUndefined])
elif solver.__class__ in [COINMP_DLL]:
#Currently there is an error in COINMP for problems where
#presolve eliminates too many variables
print("\t\t Error in CoinMP to be fixed, reports Optimal")
pulpTestCheck(prob, solver, [LpStatusOptimal])
elif solver.__class__ in [GUROBI_CMD]:
pulpTestCheck(prob, solver, [LpStatusNotSolved])
else:
pulpTestCheck(prob, solver, [LpStatusInfeasible])
def pulpTest070(solver):
#Column Based modelling of PulpTest1
prob = LpProblem("test070", LpMinimize)
obj = LpConstraintVar("obj")
# constraints
a = LpConstraintVar("C1", LpConstraintLE, 5)
b = LpConstraintVar("C2", LpConstraintGE, 10)
c = LpConstraintVar("C3", LpConstraintEQ, 7)
prob.setObjective(obj)
prob += a
prob += b
prob += c
# Variables
x = LpVariable("x", 0, 4, LpContinuous, obj + a + b)
y = LpVariable("y", -1, 1, LpContinuous, 4*obj + a - c)
z = LpVariable("z", 0, None, LpContinuous, 9*obj + b + c)
print("\t Testing column based modelling")
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6})
def pulpTest075(solver):
#Column Based modelling of PulpTest1 with empty constraints
prob = LpProblem("test075", LpMinimize)
obj = LpConstraintVar("obj")
# constraints
a = LpConstraintVar("C1", LpConstraintLE, 5)
b = LpConstraintVar("C2", LpConstraintGE, 10)
c = LpConstraintVar("C3", LpConstraintEQ, 7)
prob.setObjective(obj)
prob += a
prob += b
prob += c
# Variables
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, YAPOSIB,
PYGLPK]:
print("\t Testing column based modelling with empty constraints")
pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6})
def pulpTest080(solver):
"""
Test the reporting of dual variables slacks and reduced costs
"""
prob = LpProblem("test080", LpMinimize)
x = LpVariable("x", 0, 5)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
c1 = x+y <= 5
c2 = x+z >= 10
c3 = -y+z == 7
prob += x + 4*y + 9*z, "obj"
prob += c1, "c1"
prob += c2,"c2"
prob += c3,"c3"
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},
reducedcosts = {x:0, y:12, z:0},
duals = {"c1":0, "c2":1, "c3":8},
slacks = {"c1":2, "c2":0, "c3":0})
def pulpTest090(solver):
#Column Based modelling of PulpTest1 with a resolve
prob = LpProblem("test090", LpMinimize)
obj = LpConstraintVar("obj")
# constraints
a = LpConstraintVar("C1", LpConstraintLE, 5)
b = LpConstraintVar("C2", LpConstraintGE, 10)
c = LpConstraintVar("C3", LpConstraintEQ, 7)
prob.setObjective(obj)
prob += a
prob += b
prob += c
prob.setSolver(solver)# Variables
x = LpVariable("x", 0, 4, LpContinuous, obj + a + b)
y = LpVariable("y", -1, 1, LpContinuous, 4*obj + a - c)
prob.resolve()
z = LpVariable("z", 0, None, LpContinuous, 9*obj + b + c)
if solver.__class__ in [CPLEX_DLL, COINMP_DLL]:
print("\t Testing resolve of problem")
prob.resolve()
#difficult to check this is doing what we want as the resolve is
#over ridden if it is not implemented
#pulpTestCheck(prob, solver, [LpStatusOptimal], {x:4, y:-1, z:6})
def pulpTest100(solver):
"""
Test the ability to sequentially solve a problem
"""
# set up a cubic feasible region
prob = LpProblem("test100", LpMinimize)
x = LpVariable("x", 0, 1)
y = LpVariable("y", 0, 1)
z = LpVariable("z", 0, 1)
obj1 = x+0*y+0*z
obj2 = 0*x-1*y+0*z
prob += x <= 1, "c1"
if solver.__class__ in [CPLEX_DLL, COINMP_DLL, GUROBI]:
print("\t Testing Sequential Solves")
status = prob.sequentialSolve([obj1,obj2], solver = solver)
pulpTestCheck(prob, solver, [[LpStatusOptimal,LpStatusOptimal]],
sol = {x:0, y:1},
status = status)
def pulpTest110(solver):
"""
Test the ability to use fractional constraints
"""
prob = LpProblem("test110", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w", 0)
prob += x + 4*y + 9*z, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob += w >= 0, "c4"
prob += LpFractionConstraint(x, z, LpConstraintEQ, 0.5, name = 'c5')
print("\t Testing fractional constraints")
pulpTestCheck(prob, solver, [LpStatusOptimal],
{x:10/3.0, y:-1/3.0, z:20/3.0, w:0})
def pulpTest120(solver):
"""
Test the ability to use Elastic constraints
"""
prob = LpProblem("test120", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w")
prob += x + 4*y + 9*z + w, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob.extend((w >= -1).makeElasticSubProblem())
print("\t Testing elastic constraints (no change)")
pulpTestCheck(prob, solver, [LpStatusOptimal],
{x:4, y:-1, z:6, w:-1})
def pulpTest121(solver):
"""
Test the ability to use Elastic constraints
"""
prob = LpProblem("test121", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w")
prob += x + 4*y + 9*z + w, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob.extend((w >= -1).makeElasticSubProblem(proportionFreeBound = 0.1))
print("\t Testing elastic constraints (freebound)")
pulpTestCheck(prob, solver, [LpStatusOptimal],
{x:4, y:-1, z:6, w:-1.1})
def pulpTest122(solver):
"""
Test the ability to use Elastic constraints (penalty unchanged)
"""
prob = LpProblem("test122", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w")
prob += x + 4*y + 9*z + w, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob.extend((w >= -1).makeElasticSubProblem(penalty = 1.1))
print("\t Testing elastic constraints (penalty unchanged)")
pulpTestCheck(prob, solver, [LpStatusOptimal],
{x:4, y:-1, z:6, w:-1.0})
def pulpTest123(solver):
"""
Test the ability to use Elastic constraints (penalty unbounded)
"""
prob = LpProblem("test123", LpMinimize)
x = LpVariable("x", 0, 4)
y = LpVariable("y", -1, 1)
z = LpVariable("z", 0)
w = LpVariable("w")
prob += x + 4*y + 9*z + w, "obj"
prob += x+y <= 5, "c1"
prob += x+z >= 10, "c2"
prob += -y+z == 7, "c3"
prob.extend((w >= -1).makeElasticSubProblem(penalty = 0.9))
print("\t Testing elastic constraints (penalty unbounded)")
if solver.__class__ in [COINMP_DLL, GUROBI, CPLEX_CMD, CPLEX_PY, YAPOSIB]:
# COINMP_DLL Does not report unbounded problems, correctly
pulpTestCheck(prob, solver, [LpStatusInfeasible])
elif solver.__class__ is GLPK_CMD:
# GLPK_CMD Does not report unbounded problems, correctly
pulpTestCheck(prob, solver, [LpStatusUndefined])
elif solver.__class__ in [CPLEX_DLL, GUROBI_CMD]:
# GLPK_CMD Does not report unbounded problems, correctly
pulpTestCheck(prob, solver, [LpStatusNotSolved])
else:
pulpTestCheck(prob, solver, [LpStatusUnbounded])
def pulpTestSolver(solver, msg = 0):
tests = [
pulpTest001,
pulpTest009,
pulpTest010, pulpTest011, pulpTest012, pulpTest013, pulpTest014,
pulpTest015, pulpTest016, pulpTest017,
pulpTest018, pulpTest019,
pulpTest020,
pulpTest030,
pulpTest040,
pulpTest050,
pulpTest060,
pulpTest070, pulpTest075,
pulpTest080,
pulpTest090,
pulpTest100,
pulpTest110,
pulpTest120, pulpTest121, pulpTest122, pulpTest123
]
for t in tests:
t(solver(msg=msg))