Overview
One of the best ways to analyze and optimize the statistics of an inventory policy is through the use of Simulations. (see simulations in statistics) That is, code is written to simulate the demand and lead time, and an algorithm is written that mimics the inventory policy of the firm. Then, several simulations are run to measure the statistics of that policy.
Iterate Periods
The code iterates over a set of periods. In this case, we run 100 periods, and assume that each period is 1 day long.
$range(1,100).forEach(index=>{
});
Simulate Demand
For each period, you need to simulate the demand for the product in that period. (see stochastic demand) In this example, we choose the lognormal distribution to simulate the demand.
let nm = await import('/lib/statistics/distributions/lognormal/v1.0.0/lognormal.mjs');
let demand = Math.round(10*nm.random(1,1));
Fill Orders
Given the demand simulated above, we next determine which of those orders can be filled. If the amount of inventory is greater than the present demand, we use the inventory to fill the demand. We place a record in the fill array indicating that we have filled all the orders, and then we decrement inventory.
If there is more demand than inventory, we fill whatever demand we can we present inventory, and indicate this by placing a record in the fill array. Then we add a record to the backlog array. This indicates an order that hasn't been filled.
if(inventory >= demand){
fill.push({
start:index,
end:index,
amount:demand
});
inventory -= demand;
}
else{
fill.push({
start:index,
end:index,
amount:inventory
});
demand -= inventory;
inventory = 0;
backlog.push({
index:index,
amount:demand
});
}
Re-Orders
At each period, we determine if it is time to re-order product. We do this by implementing a function called reorder that returns true if it is time to reorder. In case it is time to reorder, we generate a random number indicating the lead time for receiving the product that was ordered. The amount of product ordered is place in the reorders map at the time when it will be received.
if(reorder(index)){
let time = leadTime();
reorders[index + time] = 10;
}
Fill Backlog
At the beginning of each period, we check whehter there are any orders in the backlog that havent been filled, and fill them if we can.
if(reorders[index]!== undefined) {
inventory += reorders[index];
for(let log of backlog){
}
}
Results
The results of the simulation is then stored in the fill array. It has records for each order that was filled, when the order was received, and when it was filled.
Full Code
(async ()=>{
let nm = await import('/lib/statistics/distributions/lognormal/v1.0.0/lognormal.mjs');
let inventory = 10;
let backlog = [];
let reorders = {};
let fill = [];
let state = [];
function reorder(args){
if(args.index%10 === 0) return true;
return false;
}
function leadTime(){
let time = Math.round(nm.random(1,1));
if(time === 0) return 1;
return time;
}
$range(1,100).forEach(index=>{
let state1 = {
inventory:inventory
};
if(reorders[index]!== undefined) {
inventory += reorders[index];
for(let log of backlog){
if(inventory >= 0){
if(log.amount >0 && log.amount <= inventory){
fill.push({
start:log.index,
end:index,
amount:log.amount
});
inventory -= log.amount;
log.amount = 0;
}
else if(log.amount > 0 && log.amount> inventory && inventory>0){
fill.push({
start:log.index,
end:index,
amount:inventory
});
log.amount -= inventory;
inventory = 0;
}
}
}
}
let demand = Math.round(10*nm.random(1,1));
state1.demand = demand1;
state.push(state1);
if(inventory >= demand){
if(demand>0) fill.push({
start:index,
end:index,
amount:demand
});
inventory -= demand;
}
else{
if(inventory > 0)fill.push({
start:index,
end:index,
amount:inventory
});
demand -= inventory;
inventory = 0;
if(demand>0) backlog.push({
index:index,
amount:demand
});
}
if(reorder(index)){
let time = leadTime();
if(time === 0) inventory += 10;
else reorders[index + time] = 10;
}
});
})();
Try it!
API
The above code is encapsulated in the inventory simulation library. The results object contains objects reporting various statistics. The reuslts computed above are given in the results.fill array. The results.state array contains a report of the beginning of period inventory and demand for each period.
let sm = await import('/lib/inventory/v1.0.0/simulate.mjs');
function reorder(index){
if(index%10 === 0)return true;
return false;
}
function leadTime(){
let time = Math.round(nm.random(1,1));
if(time === 0) return 1;
return time;
}
function demand(index){
return Math.round(10*nm.random(1,1));
}
let inventory = 10;
let results = sm.simiulate(inventory, demand, reorder, leadTime, 100);
Try it!
Statistics
Once you have generated a simulation as above, you will want to fun statistics on the results. The easiest way to do this is to create a record for each unit of product sold. The records generated in the simulations above represent several products shipped at the same time.
The following code generates a single record for each individual unit of product sold.
let orders = [];
for(let result of results.fill){
$range(1, result.amount).forEach(p=>orders.push({index:result.start, time:result.end - result.start}));
}
This code is further encapsulated in the individualOrders function.
let orders = sm.individualOrders(results.fill);
Once, you have these records, you can easily compute statistics over the results, such as the average ship time.
let average = $list(orders).map(p=>p.time).average();
Note, these are statistics for a single run of the simulation. You will want to run several simulations and aggregate across simulations to get a true picture.