| name | stochastic-inventory-models |
| description | When the user wants to model inventory systems with uncertain demand, optimize safety stock levels, implement (s,S) or (Q,r) policies, or analyze service levels under uncertainty. Also use when the user mentions "stochastic inventory," "probabilistic inventory," "(Q,r) policy," "(s,S) policy," "base stock policy," "safety stock optimization," "service level constraints," "lead time demand distribution," "fill rate calculation," or "inventory with demand uncertainty." For deterministic models, see economic-order-quantity or lot-sizing-problems. For single-period uncertainty, see newsvendor-problem. |
Stochastic Inventory Models
You are an expert in stochastic inventory theory and probabilistic inventory optimization. Your goal is to help model and optimize inventory systems under demand uncertainty, determining optimal policies that balance inventory costs with service level requirements.
Initial Assessment
Before modeling stochastic inventory, understand:
-
Demand Uncertainty
- Demand distribution? (normal, Poisson, negative binomial, empirical)
- Demand parameters (mean, variance, coefficient of variation)?
- Time period for demand (daily, weekly)?
- Intermittent or smooth demand pattern?
- Historical data available?
-
Lead Time
- Lead time from order to receipt?
- Lead time variability?
- Lead time distribution?
- Correlation between demand and lead time?
-
Inventory Policy Type
- Continuous vs. periodic review?
- (Q,r) continuous review policy?
- (R,S) periodic review policy?
- (s,S) policy with bandwidth?
- Base stock policy?
-
Service Level Requirements
- Target service level? (Type I or Type II)
- Type I: Probability of not stocking out during lead time
- Type II: Fill rate (fraction of demand satisfied)
- Critical vs. non-critical items?
-
Cost Structure
- Fixed ordering cost?
- Holding cost per unit per period?
- Backorder cost vs. lost sales?
- Emergency replenishment options?
Stochastic Inventory Fundamentals
Key Concepts
Demand During Lead Time (DDLT):
- Random variable representing total demand during replenishment lead time
- Critical for determining reorder point and safety stock
Safety Stock (SS):
- Buffer inventory to protect against demand uncertainty
- SS = k × σ_DDLT, where k is safety factor
Service Levels:
- Type I (Cycle Service Level, CSL): P(no stockout during lead time)
- Type II (Fill Rate, FR): Fraction of demand met from stock
Inventory Position:
- On-hand + on-order - backorders
- Decision based on position, not just on-hand
Python Implementation: Stochastic Models
(Q,r) Continuous Review Policy
import numpy as np
import pandas as pd
from scipy import stats
from scipy.optimize import minimize_scalar, fsolve
from typing import Dict, Tuple
import matplotlib.pyplot as plt
class ContinuousReviewQrPolicy:
"""
(Q,r) Continuous Review Inventory Policy
- Monitor inventory continuously
- When inventory position ≤ r, order Q units
- Optimize Q and r to minimize costs while meeting service level
"""
def __init__(self, demand_mean: float, demand_std: float,
lead_time: float, holding_cost: float,
ordering_cost: float, backorder_cost: float = None,
service_level: float = 0.95):
"""
Parameters:
-----------
demand_mean : float
Mean demand per period
demand_std : float
Standard deviation of demand per period
lead_time : float
Lead time in periods
holding_cost : float
Holding cost per unit per period
ordering_cost : float
Fixed ordering cost
backorder_cost : float, optional
Backorder cost per unit per period (if None, uses lost sales)
service_level : float
Target Type I service level (cycle service level)
"""
self.mu = demand_mean
self.sigma = demand_std
self.L = lead_time
self.h = holding_cost
self.K = ordering_cost
.b = backorder_cost
.alpha = service_level
.mu_L = demand_mean * lead_time
.sigma_L = demand_std * np.sqrt(lead_time)
() -> :
np.sqrt( * .mu * .K / .h)
() -> :
service_level :
service_level = .alpha
z = stats.norm.ppf(service_level)
safety_stock = z * .sigma_L
r = .mu_L + safety_stock
():
stats.norm.pdf(k) - k * ( - stats.norm.cdf(k))
k = (r - .mu_L) / .sigma_L
expected_shortage = .sigma_L * G(k)
fill_rate = - expected_shortage / Q
{
: r,
: safety_stock,
: z,
: expected_shortage,
: fill_rate,
: service_level
}
() -> :
Q = .calculate_eoq()
_ ():
r_result = .calculate_reorder_point(Q, .alpha)
r = r_result[]
Q_new = np.sqrt( * .mu * .K / .h)
(Q_new - Q) < :
Q = Q_new
r_result = .calculate_reorder_point(Q, .alpha)
r = r_result[]
ss = r_result[]
ordering_cost_annual = (.mu / Q) * .K
holding_cost_annual = (Q / + ss) * .h
.b :
expected_shortage = r_result[]
backorder_cost_annual = (.mu / Q) * expected_shortage * .b
total_cost = ordering_cost_annual + holding_cost_annual + backorder_cost_annual
:
backorder_cost_annual =
total_cost = ordering_cost_annual + holding_cost_annual
{
: ,
: Q,
: r,
: ss,
: r_result[],
: Q / + ss,
: ordering_cost_annual,
: holding_cost_annual,
: backorder_cost_annual,
: total_cost,
: r_result[],
: r_result[]
}
() -> pd.DataFrame:
seed :
np.random.seed(seed)
inventory_position = Q
inventory_on_hand = Q
orders_outstanding = []
results = []
t (num_periods):
arrivals = [order order orders_outstanding
order[] == t]
arrival arrivals:
inventory_on_hand += arrival[]
orders_outstanding.remove(arrival)
demand = (, np.random.normal(.mu, .sigma))
sales = (demand, inventory_on_hand)
stockout = demand - sales
inventory_on_hand -= sales
inventory_position = inventory_on_hand + (
o[] o orders_outstanding)
order_placed =
inventory_position <= r:
order_placed = Q
orders_outstanding.append({
: t,
: t + (.L),
: Q
})
inventory_position += Q
results.append({
: t,
: demand,
: sales,
: stockout,
: inventory_on_hand,
: inventory_position,
: order_placed,
: (orders_outstanding)
})
df = pd.DataFrame(results)
fill_rate = df[].() / df[].()
avg_inventory = df[].mean()
stockout_periods = (df[] > ).()
cycle_service_level = - stockout_periods / num_periods
summary = {
: fill_rate,
: cycle_service_level,
: avg_inventory,
: df[].(),
: (df[] > ).()
}
df, summary
():
fig, axes = plt.subplots(, , figsize=(, ))
periods = simulation_df[]
axes[].plot(periods, simulation_df[],
label=, linewidth=, color=)
axes[].plot(periods, simulation_df[],
label=, linewidth=, color=, alpha=)
axes[].axhline(y=r, color=, linestyle=, linewidth=,
label=)
axes[].fill_between(periods, , simulation_df[],
alpha=, color=)
order_periods = periods[simulation_df[] > ]
axes[].scatter(order_periods,
simulation_df.loc[simulation_df[] > ,
],
color=, s=, marker=, zorder=,
label=)
axes[].set_ylabel()
axes[].set_title(, fontweight=)
axes[].legend(loc=)
axes[].grid(, alpha=)
axes[].plot(periods, simulation_df[], label=,
linewidth=, color=, alpha=)
axes[].plot(periods, simulation_df[], label=,
linewidth=, color=)
axes[].fill_between(periods, simulation_df[],
simulation_df[],
where=(simulation_df[] > ),
alpha=, color=, label=)
axes[].set_ylabel()
axes[].set_title(, fontweight=)
axes[].legend()
axes[].grid(, alpha=)
axes[].plot(periods, simulation_df[].cumsum(),
linewidth=, color=)
axes[].set_xlabel()
axes[].set_ylabel()
axes[].set_title(, fontweight=)
axes[].grid(, alpha=)
plt.tight_layout()
plt
():
( + * )
()
( * )
model = ContinuousReviewQrPolicy(
demand_mean=,
demand_std=,
lead_time=,
holding_cost=,
ordering_cost=,
backorder_cost=,
service_level=
)
()
()
()
()
()
()
()
()
optimal = model.optimize_Qr_cost()
()
()
( * )
()
()
()
()
()
()
()
()
()
()
()
()
()
()
()
simulation, summary = model.simulate(
num_periods=,
Q=optimal[],
r=optimal[],
seed=
)
()
()
()
()
()
()
model.plot_simulation(simulation, optimal[],
optimal[])
plt.savefig(, dpi=, bbox_inches=)
()
model, optimal, simulation
__name__ == :
example_continuous_review()
Periodic Review (R,S) Policy
class PeriodicReviewRSPolicy:
"""
(R,S) Periodic Review Policy
- Review inventory every R periods
- Order up to level S (order-up-to level)
- Must cover demand during R + L periods
"""
def __init__(self, demand_mean: float, demand_std: float,
lead_time: float, review_period: float,
holding_cost: float, ordering_cost: float,
service_level: float = 0.95):
"""
Parameters:
-----------
demand_mean : float
Mean demand per period
demand_std : float
Std dev of demand per period
lead_time : float
Lead time in periods
review_period : float
Time between reviews (R)
holding_cost : float
Holding cost per unit per period
ordering_cost : float
Fixed ordering cost per order
service_level : float
Target Type I service level
"""
self.mu = demand_mean
self.sigma = demand_std
self.L = lead_time
self.R = review_period
self.h = holding_cost
self.K = ordering_cost
self.alpha = service_level
self.T = review_period + lead_time
self.mu_T = demand_mean * self.T
self.sigma_T = demand_std * np.sqrt(self.T)
def calculate_order_up_to_level(self) -> Dict:
z = stats.norm.ppf(.alpha)
safety_stock = z * .sigma_T
S = .mu_T + safety_stock
avg_order = .R * .mu
avg_inventory = avg_order / + safety_stock
orders_per_year = / .R
ordering_cost_annual = orders_per_year * .K
holding_cost_annual = avg_inventory * .h * .R
total_cost = ordering_cost_annual + holding_cost_annual
{
: ,
: .R,
: S,
: safety_stock,
: z,
: avg_inventory,
: ordering_cost_annual,
: holding_cost_annual,
: total_cost,
: .alpha
}
():
( + * )
()
( * )
model = PeriodicReviewRSPolicy(
demand_mean=,
demand_std=,
lead_time=,
review_period=,
holding_cost=,
ordering_cost=,
service_level=
)
()
()
()
()
()
()
result = model.calculate_order_up_to_level()
()
()
( * )
()
()
()
()
()
()
()
model, result
__name__ == :
example_periodic_review()
Tools & Libraries
Python Libraries
scipy.stats: Probability distributions
numpy, pandas: Numerical and data operations
- Custom implementations for policy optimization
Commercial Software
- Blue Yonder: Advanced stochastic inventory optimization
- ToolsGroup: Probabilistic demand forecasting and inventory
- Logility: Inventory optimization with uncertainty
- SAP IBP: Stochastic planning capabilities
- o9 Solutions: Probabilistic inventory planning
Common Challenges & Solutions
Challenge: Intermittent Demand
Problem: Many zeros, high variability
Solutions:
- Use specialized distributions (Croston's method, Poisson, negative binomial)
- Higher safety stocks
- Consider make-to-order
Challenge: Lead Time Variability
Problem: Supplier lead times uncertain
Solutions:
- Model lead time as random variable
- Combined uncertainty formula: σ²_DDLT = L·σ²_D + μ²_D·σ²_L
- Safety lead time approach
Challenge: Service Level Selection
Problem: Difficult to specify target
Solutions:
- Use cost-based optimization (balance holding vs. stockout)
- ABC classification with differentiated service
- Empirically validate with business
Challenge: Demand Distribution Selection
Problem: Don't know appropriate distribution
Solutions:
- Use empirical bootstrap methods
- Fit and test multiple distributions (normal, lognormal, gamma)
- Normal often works well for aggregate demand
Related Skills
- inventory-optimization: General inventory management
- economic-order-quantity: Deterministic models
- newsvendor-problem: Single-period stochastic
- multi-echelon-inventory: Network-wide stochastic models
- demand-forecasting: Demand distribution estimation
- safety-stock: Safety stock calculation methods