0%

Gurobi Modeling with Python

Gurobi is one of the most powerful mathematical optimization solver. Here I introduce an planning optimization model first, and try to model it with python and gurobi python package.

Planning Model

There are 7 products want to be made by a factory, and each produces need to use a range of machines. The factory have 4 grinders, 2 vertical drills, 3 horizontal drills, 1 borer, and 1 planer.

Time P1 P2 P3 P4 P5 P6 P7
Profit 10 6 8 4 16 9 3
Grinding 0.5 0.7 - - 0.3 0.2 0.5
V drilling 0.1 0.2 - 0.3 - 0.6 -
H drilling 0.2 - 0.8 - - - 0.6
Boring 0.05 0.03 - 0.07 0.1 - 0.08
Planing - - 0.01 - 0.05 - 0.05

According to this table, product 1 contribute 10 $ to profit, and it need manufacturing time on grinding is 0.5 hours, 0.1 hours in vertical drilling, 0.2 hours on horizon drilling, and 0.05 hours in boring. Machines also scheduled for maintenance. See as follows.

Month Machine
Jan One Grinder
Feb Two H Drillers
Mar One Borer
Apr One V Drill
May One Grinder + One V Drill
June One H Drill

Besides, there have limitations to how many of each product can be sold in a given month. At the Jan, there is no product inventory, but at June, there should be 50 units of each products in inventory.

Month P1 P2 P3 P4 P5 P6 P7
Jan 500 1000 300 300 800 200 100
Feb 600 500 200 0 400 300 150
Mar 300 600 0 0 500 400 100
Apr 200 300 400 500 200 0 100
May 0 100 500 100 1000 300 0
June 500 500 100 300 1100 500 60

Mathematical Modeling

Sets

$T:$ Set of monthes, $t{0}$ is the first month, and $t{e}$ is the last month.
$P:$ Set of products.
$M:$ Set of machines.

Parameters

$f{pm}:$ Hours given to product $p$ manufactured on machine $m$.
$l
{tp}:$ Upper bound on sales for each product $p$ in month $t$.
$k{p}:$ Profit for each product $p$.
$q
{tm}:$ Number of avaiable machines $m$ in month $t$.
$g:$ Machine can work $g$ hours per month.
$r:$ Store cost.
$z:$ Store capacity.

Varibales

$b{tp}:$ Number of product $p$ produced in month $t$.
$u
{tp}:$ Number of product $p$ selled in month $t$.
$s_{tp}:$ Number of product $p$ stored in month $t$.

Objective Function

Constraints

The last constraint ensures that per month the time all products needs on a certain kind of machines is lower or equal than the available hours for that machine in that month multiplied by the number of available machines in that month.

Gurobi Python Modeling

Basic Gurobi Concepts

Parameters control the operations of Gurobi solver, for example, the optimizaiton running time. Attributes are primary machanism for querying and modifying properties of a Gurobi model, you can set the variable name by variable.VarName = 'Cost'. Environments are the container for models and global parameters settings.

When we build a model with Gurobi Python API, the whole process is as follows:

The Gurobi Python API support linear and quadratic experssion. Here we give a toy example of solving optimization problem use Gurobi Python API.

The Python code:

Solve The Planning Model

Here we give the Python code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# Data 
from gurobipy import *

# Data
products = ["Prod1", "Prod2", "Prod3", "Prod4", "Prod5", "Prod6", "Prod7"]
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
machines = ["grinder", "vertDrill", "horiDrill", "borer", "planer"]

profit = { "Prod1": 10, "Prod2": 6, "Prod3": 8, "Prod4": 4, "Prod5": 11, "Prod6": 9, "Prod7": 3}
qMachine = { "grinder" : 4, "vertDrill" : 2, "horiDrill" : 3, "borer" : 1, "planer" : 1}

