## 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

• 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

$$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)

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'].
Cost c[i,j]:
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,
********