title: "Lecture 6: Multiple Knapsacks and Logic" author: "Junaid Hasan" date: "July 2, 2021"¶
Multiple Knapsacks¶
- Last Friday we talked about the Knapsack problem.
- Let us consider an extension to the Knapsack Problem:
- We start with a collection of items with varying weights and values.
- However, now we can pack them in N equal knapsacks (say 5 knapsacks).
- Again, we want to pack so that the total volume is maximum.
Contd..¶
Suppose we have 5 knapsacks(bins) each of maximum allowed weight 100.
The weights are
weights = {48, 30, 42, 36, 36, 48,42, 42, 36, 24, 30, 30, 42, 36, 36}The values are
values = {10, 30, 25, 50, 35, 30,15, 40, 30, 35, 45, 10, 20, 30, 25}How to pack?
In [3]:
weights = {1:48, 2:30, 3:42, 4:36, 5:36, 6:48, 7:42, 8:42, 9:36, 10:24, 11:30, 12:30, 13:42, 14:36, 15:36}
values = {1:10, 2:30, 3:25, 4:50, 5:35, 6:30, 7:15, 8:40, 9:30, 10:35, 11:45, 12:10, 13:20, 14:30, 15:25}
items = range(1, len(weights)+1)
bin_capacities = {1:100, 2:100, 3:100, 4:100, 5:100}
bins = range(1, len(bin_capacities)+1)
sum(weights)
Out[3]:
120
Model: Multiple Knapsack¶
- Let us consider
data[i,j]as a binary variable which is 1 when item i is packed in bin j. - Constraints:
- Each item goes in exactly one bin for each i $$\sum_{j} data[i,j] \leq 1$$
- Bin capacity for each bin j $$\sum_{i} data[i,j]\cdot weights[i] \leq 100$$
- Maximize value $$\sum_{i} data[i,j]\cdot values[i]$$
In [4]:
from pyscipopt import Model, quicksum
In [5]:
model = Model("Multiple Knapsack")
data = {}
for i in items:
for j in bins:
data[(i,j)] = model.addVar(vtype="B", name="Object %s " %i + " in bin %s" %j)
In [6]:
for i in items:
model.addCons(quicksum(data[(i,j)] for j in bins) <= 1, "item %s" %i +" is in exactly one bin")
for j in bins:
model.addCons(quicksum(data[(i,j)]*weights[i] for i in items) <= bin_capacities[j], "bin %s" %j +" must not exceed capacity")
In [7]:
model.setObjective(quicksum(data[(i,j)]*values[i] for i in items for j in bins), "maximize")
model.optimize()
In [8]:
for j in bins:
print("\nBin %s" %j+"\t total weight %s" %sum([model.getVal(data[(i,j)])*weights[i] for i in items])+ "\t total price %s" %sum([model.getVal(data[(i,j)])*values[i] for i in items]))
for i in items:
if model.getVal(data[(i,j)]) != 0:
print("Object %s " %i + "\t of weight %s" %weights[i] + "\t and price %s" %values[i])
Bin 1 total weight 90.0 total price 50.0 Object 6 of weight 48 and price 30 Object 13 of weight 42 and price 20 Bin 2 total weight 84.0 total price 65.0 Object 3 of weight 42 and price 25 Object 8 of weight 42 and price 40 Bin 3 total weight 96.0 total price 120.0 Object 4 of weight 36 and price 50 Object 5 of weight 36 and price 35 Object 10 of weight 24 and price 35 Bin 4 total weight 96.0 total price 105.0 Object 2 of weight 30 and price 30 Object 10 of weight 24 and price 35 Object 11 of weight 30 and price 45 Object 14 of weight 36 and price 30 Bin 5 total weight 72.0 total price 55.0 Object 9 of weight 36 and price 30 Object 15 of weight 36 and price 25
Bin Packing¶
- Let us ask a new question.
- Suppose we have an infinite number of knapsacks(bins) of a common size say 100.
- However we want to pack all items with the fewest number of bins.
- An application of this problem is when a logistics agent wants to pack items in boxes of a fixed size and wants to use the fewest number of boxes.
- Note that the value of the items is irrelevant now.
In [25]:
weights = {1:90, 2:90, 3:10, 4:10, 5:10, 6:90, 7:20, 8:80, 9:30, 10:70, 11:40, 12:60, 13:50, 14:50, 15:100}
# weights = {1:48, 2:30, 3:42, 4:36, 5:36, 6:48, 7:42, 8:42, 9:36, 10:24, 11:30, 12:30, 13:42, 14:36, 15:28}
values = {1:10, 2:30, 3:25, 4:50, 5:35, 6:30, 7:15, 8:40, 9:30, 10:35, 11:45, 12:10, 13:20, 14:30, 15:25}
items = range(1, len(weights)+1)
bin_capacities = 100
bins = range(1, len(items)+1)
In [26]:
from pyscipopt import Model, quicksum
model = Model("Bin Packing")
In [27]:
x = {}
for i in items:
for j in bins:
x[(i,j)] = model.addVar(vtype="B", name="Object %s in bin %s" %(i,j))
y = {}
for j in bins:
y[j] = model.addVar(vtype="B", name="Bin %s " %j + " is used or not")
Model¶
x[i,j]is a binary variable which records if object i is placed in bin j.y[j]is a binary variable which records if bin j is used.- Contraints:
- Each item is packed in exactly one bin, for all i $$\sum_{j} x[i,j] = 1$$
- Bin capacity for each j $$\sum_{i} x[i,j]\cdot weights[i] \leq 100 \cdot y[j]$$
- The multiplication by y[j] makes sure that the capcity is 0 if not used.
- Minimize $$\sum_{j} y[j]$$.
In [28]:
for i in items:
model.addCons(quicksum(x[(i,j)] for j in bins) == 1, "item %s" %i + "in only one bin")
for j in bins:
model.addCons(quicksum(x[(i,j)]*weights[i] for i in items) <= bin_capacities*y[j], "bin %s capacity" %j)
In [29]:
model.setObjective(quicksum(y[j] for j in bins), "minimize")
In [30]:
model.optimize()
In [31]:
for j in bins:
if model.getVal(y[j]) !=0:
print("\nBin %s" %j+"\t total weight %s" %sum([model.getVal(x[(i,j)])*weights[i] for i in items]))
for i in items:
if model.getVal(x[(i,j)]) != 0:
print("Object %s " %i + "\t of weight %s" %weights[i])
print("\nNumber of bins: %s" %round(model.getObjVal()))
Bin 1 total weight 100.0 Object 1 of weight 90 Object 3 of weight 10 Bin 2 total weight 100.0 Object 2 of weight 90 Object 4 of weight 10 Bin 3 total weight 100.0 Object 5 of weight 10 Object 6 of weight 90 Bin 4 total weight 100.0 Object 7 of weight 20 Object 8 of weight 80 Bin 5 total weight 100.0 Object 9 of weight 30 Object 10 of weight 70 Bin 6 total weight 100.0 Object 11 of weight 40 Object 12 of weight 60 Bin 7 total weight 100.0 Object 13 of weight 50 Object 14 of weight 50 Bin 8 total weight 100.0 Object 15 of weight 100 Number of bins: 8
In [24]:
model.getSolvingTime()
Out[24]:
0.513675
LPs and Logic¶
Let us change course and discuss how to put logical constraints in LPs.
Suppose we have two binary variables and we require that $x_1$ or $x_2$ is 1. How?
We can do this by $$x_1 + x_2 \geq 1.$$
If we want exclusive or, then?
$$ x_1 + x_2 = 1$$
More logical operations¶
- If we want $x_1$ to imply $x_2$
- If $x_1 =1$, then $x_2 = 1$, how to enforce this with linear constraints?
- Answer: $$x_2 \geq x_1.$$
AND operator¶
- If we want $b = 1$ if $x_1 = 1$ and $x_2 = 1$, and $b = 0$ otherwise. Then?
- $$\begin{aligned}b &\geq x_1 + x_2 -1 \\ b &\leq x_1 \\ b &\leq x_2 \\ b &\in \{0,1 \}\end{aligned}$$
- When both are 1, the first condition forces b to be 1 and
- When either is 0, it forces b to be less than them making b equal to 0.
OR Operator¶
- If we want $b = 1$ if $x_1 = 1$ or $x_2 = 1$, then
- $$\begin{aligned}b &\leq x_1 + x_2\\ b &\geq x_1 \\ b &\geq x_2 \\ b &\in \{0,1\}\end{aligned}$$
- When both are 0, the first statement makes b 0 as well and
- When either is 1, then the greater than equal makes b equal to 1 as well.
XOR Operator¶
- If we want $b = 1$ if $x_1 = 1$ xor $x_2 = 1$. This means that $b$ must be 0 when both are 1, and $b$ is 1 only when exactly on of $x_1, x_2$ is 1.
- $$\begin{aligned}b &\leq x_1 +x_2 \\ b &\geq x_1 - x_2 \\ b &\geq x_2 - x_1 \\ b &\leq 2-x_1-x_2 \end{aligned}$$
- The first condition ensures b is 0, when both are 0.
- The last condition ensures b is 0 when both are 1.
- The remaining two ensure b is 1, when exactly one of them is 1.
More complex contructions¶
- If we want either $2x_1+x_2 \geq 5$ or $2x_3 -x_4 \leq 2$ or both.
- Then how to do?
- We add a binary variable $y$ and
- a large value $M$. How large $M$ is depends on the largest possible value taken by $2x_3 - x_4$ and $2x_1 +x_2$.
- We make $M$ larger than the largest these can take so that they are always less than $M$.
- $$\begin{aligned}2x_1 + x_2 &\geq 5 - M \cdot y \\ 2x_3 -x_4 &\leq 2 + M\cdot(1-y) \end{aligned}$$
contd..¶
- $$\begin{aligned}2x_1 + x_2 &\geq 5 - M \cdot y \\ 2x_3 -x_4 &\leq 2 + M\cdot(1-y) \end{aligned}$$
- If $y = 0$ then this means we only check if $2x_1 + x_2 \geq 5$ because the second is always true.
- If $y=1$, then we only check the second condition as the first is always true.
Indicator variable¶
- Suppose $x$ is an integer variable (say $x \geq 0$).
- We want a binary variable $y$ such that if $ x >0$ then $y = 1$, else $y = 0$.
- Again we choose a postive $M$ such that $x < M$ always holds then
- $$\begin{aligned}x \leq M \cdot y \\ y \leq M \cdot x\end{aligned}$$
- Why?
In [ ]: