title: "Lecture 2: Introduction to Linear Programming" author: "Junaid Hasan" date: "June 23, 2021"¶
A sample problem¶
Jack wants to buy oranges and apples.
Suppose apples are priced at 3 dollars a pound and oranges at 4 dollars a pound.
Each pound of apples has 70 grams of protein while each pound of orange has 50 grams of protein.
A pound of apples has 50 grams of fiber while a pound of oranges has 60 grams of fiber.
He wants to have at least 100 grams of fiber and at least 50 grams of protein.
How much apples and oranges should he buy to minimize cost.
The Mathematical Model¶
- Suppose Jack buys $x$ pounds of apples and $y$ pounds of oranges.
- He wants to minimize $3x + 4y$.
- However he must ensure that $70x + 50y \geq 50$ (protein requirement) and
- that $50 x + 60 y \geq 60$ for the fiber requirement.
Discussion¶
- What do you notice here?
Solution by hand¶
- Lets plot something
(continued)..¶
from pyscipopt import Model
model = Model("Apple and Oranges")
x = model.addVar("x")
y = model.addVar("y")
model.addCons(70*x + 50*y >=50, "protein requirement")
model.addCons(50*x + 80*y >=60, "fiber requirement")
model.setObjective(3*x + 4*y, "minimize")
model.optimize()
if model.getStatus() == "optimal":
print("Optimal value:", model.getObjVal())
print("Solution:")
print(" x = ", model.getVal(x))
print(" y = ", model.getVal(y))
else:
print("Problem could not be solved to optimaly")
Optimal value: 3.161290322580645 Solution: x = 0.3225806451612903 y = 0.5483870967741935
Takeaways¶
- The problem has a linear (objective function) max/min objective.
- The constraints are linear as well.
- Implicit non-negativity constraints.
- Sometimes could be integer constraints (Integer LP).
- This kind of problem is known as a Linear Programming Problem.
- If you are interested in the history check out Wikipedia.
Standard Terminology¶
A problem of the form $$ \begin{aligned} \text{maximize/minimize } & c_1 x_1 + c_2 x_2 \cdots + c_n x_n\\ \text{subject to } & a_{1,1} x_1 + \cdots + a_{1,n}x_n \leq \text{(or)} \geq & b_1 \\ & a_{2,1} x_1 + \cdots + a_{2,n}x_n \leq \text{(or)} \geq & b_2 \\ & \vdots \\ & a_{m,1} x_1 + \cdots + a_{m,n}x_n \leq \text{(or)} \geq & b_m \\ & x_1, x_2, \ldots, x_n \geq 0 \end{aligned} $$
is called a standard linear programming problem.
Another Example: Transportation Problem¶
- A sports equipment company XYZ has products manufactured at three factories(j =1,2,3) and delivered to five stores(i = 1,2,3,4,5). What to do to minimize cost.
- Lets us draw the scenario
Transportation Problem contd.¶
- As a table it is given by
Problem Formulation¶
- Let $x_{ij}$ be the amount of goods transported from factory j to customer i.
- Then can you write the optimization problem.
Answer:¶
- $$ \begin{aligned} \text{minimize } & \sum_{i \in I}\sum_{j \in J} c_{ij}x_{ij}\\ \text{subject to demand } & \ \sum_{j \in J}x_{ij} = d_i & \forall i \in I\\ \text{ and factory capacity }& \sum_{i \in I}x_{ij} \leq M_j & \forall j \in J \\ & x_{ij} \geq 0 &\forall i \in I, j \in J \end{aligned} $$
Solution via code¶
- We will use a special datatype in Python to our advantage. It is a dictionary(or arrays would work fine as well). It allows to specify the keys and the values at the keys
{key1:value1, key2:value2, ...} - For demand
demand = {1:80, 2:270, 3:250, 4:160, 5:180} - For capacity
capacity = {1:500, 2:500, 3:500} - List(arrays in Python are called lists) of customers
I = [1,2,3,4,5] - Factories
J = [1,2,3] - Shipping cost would be a 2D array. We use dictionaries as
cost = {(1,1):4, (1,2):6, (1,3):9(2,1):5, (2,2):4, (2,3):7,(3,1):6, (3,2):3, (3,3):3,(4,1):8, (4,2):5, (4,3):3,
(5,1):10,
}
from pyscipopt import Model, quicksum
demand = {1:80, 2:270, 3:250, 4:160, 5:180}
capacity = {1:500, 2:500, 3:500}
I = [1,2,3,4,5]
J = [1,2,3]
cost = {(1,1):4, (1,2):6, (1,3):9,
(2,1):5, (2,2):4, (2,3):7,
(3,1):6, (3,2):3, (3,3):3,
(4,1):8, (4,2):5, (4,3):3,
(5,1):10, (5,2):8, (5,3):4,
}
model = Model("transportation")
x = {}
for i in I:
for j in J:
x[i,j] = model.addVar(vtype="C", name="x(%s,%s)" % (i,j))
The quicksum function below is available in SCIP to sum over a for loop (iterator) and saves us a for loop
for i in I:
model.addCons(quicksum(x[i,j] for j in J) == demand[i], name="Demand(%s)" % i)
for j in J:
model.addCons(quicksum(x[i,j] for i in I) <= capacity[j], name="Capacity(%s)" % j)
model.setObjective(quicksum(cost[i,j]*x[i,j] for (i,j) in x), "minimize")
model.optimize()
print("Optimal value:", model.getObjVal())
for (i,j) in x:
print("sending quantity %10s from factory %3s to customer %3s" % (model.getVal(x[i,j]),j,i))
Optimal value: 3350.0 sending quantity 80.0 from factory 1 to customer 1 sending quantity 0.0 from factory 2 to customer 1 sending quantity 0.0 from factory 3 to customer 1 sending quantity 0.0 from factory 1 to customer 2 sending quantity 270.0 from factory 2 to customer 2 sending quantity 0.0 from factory 3 to customer 2 sending quantity 0.0 from factory 1 to customer 3 sending quantity 230.0 from factory 2 to customer 3 sending quantity 20.0 from factory 3 to customer 3 sending quantity 0.0 from factory 1 to customer 4 sending quantity 0.0 from factory 2 to customer 4 sending quantity 160.0 from factory 3 to customer 4 sending quantity 0.0 from factory 1 to customer 5 sending quantity 0.0 from factory 2 to customer 5 sending quantity 180.0 from factory 3 to customer 5
We can avoid the 0.0 by an if statement
"x%s" %1
'x1'
print("Optimal value:", model.getObjVal())
for (i,j) in x:
if model.getVal(x[i,j]) > 0.000001:
print("sending quantity %10s from factory %3s to customer %3s" % (model.getVal(x[i,j]),j,i))
Optimal value: 3350.0 sending quantity 80.0 from factory 1 to customer 1 sending quantity 270.0 from factory 2 to customer 2 sending quantity 230.0 from factory 2 to customer 3 sending quantity 20.0 from factory 3 to customer 3 sending quantity 160.0 from factory 3 to customer 4 sending quantity 180.0 from factory 3 to customer 5