## Python PuLP Mathematical Optimization

I have never done optimization calculations with pulp before, so I’ll try to run through the basic usage of pulp according to the reference article.

The following is the article I used as a reference. It is very easy to understand.

### github

- The file in jupyter notebook format on github is here

### google colaboratory

- To run it in google colaboratory here 008/008_nb.ipynb)

### Author’s environment

```
! sw_vers
ProductName: Mac OS X
```

```
ProductName: Mac OS X
ProductVersion: 10.14.6
BuildVersion: 18G6020
```

```
Python -V
```

```
Python 3.7.3
```

### 1. Linear optimization problems (linear programming problems)

I remember doing this in high school math.

#### maximization

$$ x + y $$ x + y

#### constraints

$$
2x + y \leq 2 \\

x + 2y \leq 2 \\

x \geq 0 \\

y \geq 0
$$ $$

```
import pulp
import sys
# Declare the optimization (maximization)
prob = pulp.LpProblem('test', pulp.LpMaximize)
# Declare the variables
x = pulp.LpVariable("xx", 0, sys.maxsize, pulp.LpContinuous)
y = pulp.LpVariable("yy", 0, sys.maxsize, pulp.LpContinuous)
```

```
# Declare the objective function
prob += ( x + y, "Objective" )
# Declare a constraint
prob += ( 2 * x + y <= 2 , "Constraint_1" )
prob += ( x + 2 * y <= 2 , "Constraint_2" )
```

```
prob
```

```
test:
MAXIMIZE
1*xx + 1*yy + 0
SUBJECT TO
Constraint_1: 2 xx + yy <= 2
Constraint_2: xx + 2 yy <= 2
VARIABLES
xx <= 9.22337203685e+18 Continuous
yy <= 9.22337203685e+18 Continuous
```

```
result = prob.solve()
```

```
print("Result")
print("*" * 8)
print(f "Optimality = {pulp.LpStatus[result]}")
print(f "Objective function value = {pulp.value(prob.objective)}")
print(f "solution x = {pulp.value(x)}")
print(f" y = {pulp.value(y)}")
print("*" * 8)
```

```
Calculation results
********
Optimality = Optimal
Objective function value = 1.333333334
Solution x = 0.6666666667
y = 0.66666666667
********
```

### 2. Integer optimization problem (integer programming problem)

#### minimization

$$ \sum_{i \in I} \sum_{j \in J} c_{ij}x_{ij} $$

#### Constraints

$$
\sum_{j\in J}x_{ij} \leq 1 \\

\sum_{i\in I}x_{ij} = 1 \\

x_{ij} \in {0,1}
$$

```
import pulp
import time
# Set of workers (for convenience, we use a list)
I = ["Mr. A", "Mr. B", "Mr. C"].
print(f "Set of workers I = {I}")
# Set of tasks (for convenience, use a list)
J = ["Task A", "Task B", "Task C"].
print(f "Set of tasks J = {J}")
# Set of costs for assigning worker i to task j (temporary list)
cc = [.
[ 1, 2, 3],
[ 4, 6, 8],
[10, 13, 16],
]
# Since cc is a list and the subscripts are numeric.
# Define a dictionary c so that, for example, cc[0][0] can be accessed by c["Mr. A", "Job I"].
c = {} # empty dictionary
for i in I:
for j in J:
c[i,j] = cc[I.index(i)][J.index(j)].
print("Cost c[i,j]: ")
for i in I:
for j in J:
print(f "c[{i},{j}] = {c[i,j]:2d}, ", end = "")
print("")
print("")
```

```
Set of workers I = ['Mr. A', 'Mr. B', 'Mr. C'].
Set of tasks J = ['Task A', 'Task B', 'Task C'].
Cost c[i,j]:
c[Mr. A,Task i] = 1, c[Mr. A,Task b] = 2, c[Mr. A,Task c] = 3,
c[Ms. B,Work A] = 4, c[Ms. B,Work B] = 6, c[Ms. B,Work C] = 8,
c[Mr. C, Job A] = 10, c[Mr. C, Job B] = 13, c[Mr. C, Job C] = 16,
```

```
# Declare mathematical optimization.
# pulp.LpMinimize => Minimize
# pulp.LpMaximize => maximize
prob = pulp.LpProblem('prob', pulp.LpMinimize)
```

```
# Dictionary for a set of variables
x = {} # empty dictionary
# Read and write values in x[i,j] or x[(i,j)], using the tuple (i,j) as key
# Declare a 0-1 variable
for i in I:
for j in J:
x[i,j] = pulp.LpVariable(f "x({i},{j})", 0, 1, pulp.LpInteger)
# Why does the variable label change to '_' when I put '[', ']' or '-'?
# If lowBound and upBound are not specified, they become -infinity and +infinity respectively.
# You can also use comprehension notation.
# x_suffixes = [(i,j) for i in I for j in J].
# LpVariable.dicts("x", x_suffixes, cat = pulp.LpBinary)
# LpContinuous : continuous variable
# LpInteger : Integer variable
# LpBinary : 0-1 variable
```

