Optimization in R - purchase optimization to minimize cost

This article showcases solving an assumed business problem using optimization, with R as the tool to do so. Optimization problems come is various shapes and sizes and complexities. In this article the focus is on a simplified version of a problem to decide on purchase quantities to minimize cost. You may also like to read other examples of optimization series.

Introduction

Business units often must decide on purchase quantities under various constrains, to minimize cost. Mathematics can help with such decisions, as shown below using a simplified example.

Problem

XYZ corp is local supplier of milk. Having known the market for substantial amount of time, they have already developed a robust forecasting model. For the upcoming days, they expect the following demand.

Day 1 2 3 4
Demand (l) 5000 8000 9000 6000

They need to purchase milk from suppliers at market prices every day. They also have a refrigerated tank that can hold 4000 l of milk. The daily price is expected to be as follows.

Day 1 2 3 4
Price ($/l) 0.75 0.72 0.92 0.90

Their current stock is 2000 l. XYZ wants to purchase milk everyday (at the beginning of the day) to satisfy customer demand at minimal cost. The milk is delivered directly to customer and only the leftover of the day is stored in the tank.

Solution

Objective function

The function of margin can be defined as

$$ 0.75 \times m_1 + 0.72 \times m_2 + 0.92 \times m_3 + 0.90 \times m_4 $$ Where \(m_n\) is the quantity of milk to purchase.

Constrains

If the tank already contains \(t_1\) milk at the beginning of day 1 and XYZ buys \(m_1\) liters on that day, it should cover the demand of that day (5000 in this case for day 1) and the remaining needs to be stored in the tank. This becomes the initial stock of day 2 or \(t_2\).

$$ m_1 + t_1 = 5000 + t_2 $$ $$ => m_1 + t_1 - t_2 = 5000 $$

The second and third lines are rearrangement of the first line to help us construct the matrix. But, we also know that \(t_1\) is 2000. So the equation becomes

$$ 1 \times m_1 + 0 \times m_2 + 0 \times m_3 + 0 \times m_4 + (-1) \times t_2 + 0 \times t_3 + 0 \times t_4 = 3000 $$

Following the same logic, we also have

$$ m_2 + t_2 = 8000 + t_3 $$

$$ m_3 + t_3 = 9000 + t_4 $$

Also,

$$ m_4 + t_4 \ge 6000 $$

Because purchase of day 4 and stock at the beginning of the day should satisfy the demand of the day.

Finally,

$$ t_n \le 4000 $$ and

$$ t_n, m_n \ge 0 $$

Solving the equations

 1library(lpSolve)
 2
 3objective.fn <- c(0.75, 0.72, 0.92, 0.90, 0, 0, 0)
 4const.mat <- matrix(c(1, 0, 0, 0, -1, 0, 0,
 5                      0, 1, 0, 0, 1, -1, 0,
 6                      0, 0, 1, 0, 0, 1, -1,
 7                      0, 0, 0, 1, 0, 0, 1,
 8                      0, 0, 0, 0, 1, 0, 0,
 9                      0, 0, 0, 0, 0, 1, 0,
10                      0, 0, 0, 0, 0, 0, 1,
11                      1, 0, 0, 0, 0, 0, 0,
12                      0, 1, 0, 0, 0, 0, 0,
13                      0, 0, 1, 0, 0, 0, 0,
14                      0, 0, 0, 1, 0, 0, 0,
15                      0, 0, 0, 0, 1, 0, 0,
16                      0, 0, 0, 0, 0, 1, 0,
17                      0, 0, 0, 0, 0, 0, 1
18                      ) , ncol=7 , byrow=TRUE)
19
20const.rhs <- c(3000, 8000, 9000, 6000, 4000, 4000, 4000, 0, 0, 0, 0, 0, 0, 0)
21const.dir <- c("=", "=", "=",">=","<=","<=","<=", ">=",">=",">=",">=",">=",">=",">=")
22lp.solution <- lpSolve::lp("min", objective.fn, const.mat, 
23                  const.dir, const.rhs, compute.sens=TRUE)
24lp.solution$solution
1## [1]  3000 12000  5000  6000     0  4000     0

Hence, the optimum solution to minimize cost under the given condition is to order 3000, 12000, 5000 and 6000 liters of milk for the respective days. This cost will then be 20890 dollars.

Insights

Optimum purchase quantities are as follows

Day Purchase (liters) Demand/Sales (liters) Initial Stock (liters) Final Stock (liters)
1 3000 5000 2000 0
2 12000 8000 0 4000
3 5000 9000 4000 0
4 6000 6000 0 0

The optimum values will not change within these following price ranges.

1data.frame(Price=c("Day 1","Day 2", "Day 3", "Day 4"), From=round(lp.solution$sens.coef.from[1:4],2), To=round(lp.solution$sens.coef.to[1:4],2))
1##   Price     From      To
2## 1 Day 1  7.2e-01 1.0e+30
3## 2 Day 2 -1.0e+30 7.5e-01
4## 3 Day 3  9.0e-01 1.0e+30
5## 4 Day 4  0.0e+00 9.2e-01

Furthermore, the following table shows the possible change in objective function (i.e. cost) on relaxing the constrain i.e. the demands each day. In this case, if demand is relaxed by one unit, the change in cost function is shown.

1data.frame(Duals = lp.solution$duals[1:4])
1##   Duals
2## 1  0.75
3## 2  0.72
4## 3  0.92
5## 4  0.90

Posts in this Series