Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set MIP start (initial solution) with Gurobi solver from PuLP?

I'm using the PuLP module in Python to formulate a mixed integer program. I am trying to work out how to set a MIP start (i.e. a feasible solution for the program to start from) via the PuLP interface.

Details on how to set MIP start are given here

And the developer of the PuLP package claims that you can access the full Gurobi model via the PuLP interface here

Pasted below are two complete models. I have made these as small as possible whilst preventing the gurobi solver from finding the optimal value using a heuristic.

I have attempted to set an initial solution (to the optimal values) in both models, but in the PuLP model it is ignored, but in the gurobipy model it works as expected.

How do you set an initial solution for the Gurobi solve via the PuLP interface?

from pulp import *

prob = LpProblem("min example",LpMinimize)

x1=LpVariable("x1",0,None,LpInteger)
x2=LpVariable("x2",0,None,LpInteger)
x3=LpVariable("x3",0,None,LpInteger)
x4=LpVariable("x4",0,None,LpInteger)

# Objective function
prob += 3*x1 + 5*x2 + 6*x3 + 9*x4

# A constraint
prob += -2*x1 + 6*x2 -3*x3 + 4*x4 >= 2, "Con1"
prob += -5*x1 + 3*x2 + x3 + 3*x4 >= -2, "Con2"
prob += 5*x1 - x2 + 4*x3 - 2*x4 >= 3, "Con3"

# Choose solver, and set it to problem, and build the Gurobi model
solver = pulp.GUROBI()
prob.setSolver(solver)
prob.solver.buildSolverModel(prob)

# Attempt to set an initial feasible solution (in this case to an optimal solution)
prob.solverModel.getVars()[0].start = 1
prob.solverModel.getVars()[1].start = 1
prob.solverModel.getVars()[2].start = 0
prob.solverModel.getVars()[3].start = 0

# Solve model
prob.solve()

# Status of the solution is printed to the screen
print "Status:", LpStatus[prob.status]

# Each of the variables is printed with it's resolved optimum value
for v in prob.variables():
    print v.name, "=", v.varValue

# Optimised objective function value is printed to the screen
print "OF = ", value(prob.objective)

Which returns:

Optimize a model with 3 rows, 4 columns and 12 nonzeros
Coefficient statistics:
  Matrix range    [1e+00, 6e+00]
  Objective range [3e+00, 9e+00]
  Bounds range    [0e+00, 0e+00]
  RHS range       [2e+00, 3e+00]
Found heuristic solution: objective 12
Presolve removed 0 rows and 1 columns
Presolve time: 0.00s
Presolved: 3 rows, 3 columns, 9 nonzeros
Variable types: 0 continuous, 3 integer (0 binary)

Root relaxation: objective 7.400000e+00, 1 iterations, 0.00 seconds

    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time

     0     0    7.40000    0    1   12.00000    7.40000  38.3%     -    0s
H    0     0                       8.0000000    7.40000  7.50%     -    0s

Explored 0 nodes (1 simplex iterations) in 0.00 seconds
Thread count was 8 (of 8 available processors)

Optimal solution found (tolerance 1.00e-04)
Best objective 8.000000000000e+00, best bound 8.000000000000e+00, gap 0.0%
('Gurobi status=', 2)
Status: Optimal
x1 = 1.0
x2 = 1.0
x3 = -0.0
x4 = -0.0
OF =  8.0

Secondly I can implement the same model using the gurobipy module, but in this case the MIP start is actually used:

from gurobipy import *

m = Model("min example")
m.modelSense = GRB.MINIMIZE

objFcnCoeffs = [3, 5, 6, 9]
xVars = []
for i in range(4):
    xVars.append(m.addVar(vtype=GRB.INTEGER, obj=objFcnCoeffs[i], name="Open%d" % i))

# Update model to integrate new variables
m.update()

# Constraints
m.addConstr(-2*xVars[0] + 6*xVars[1] -3*xVars[2] + 4*xVars[3] >= 2, "Con1")
m.addConstr(-5*xVars[0] + 3*xVars[1] + xVars[2] + 3*xVars[3] >= -2, "Con2")
m.addConstr(5*xVars[0] - xVars[1] + 4*xVars[2] - 2*xVars[3] >= 3, "Con3")


# Attempt to set an initial feasible solution (in this case to an optimal solution)
startValues = [1, 1, 0, 0]
for i in range(4):
    xVars[i].start = startValues[i]

# Solve model
m.optimize()

# Print solution
print('\nTOTAL COSTS: %g' % m.objVal)
for i in range(4):
    print('\n xVar[%s] = %g' % i, xVars[i])

Which returns:

Optimize a model with 3 rows, 4 columns and 12 nonzeros
Coefficient statistics:
  Matrix range    [1e+00, 6e+00]
  Objective range [3e+00, 9e+00]
  Bounds range    [0e+00, 0e+00]
  RHS range       [2e+00, 3e+00]
Found heuristic solution: objective 12
Presolve removed 0 rows and 1 columns
Presolve time: 0.00s
Presolved: 3 rows, 3 columns, 9 nonzeros

Loaded MIP start with objective 8

Variable types: 0 continuous, 3 integer (0 binary)

Root relaxation: infeasible, 0 iterations, 0.00 seconds

Explored 0 nodes (0 simplex iterations) in 0.00 seconds
Thread count was 8 (of 8 available processors)

Optimal solution found (tolerance 1.00e-04)
Best objective 8.000000000000e+00, best bound 8.000000000000e+00, gap 0.0%

TOTAL COSTS: 8

 xVar[0] = 1

 xVar[1] = 1

 xVar[2] = 0

 xVar[3] = 0
like image 312
kabdulla Avatar asked Oct 20 '16 01:10

kabdulla


People also ask

What is the difference between MIP and MILP?

MIP models with quadratic constraints are called Mixed Integer Quadratically Constrained Programming (MIQCP) problems. Models without any quadratic features are often referred to as Mixed Integer Linear Programming (MILP) problems.

What is Presolve in gurobi?

Presolve transforms your model into an equivalent model that theoretically has the following properties: The presolved model is infeasible if and only if the original model is infeasible. The presolved model is unbounded if and only if the original model is unbounded.

What method does gurobi use?

The default simplex algorithm in the Gurobi solver is dual simplex, which tries to maintain dual feasibility while performing simplex pivots to improve the objective. Thus, once the dual simplex algorithm has found an initial dual feasible basis, you will generally see a dual infeasibility value of zero.


2 Answers

You are setting the start values like this

prob.solverModel.getVars()[0].start = 1

and you are then solving the model with this call

prob.solve().

The oritinal prob is not changed, if you call

prob.solver.callSolver(prob)

Gurobi will use the start vector.

like image 124
Sonja Mars Avatar answered Nov 03 '22 01:11

Sonja Mars


Very late to the question but hopefully this will help new visitors. Starting in version 2.3 of PuLP, the common warmStart interface supports the GUROBI api. By following the instructions here you should be able to warm start the gurobi solver without having to tinker with the pulp internals or the gurobi package.

like image 31
pchtsp Avatar answered Nov 03 '22 02:11

pchtsp