```
# Declare the objective function
prob += pulp.lpSum(c[i,j] * x[i,j] for i in I for j in J), "TotalCost"
# problem += sum(c[i,j] * x[i,j] for i in I for j in J)
```

```
# Declare constraints
for i in I:
prob += sum(x[i,j] for j in J) <= 1, f "Constraint_leq_{i}"
# If I put '[' or ']' or '-' in the constraint label, why does it change to '_'...?
# For each task j, there is exactly one worker to assign
for j in J:
prob += sum(x[i,j] for i in I) == 1, f "Constraint_eq_{j}"
```

```
# Print the whole expression of the problem
print("Expression in question")
print(f"-" * 8)
print(prob)
print(f"-" * 8)
print("")
```

```
The expression in question
--------
prob:
MINIMIZE
1*x(Mr. A, Job A) + 3*x(Mr. A, Job C) + 2*x(Mr. A, Job B) + 4*x(Mr. B, Job I) + 8*x(Mr. B, Job C) + 6*x(Mr. B, Job B) + 10*x(Mr. C, Job I) + 16*x(Mr. C, Job C) + 13*x(Mr. C, Job B) + 0
SUBJECT TO
Constraint_leq_A: x(Mr. A,Job A) + x(Mr. A,Job C) + x(Mr. A,Job B) <= 1
Constraint_leq_B: x(Mr. B, Job A) + x(Mr. B, Job C) + x(Mr. B, Job B) <= 1
Constraint_leq_C: x(Mr. C,Job A) + x(Mr. C,Job C) + x(Mr. C,Job B) <= 1
Constraint_eq_work_i: x(Ms A,work_i) + x(Ms B,work_i) + x(Ms C,work_i) = 1
Constraint_eq_Work_R: x(Mr. A,Work_R) + x(Mr. B,Work_R) + x(Mr. C,Work_R) = 1
Constraint_eq_Job_H: x(A,Job_H) + x(B,Job_H) + x(C,Job_H) = 1
VARIABLES
0 <= x(Ms. A,Job I) <= 1 Integer
0 <= x(Ms. A,Job C) <= 1 Integer
0 <= x(Mr. A, Job B) <= 1 Integer
0 <= x(Mr. B, Job A) <= 1 Integer
0 <= x(Ms. B, Job C) <= 1 Integer
0 <= x(Mr. B, Job B) <= 1 Integer
0 <= x(Ms. C, Job A) <= 1 Integer
0 <= x(Ms. C, Job C) <= 1 Integer
0 <= x(Mr. C, Job B) <= 1 Integer
--------
```

```
# Compute
# Solver specification
solver = pulp.PULP_CBC_CMD()
# pulp.PULP_CBC_CMD() : Coin-CBC that comes with PuLP
# pulp.GUROBI_CMD() : Start Gurobi from command line (.lp file is created temporarily)
# GUROBI() : Launch Gurobi from the library (need to specify the location of the library)
# Start time measurement
time_start = time.perf_counter()
result_status = prob.solve(solver)
# Solver can be specified in () of solve().
# If nothing is specified, pulp.PULP_CBC_CMD()
# End of time measurement
time_stop = time.perf_counter()
```

```
# Print the objective function value and solution (if the solution was obtained)
print("Calculation result")
print(f "*" * 8)
print(f "Optimality = {pulp.LpStatus[result_status]}, ", end="")
print(f "objective function value = {pulp.value(prob.objective)}, ", end="")
print(f "Computation time = {time_stop - time_start:.3f} (in seconds)")
print("Solution x[i,j]: ")
for i in I:
for j in J:
print(f"{x[i,j].name} = {x[i,j].value()}, ", end="")
print("")
print(f "*" * 8)
```

```
Calculation results
********
Optimality = Optimal, Objective function value = 19.0, Computation time = 0.028 (sec)
Solution x[i,j]:
x(Mr. A,Job a) = 0.0, x(Mr. A,Job b) = 0.0, x(Mr. A,Job c) = 1.0,
x(Ms. B, job a) = 0.0, x(Ms. B, job b) = 1.0, x(Ms. B, job c) = 0.0,
x(Ms. C, job a) = 1.0, x(Ms. C, job b) = 0.0, x(Ms. C, job c) = 0.0,
********
```

I learned a lot, line by line. Please jump to the link and study it in the original article.