1. Analytic Results for the \((Q,r)\) Policy

In this section, we extend the analysis of a single-item inventory system under a basestock policy to a \((Q,r)\) inventory system.

There is a convenient way to relate the \((Q,r)\) system to a number of basestock systems. To see this, imagine two animals, a squirrel that observes the \((Q,r)\) system at the end of each demand period, and a bear that only wakes up when the inventory position right at the start of the interval equals some fixed \(i\), where \(i\) must lie in the set \(\{r+1, \ldots r+Q\}\); if not, the bear hibernates. Clearly, from the bear’s point of view, the system behaves as a basestock system with order-up-to level \(i\), because whenever the bear wakes, the inventory position is \(i\), and the position just before the bear falls asleep is \(i-D\). More generally, we can associate a different bear with each position in the set \(\{r+1, \ldots r+Q\}\), so exactly one bear is awake at any time. To combine the statistics observed by the bears and the statistics observed by the squirrel, we need, for each \(i\), the long-run fraction of periods in which the inventory position \(\IP\) equals \(i\). For instance, if the squirrel knows1 We assume that the limit over all sample paths \(\{\IP_{k}\}\) exists almost surely, so that we can invoke the strong law of large numbers.

\begin{equation*} p(i) = \lim_{T\to \infty} \frac{1}{T} \sum_{k=1}^{T}\1{\IP_k = i}, \end{equation*}

for all \(i\in \{r+1, \ldots, r+Q\}\), then it can retrieve the ready rate \(\sum_{i=r+1}^{Q+r} p(i) \alpha(i)\) from the ready rate \(\alpha(i)\) observed by the bear with order-up-to level \(i\).2 We label the ready rate of the basestock model by the order-up-to level.

Theorem 2. 

Under the \((Q,r)\) policy, \(p(i) = \P{\IP = i} = 1/Q\) for \(i=r+1, \ldots, r+Q\), in the long-run time limit. Consequently, \(\IL\) is distributed as \(\IP-X\).

Proof

For simplicity take \(Q=3, r=0\), so that the inventory position \(\IP\) cycles between the levels \(1, 2\) and \(3\).3 The proof is the same for general \(Q\) and \(r\), but needs a bit more notation. We apply level-crossing arguments, as follows. Write \(f_{i} = \P{D\in \{i, i+3, i+6, \ldots\}}\) for the probability that the period demand equals a multiple of \(3\) plus \(i\), \(i=0, 1, 2\). Suppose that, at the start of period \(k\), the position \(\IP_{k} = 3\). Since we order in multiples of \(Q=3\), the probability \(\P{\IP_{k+1} =3|\IP_{k}=3} = f_{0}\), i.e., equal to the probability that \(D_{k}\) is a multiple of \(3\). Likewise, \(\P{\IP_{k+1} = 2|\IP_k=3} = f_{1}\), and \(\P{\IP_{k+1} = 1|\IP_k=3} = f_{2}\). Next, since we again order in multiples of \(Q\), \(\P{\IP_{k+1} = 1|\IP_k=2} = f_{1}\), and similarly for the other possibilities. Therefore, we can use level crossing to conclude that in the long run, \((f_{1}+f_2)p(3) = f_1p(2) + f_2 p(1)\). However, because of the \((Q,r)\) ordering rule, we can cyclically change \(p(1), p(2)\) and \(p(3)\) in this equality. By symmetry, the only possible solution is \(p(i) = 1/3\) for all \(i\).

With the above theorem the ready rate for the \((Q,r)\) model can be computed with the formula

\begin{align} \alpha = \frac1Q \sum_{i=r+1}^{r+Q} \alpha(i) = \frac{1}{Q} \sum_{i=r+1}^{r+Q} \P{X+D\leq i}, \end{align}

where \(\alpha(i)\) is the ready rate of the basestock model with reorder level \(i\).

For the average inventory level, we write \(I(i)\) for the inventory level of the basestock model with order-up-to level \(i\), and get

\begin{align*} \E{I} &= \frac1Q\sum_{i=r+1}^{r+Q} \E{I(i)} = \frac1Q\sum_{i=r+1}^{r+Q} (i - \E X) = \frac{Q+1}2 + r - \E X. \end{align*}

The expected number of back-orders \(\E{I^-}\) and expected on-hand inventory \(\E{I^+}\) can be found by equivalent summations:

\begin{align*} \E{I^{+}} &= \frac1Q\sum_{i=r+1}^{r+Q} \E{I^{+}(i)}, & \E{I^{-}} &= \frac1Q\sum_{i=r+1}^{r+Q} \E{I^{-}(i)}, \end{align*}

using \cref{eq:27} and \cref{eq:ia22} of the basestock model.

Finally, with \(h\) and \(b\) the holding and backorder cost per item per period, the total average cost becomes

\begin{equation*} c(Q,r) = K\frac{\E D}{Q} + h \E{I^{+}} + b \E{I^-}. \end{equation*}

With regard to the first term, note that \(\E D\) is the average demand per period, so \(\E D / Q\) is the order frequency.

It remains to find optimal values for \(r\) and \(Q\). Fast algorithms exist to compute optimal \(r\) and \(Q\), but we omit this. A simple workaround is to perform a complete grid search over a reasonable set of pairs of \(r\) and \(Q\). Another simple heuristic is to use the EOQ4 See \cref{ex:qr2}, and set \(d=\E D\). formula to set \(Q = \sqrt{2\E{D}K/h}\) and then search for \(r\) such that the total average cost is minimal.\sidenote{For that value of $Q$!}

