Ten chapters computed the performance of inventory policies by an exact route where the assumptions held, and an approximate one where they did not. Simulation is a third route, and the only one that reaches every case.
The book has simulated once already: the newsvendor’s expected profit was estimated by sampling 100,000 demands at each of five quantities, and every estimate covered the exact value. That agreement was not a curiosity. It was the evidence that the sampling code computed the newsvendor’s cost and not something near it.
A simulation is trusted only after it reproduces a number the book has already derived. Every model in this chapter first reproduces an earlier result, inside its half-width, and only then is one assumption behind that result removed.
A simulation is a program, and a program can be wrong without saying so. An exact formula is checked once, by derivation; a simulation is checked by running it on a case whose answer is known.
Four boundaries are crossed by nothing in the book except simulation:
Lost sales. Every continuous-review formula assumes an unfilled demand waits. Charging a lost sale as a backorder is the usual remedy, and whether it is close for a given item only a model of the lost-sales system can say.
Orders that overtake one another. A random lead time leaves the \((r, Q)\) formulas intact only if orders arrive in the sequence placed. A simulation delivers each order when its own lead time ends.
Job lots at a batching hub. Chapter 10 set policies for customers taking one, two or four units, and nothing exact covers that case.
How wrong METRIC is for this system. METRIC predicted 0.4055 backorders per storeroom and VARI-METRIC 0.4815, 19% apart. Neither says which is closer here; a simulation does.
Every answer is an estimate. A simulated cost is a sample average with a half-width, and a cost reported without its half-width reports a number that is not there. The half-width shrinks with the square root of the run, so ten times the precision costs a hundred times the run.
Every answer is for one policy. The exact route searched a cost function it could evaluate anywhere. A simulation evaluates one policy per run, with noise, so finding the best is a search over noisy estimates: a harder problem than the one it replaces.
The run is measured in demands, not years. The transformer sees 45 demands a year, so a thousand simulated years is 45,000 demands: a modest sample for a backorder that occurs a few times a year.
None of these costs applies to the formulas, which is why the formulas come first wherever they exist.
An event is an instant at which the state changes. Between events nothing changes, so \(I\), \(\mathit{IO}\), \(B\), \(\mathit{IN}\) and \(\mathit{IP}\) are step functions, computed only at events. An \((r, Q)\) system with backorders has two:
| Event | Changes to the state | Decision taken | Schedules |
|---|---|---|---|
| Demand | \(I \leftarrow I - 1\) if \(I > 0\), else \(B \leftarrow B + 1\); \(\mathit{IP} \leftarrow \mathit{IP} - 1\) | If \(\mathit{IP} \le r\), order \(Q\): \(\mathit{IO} \leftarrow \mathit{IO} + Q\), \(\mathit{IP} \leftarrow \mathit{IP} + Q\) | The next demand; a receipt at \(t + L\) if an order was placed |
| Receipt | \(\mathit{IO} \leftarrow \mathit{IO} - Q\), \(I \leftarrow I + Q\), then fill \(\min(B, I)\) backorders | None | Nothing |
Notice what is missing: placing an order. It never happens at an instant of its own; it happens inside a demand, when the demand lowers the position to \(r\). An order is a decision taken during an event, and what it does is schedule a receipt on the event calendar, the list of future events in time order.
Let \(t_0 = 0 < t_1 < t_2 < \cdots\) represent the event times, and \(A_I(t) = \int_0^t I(u)\,du\) the area under the on-hand curve. \(I\) is constant between events, so the area grows by a rectangle at each:
\[ \begin{aligned} A_I(t_n) &= A_I(t_{n-1}) + I(t_{n-1})\,(t_n - t_{n-1}) &&\text{credit the interval just ended}\\ \bar{I} = \frac{A_I(T)}{T}, \quad \bar{B} &= \frac{A_B(T)}{T}, \quad \overline{\mathit{RR}} = \frac{A_{+}(T)}{T} &&\text{areas divided by time}\\ \overline{\mathit{FR}} &= \frac{N_F(T)}{N_D(T)} &&\text{filled over demanded: a count} \end{aligned} \]
where \(A_B\) is the area under \(B(t)\), \(A_{+}\) the time with \(I > 0\), \(N_D\) the demands and \(N_F\) those filled from the shelf on arrival.
The area is updated before the event changes the state, using the value that held during the interval just ended. Updating it afterward credits the interval with a value the system did not hold.
The transformer at the storeroom of chapter 8: \((r, Q) = (4, 5)\), an eight-week lead time, 6 on hand at the start, nothing on order. Ten demands, in weeks 1, 2, 3, 5, 6, 7, 8, 9, 11 and 13, given rather than sampled; the observation closes at week 13.
| Week | Event | \(I\) | \(B\) | \(\mathit{IP}\) | \(A_I\) | \(A_B\) | \(A_{+}\) | Receipts due |
|---|---|---|---|---|---|---|---|---|
| 0 | start | 6 | 0 | 6 | 0 | 0 | 0 | none |
| 1 | demand | 5 | 0 | 5 | 6 | 0 | 1 | none |
| 2 | demand, order 5 | 4 | 0 | 9 | 11 | 0 | 2 | week 10 |
| 3 | demand | 3 | 0 | 8 | 15 | 0 | 3 | week 10 |
| 5 | demand | 2 | 0 | 7 | 21 | 0 | 5 | week 10 |
| 6 | demand | 1 | 0 | 6 | 23 | 0 | 6 | week 10 |
| 7 | demand | 0 | 0 | 5 | 24 | 0 | 7 | week 10 |
| 8 | backordered, order 5 | 0 | 1 | 9 | 24 | 0 | 7 | weeks 10, 16 |
| 9 | backordered | 0 | 2 | 8 | 24 | 1 | 7 | weeks 10, 16 |
| 10 | receipt of 5 | 3 | 0 | 8 | 24 | 3 | 7 | week 16 |
| 11 | demand | 2 | 0 | 7 | 27 | 3 | 8 | week 16 |
| 13 | demand | 1 | 0 | 6 | 31 | 3 | 10 | week 16 |
With \(T = 13\) weeks:
\[ \begin{aligned} \bar{I} &= 31/13 = 2.3846, \qquad \bar{B} = 3/13 = 0.2308 &&\text{areas over time}\\ \overline{\mathit{RR}} &= 10/13 = 0.7692, \qquad \overline{\mathit{FR}} = 8/10 = 0.8000 &&\text{time with stock; demands filled}\\ A_{+}(13) + 3 &= 10 + 3 = 13 &&\textit{check}\text{: empty from week 7 to 10}\\ A_B(13) &= 3 = 2 + 1 \text{ backorder-weeks} &&\textit{check}\text{: weeks 8 and 9 waited 2 and 1} \end{aligned} \]
The second check is Little’s law: the average wait per demand is \(3/10 = 0.3\) weeks either way. That check is worth performing on every event ledger, because an area credited after the state changed fails it at once.
Averaging the rows instead gives \(27/12 = 2.25\) on hand and \(3/12 = 0.25\) backordered. Both are wrong. And the ready rate and fill rate disagree, 0.7692 against 0.8000, although nothing here is unusual: ten demands is a small sample, and their equality is a statement about the long run.
The model is the event table turned into code. The decision is the shortest piece, and it is the policy:
inventoryPosition is \(\mathit{IP} = I + \mathit{IO} - B\); reorderPoint and orderQuantity are \(r\) and \(Q\). The division is between whole numbers, so batches is \(\lfloor (r - \mathit{IP})/Q \rfloor + 1\), the fewest batches that lift the position above \(r\): always one with single-unit demand, possibly more with demand in lots.
Notice what the stock point does not know: how its orders are filled. It hands each order to a supplier whose only obligation is to deliver eventually. Here the supplier waits a constant two months; later it is a random lead time, then another stock point, and the stock point’s own code changes in neither case.
myOnHand, myBackordered and myOnOrder are \(I\), \(B\) and \(\mathit{IO}\), each a KSL TWResponse, so every change closes a rectangle of the area update. myOwed is the backorder queue, filled first come first served by fillOwed. The decision comes last, after the state has changed.
\(\lambda = 45\) a year, \(L = 1/6\) year exactly, \(k = \$220\), \(h = \$950\), \(b = \$8{,}550\). Every measure is known exactly, so the simulation has nothing to discover: its purpose is to reproduce what is known. Thirty replications of 1,000 years after a 20-year warm-up, each starting with \(r + Q = 15\) on hand.
| Measure | Exact | Simulated, 95% |
|---|---|---|
| \(\bar{I}\), units | 4.6617 | 4.6570 \(\pm\) 0.0107 |
| \(\bar{B}\), units | 0.1617 | 0.1628 \(\pm\) 0.0020 |
| \(\overline{\mathit{RR}}\) | 0.8781 | 0.8779 \(\pm\) 0.0009 |
| \(\overline{\mathit{FR}}\) | 0.8781 | 0.8776 \(\pm\) 0.0010 |
| \(\overline{\mathit{OF}}\), orders a year | 6.4286 | 6.4333 \(\pm\) 0.0105 |
| Total cost | $7,225.15 | $7,231.50 \(\pm\) 15.42 |
The check: every interval covers its exact value, and the ready and fill rates agree to the third decimal, as single-unit Poisson demand requires. With several measures at 95% each, one miss would not be alarming; the intervals are not independent, since all come from the same runs.
A simulation is trusted only after it reproduces a derived number, and then one assumption at a time is removed.
An \((r, Q)\) system is two events, a demand and a receipt; ordering is a decision taken inside a demand, which schedules a receipt.
Averages are areas, credited before the state changes; the ready rate is time-persistent and the fill rate observation-based.
The thirteen-week ledger gives 2.3846 on hand and 0.2308 owed, checked two ways, where row averaging gives two wrong numbers.
The model reproduces chapter 8 at \((8, 7)\) in every measure.
Read before next session: Output Analysis for a Stock Point and Where the (r, Q) Formulas Stop.
Today’s material was When the Formulas Stop, The Inventory Processes as Events and Building the (r, Q) Model.
Next session:
Today’s run used 30 replications of 1,000 years. Next session says why, and when that is not enough.