time_table = {
"grinder": {"Prod1": 0.5, "Prod2": 0.7, "Prod5": 0.3, "Prod6": 0.2, "Prod7": 0.5 },
"vertDrill": {"Prod1": 0.1, "Prod2": 0.2, "Prod4": 0.3, "Prod6": 0.6 },
"horiDrill": {"Prod1": 0.2, "Prod3": 0.8, "Prod7": 0.6 },
"borer": {"Prod1": 0.05,"Prod2": 0.03,"Prod4": 0.07, "Prod5": 0.1, "Prod7": 0.08 },
"planer": {"Prod3": 0.01,"Prod5": 0.05,"Prod7": 0.05 }
}

down = {("Jan","grinder") : 1, ("Feb", "horiDrill"): 2, ("Mar", "borer") : 1,
("Apr", "vertDrill"): 1, ("May", "grinder") : 1, ("May", "vertDrill"): 1,
("Jun", "planer") : 1, ("Jun", "horiDrill"): 1
}

upper_dict = {
"Jan" : { "Prod1" : 500, "Prod2" : 1000, "Prod3" : 300, "Prod4" : 300, "Prod5" : 800, "Prod6" : 200, "Prod7" : 100 },
"Feb" : { "Prod1" : 600, "Prod2" : 500, "Prod3" : 200, "Prod4" : 0, "Prod5" : 400, "Prod6" : 300, "Prod7" : 150 },
"Mar" : { "Prod1" : 300, "Prod2" : 600, "Prod3" : 0, "Prod4" : 0, "Prod5" : 500, "Prod6" : 400, "Prod7" : 100 },
"Apr" : { "Prod1" : 200, "Prod2" : 300, "Prod3" : 400, "Prod4" : 500, "Prod5" : 200, "Prod6" : 0, "Prod7" : 100 },
"May" : { "Prod1" : 0, "Prod2" : 100, "Prod3" : 500, "Prod4" : 100, "Prod5" : 1000, "Prod6" : 300, "Prod7" : 0 },
"Jun" : { "Prod1" : 500, "Prod2" : 500, "Prod3" : 100, "Prod4" : 300, "Prod5" : 1100, "Prod6" : 500, "Prod7" : 60 }
}

upper = { (month, product) : upper_dict[month][product] for month in months for product in products }

storeCost = 0.5
storeCapacity = 100
endStock = 50
hoursPerMonth = 2*8*24

# Gurobi Modeling

# build a model
model = Model("Factory Planning")

# decision varibales
manu = model.addVars(months, products, name="manu")
held = model.addVars(months, products, name="held", ub = storeCapacity)
sell = model.addVars(months, products, name="sell", ub = upper)

# constraints
model.addConstrs((manu[months[0], product] == sell[months[0], product] + held[months[0], product]
for product in products),
name="balance")
model.addConstrs((held[months[month_index-1],product] + manu[month, product] == sell[month, product] + held[month, product]
for product in products for month_index, month in enumerate(months) if month != months[0]),
name='balance')
model.addConstrs((held[months[-1], product] == endStock
for product in products),
name='End_Balance')
model.addConstrs((held[month, product] <= endStock
for month in months for product in products),
name='Capacity')
model.addConstrs(((quicksum(time_table[machine][product] * manu[month,product]
for product in time_table[machine]) <= hoursPerMonth * qMachine[machine])
for machine in machines for month in months
if (month, machine) in down),
name='Capacity')
model.addConstrs((quicksum(time_table[machine][product] * manu[month, product]
for product in time_table[machine]) <= hoursPerMonth * qMachine[machine]
for machine in machines for month in months
if (month, machine) not in down), name = "Capacity")

# objective function
obj = quicksum(
profit[product]*sell[month, product] - storeCost*held[month, product]
for month in months for product in products
)

model.setObjective(obj, GRB.MAXIMIZE)

# solve model
model.optimize()

# results
model.printAttr('X')

Conclusion

Gurobi is a very powful mathematical programming solver, and Python is friendly to be used in modelling optimizaiton problem. Here I just post an example with code to learn how to use Gurobi solve real word planning problem. The orginal figures and codes are refer to Gurobi seminars.