With similar methods we can search for an \(r\) such that the \(\alpha\) or \(\beta\) service level criteria are met.

To code the above formulas we use the RV class again so that our implementation can stay on a high level. Note in particular that the code relies on \cref{th:3}.

from functools import cache
import numpy as np

import random_variable as rv
from functions import Plus, Min, grid_search
from lighthouse_case import D, K, N, l, h, b


class Qr:
    def __init__(self, D, l, h, b):
        self.D = D
        self.l = l
        self.h = h
        self.b = b
        self.X = sum(self.D for _ in range(l)) if l > 0 else rv.RV({0: 1})

    @cache
    def IP(self, Q, r):
        return rv.RV({i: 1 / Q for i in range(r + 1, r + Q + 1)})

    @cache
    def IL(self, Q, r):
        return self.IP(Q, r) - self.X

    @cache
    def Imin(self, Q, r):
        return rv.apply_function(lambda x: Min(x), self.IL(Q, r))

    @cache
    def Iplus(self, Q, r):
        return rv.apply_function(lambda x: Plus(x), self.IL(Q, r))

    def order_freq(self, Q,r):
        return self.D.mean()/Q

    def cost(self, Q, r):
        C = self.K*self.order_freq(Q)
        C += self.b * self.Imin(Q, r).mean() + self.h * self.Iplus(Q, r).mean()
        return C

    def alpha(self, Q, r):
        return (self.IL(Q, r) - self.D).sf(-1)

    def beta(self, Q, r):
        m = rv.compose_function(
            lambda x, y: min(x, y), self.D, self.Iplus(Q, r)
        )
        return m.mean() / self.D.mean()
2.275

Here is an example of how to use this class.

qr = Qr(D, l, h, b)
Q, r = 3, 10
print(qr.IL(Q, r).mean())
print((Q + 1) / 2 + r - qr.X.mean())
7.450000000000001
7.45
TF 1:

Claim: The (\(Q, r\)) inventory policy can be analyzed by comparing it with multiple basestock systems.

Solution
Did you actually try? Maybe see the ‘hints’ above!:
Solution, for real

True.

TF 2:

Claim: In a (\(Q, r\)) policy system, the inventory position \(P\) at the beginning of an interval must always be equal to the reorder level \(r\).

Solution
Did you actually try? Maybe see the ‘hints’ above!:
Solution, for real

False.

TF 3:

Consider the following Python code.

class Qr:
    def __init__(self, D, l, h, b):
        self.D = D
        self.l = l
        self.h = h
        self.b = b
        self.X = sum(self.D for _ in range(self.l))

qr_instance = Qr(D=2, l=3, h=5, b=1)

Claim: The value of \texttt{qr\_instance.X} will be 6.

Solution
Did you actually try? Maybe see the ‘hints’ above!:
Solution, for real

True. The sum operation in the constructor computes \(2 + 2 + 2 = 6\) based on \(D=2\) and \(l=3\).

TF 4:

The fill rate for the \((Q,r)\) inventory system is defined as

\begin{equation*} \beta = \E{\min\{D, I^{+}\}}/\E D. \end{equation*}

Claim: The following Python code implements this correctly:

def beta(self, Q, r):
    m = rv.compose_function(
        lambda x, y: min(x, y), self.D, self.Iplus(Q, r)
    )
    return m.mean() / self.D.mean()
Solution
Did you actually try? Maybe see the ‘hints’ above!:
Solution, for real

True.

TF 5:

Claim: The order frequency of the (\(Q, r\)) is equal to \(Q/\E D\).

Solution
Did you actually try? Maybe see the ‘hints’ above!:
Solution, for real

False.

TF 6:

Claim: this Python code computes the average cost under a \((Q,r)\) policy.

C = K * Q / D.mean()
C += b * Imin(Q, r).mean() + h * Iplus(Q, r).mean()

Claim: The value of \texttt{qr\_instance.X} will be 6.

Solution
Did you actually try? Maybe see the ‘hints’ above!:
Solution, for real

True. The sum operation in the constructor computes \(2 + 2 + 2 = 6\) based on \(D=2\) and \(l=3\).

Exercise 7:

Perhaps the most important formula in practical inventory management is the Economic Order Quantity (EOQ) that determines the optimal order quantity in case demand is constant, i.e., \(D\equiv d\), the lead time \(\leadtime=0\) and backlogging is not allowed. Use the \((Q,r)\) model to show that it is optimal to take \(Q^{2} = 2 d K/h\).

Hint

No backlogging and \(l=0\) \(\implies r=-1\). Use \cref{ex:bs1}.

Solution
Did you actually try? Maybe see the ‘hints’ above!:
Solution, for real

When \(\leadtime = 0\), we have \(\E X = 0\) so that for the basestock model, \(\E{\IL^{+}(i)} = i\). Hence, for the \((Q,r)\) model, \(\E{\IL^{+}} = Q(Q-1)/2Q = (Q-1)/2\). Since demand arrives at a constant rate \(d\), the average cost becomes \(Kd/Q + h Q/2 - h/2\). Differentiating with respect to \(Q\) and setting the derivative to zero yields the result.

CC BY-SA 4.0 This work is licensed by the University of Groningen under a Creative Commons Attribution-ShareAlike 4.0 International License.