Computing with PuLP#
PuLP is a Python package for computing solutions of linear programming problems. Let’s learn how to setup a problem with variables and constraints and how to call the solvers to find optimial solutions.
from pulp import *
Example#
Consider the following linear programming problem in standard form:
The function \(f(x_1,x_2,x_3) = 5x_1 + 4x_2 + 3x_3\) is called the objective function. The inequalities are constraints. Our goal is to find values \(x_1\), \(x_2\), \(x_3\) which maxmize the objective function and which satisfies all the contraints.
Define the Problem#
We begin by defining a problem object using the function LpProblem
:
problem = LpProblem("Example",LpMaximize)
where:
"Example"
is the name of the problemLpMaximize
sets the objective to a maximization problem (as ooposed toLpMinimize
)LpProblem
creates apulp
problem object and we save it to a variable calledproblem
Note that LpMaximize
is literally the integer value -1
:
print(LpMaximize)
-1
type(LpMaximize)
int
And LpMinimize
is the integer value 1
:
print(LpMinimize)
1
type(LpMinimize)
int
Therefore we could write equivalently:
problem = LpProblem("Example",-1)
But the variable LpMaximize
makes the pulp
code more explicit.
Define Variables#
Define variables using the function LpVariable
:
x = LpVariable("varName",lowBound,upBound,cat)
where:
x
is the Python variable we use to reference thepulp
variablevarName
is the name we choose for the variablelowBound
is a lower bound (default isNone
)upBound
is an upper bound (default isNone
)cat
is the category of variable:Continuous
,Integer
orBinary
(default isContinuous
)
For example, we could create a continuous variable with no bounds:
x = LpVariable("xVariable")
print(x.name)
xVariable
print(x.lowBound)
None
print(x.upBound)
None
print(x.cat)
Continuous
Let’s create the variables for our example problem and include the lower bounds:
x1 = LpVariable("x1",0)
x2 = LpVariable("x2",0)
x3 = LpVariable("x3",0)
Add Objective Function#
We treat a pulp
problem object as a list-type object and append the objective and constraints using the +=
operator. For example, use the variables we defined above for our example problem:
problem += 5*x1 + 4*x2 + 3*x3
Print the problem object to see its current formulation:
print(problem)
Example:
MAXIMIZE
5*x1 + 4*x2 + 3*x3 + 0
VARIABLES
x1 Continuous
x2 Continuous
x3 Continuous
Add Constraints#
Use the operator +=
to append constraints to the problem. Constraints are expressions involving the variables which represent the linear inequalities:
problem += 2*x1 + 3*x2 + x3 <= 5
problem += 4*x1 + x2 + 2*x3 <= 11
problem += 3*x1 + 4*x2 + 2*x3 <= 8
print(problem)
Example:
MAXIMIZE
5*x1 + 4*x2 + 3*x3 + 0
SUBJECT TO
_C1: 2 x1 + 3 x2 + x3 <= 5
_C2: 4 x1 + x2 + 2 x3 <= 11
_C3: 3 x1 + 4 x2 + 2 x3 <= 8
VARIABLES
x1 Continuous
x2 Continuous
x3 Continuous
Solve#
Call the method .solve()
on the problem to compute the optimal solution (if it exists):
problem.solve()
Welcome to the CBC MILP Solver
Version: 2.10.3
Build Date: Dec 15 2019
command line - /opt/miniconda3/lib/python3.13/site-packages/pulp/apis/../solverdir/cbc/osx/i64/cbc /var/folders/2h/25vcwmr52mvfqs1h92028rph0000gn/T/885f0bcef26c42f798617b3b4983caee-pulp.mps -max -timeMode elapsed -branch -printingOptions all -solution /var/folders/2h/25vcwmr52mvfqs1h92028rph0000gn/T/885f0bcef26c42f798617b3b4983caee-pulp.sol (default strategy 1)
At line 2 NAME MODEL
At line 3 ROWS
At line 8 COLUMNS
At line 21 RHS
At line 25 BOUNDS
At line 26 ENDATA
Problem MODEL has 3 rows, 3 columns and 9 elements
Coin0008I MODEL read with 0 errors
Option for timeMode changed from cpu to elapsed
Presolve 3 (0) rows, 3 (0) columns and 9 (0) elements
0 Obj -0 Dual inf 15 (3)
0 Obj -0 Dual inf 15 (3)
2 Obj 13
Optimal - objective value 13
Optimal objective 13 - 2 iterations time 0.002
Option for printingOptions changed from normal to all
Total time (CPU seconds): 0.00 (Wallclock seconds): 0.02
1
Inspect the status of the problem. The dictionary object LpStatus
translates the value:
problem.status
1
LpStatus
{0: 'Not Solved',
1: 'Optimal',
-1: 'Infeasible',
-2: 'Unbounded',
-3: 'Undefined'}
LpStatus[problem.status]
'Optimal'
Therefore the solution computed by the solver is optimal. Let’s inspect the value of each of the variables and the objective function:
x1.value()
2.0
x2.value()
0.0
x3.value()
1.0
problem.objective.value()
13.0
Put It All Together#
Let’s combine all the code into a single cell to see how it all works:
# Initialize the problem
problem = LpProblem("Example",LpMaximize)
# Define variables (each with lower bound 0)
x1 = LpVariable("x1",0)
x2 = LpVariable("x2",0)
x3 = LpVariable("x3",0)
# Define objective function
problem += 5*x1 + 4*x2 + 3*x3
# Define constraints
problem += 2*x1 + 3*x2 + x3 <= 5
problem += 4*x1 + x2 + 2*x3 <= 11
problem += 3*x1 + 4*x2 + 2*x3 <= 8
# Solve and inspect status
problem.solve()
print("Status :", LpStatus[problem.status])
# Inspect optimal values of variables and objective function
print(x1.name,":",x1.value())
print(x2.name,":",x2.value())
print(x3.name,":",x3.value())
print("Objective :",problem.objective.value())
Welcome to the CBC MILP Solver
Version: 2.10.3
Build Date: Dec 15 2019
command line - /opt/miniconda3/lib/python3.13/site-packages/pulp/apis/../solverdir/cbc/osx/i64/cbc /var/folders/2h/25vcwmr52mvfqs1h92028rph0000gn/T/f75df7992713434182f9fe544a004781-pulp.mps -max -timeMode elapsed -branch -printingOptions all -solution /var/folders/2h/25vcwmr52mvfqs1h92028rph0000gn/T/f75df7992713434182f9fe544a004781-pulp.sol (default strategy 1)
At line 2 NAME MODEL
At line 3 ROWS
At line 8 COLUMNS
At line 21 RHS
At line 25 BOUNDS
At line 26 ENDATA
Problem MODEL has 3 rows, 3 columns and 9 elements
Coin0008I MODEL read with 0 errors
Option for timeMode changed from cpu to elapsed
Presolve 3 (0) rows, 3 (0) columns and 9 (0) elements
0 Obj -0 Dual inf 15 (3)
0 Obj -0 Dual inf 15 (3)
2 Obj 13
Optimal - objective value 13
Optimal objective 13 - 2 iterations time 0.002
Option for printingOptions changed from normal to all
Total time (CPU seconds): 0.00 (Wallclock seconds): 0.00
Status : Optimal
x1 : 2.0
x2 : 0.0
x3 : 1.0
Objective : 13.0