supply_uncertainty
Module¶
Overview¶
Stockpyl contains code to solve the following types of single-echelon inventory optimization problems
in the supply_uncertainty
module:
- Economic order quantity (EOQ)-based models
with disruptions
with yield uncertainty
- Newsvendor-based models
with disruptions
with yield uncertainty
Note
The notation and references (equations, sections, examples, etc.) used below refer to Snyder and Shen, Fundamentals of Supply Chain Theory (FoSCT), 2nd edition (2019).
See Also
For an overview of supply uncertainty in Stockpyl, see the tutorial page for supply uncertainty.
API Reference¶
- eoq_with_disruptions(fixed_cost, holding_cost, stockout_cost, demand_rate, disruption_rate, recovery_rate, approximate=False)[source]¶
Solve the economic order quantity problem with disruptions (EOQD) as presented by Parlar and Berkin (1991) and Berk and Arreola-Risa (1994). Problem is solved numerically using golden section search.
Set
approximate
toTrue
to use the approximation by Snyder (2014).- Parameters
fixed_cost (float) – Fixed cost per order. [\(K\)]
holding_cost (float) – Holding cost per item per unit time. [\(h\)]
stockout_cost (float) – Stockout cost per item. [\(p\)]
demand_rate (float) – Demand (items) per unit time. [\(d\)]
disruption_rate (float) – Parameter of exponential distribution governing length of “up” intervals. [\(\lambda\)]
recovery_rate (float) – Parameter of exponential distribution governing length of “down” intervals. [\(\mu\)]
approximate (bool, optional) – Use approximate cost function?
- Returns
order_quantity (float) – Optimal order quantity (items). [\(Q^*\)]
cost (float) – Optimal cost per unit time. [\(g^*\)]
- Raises
ValueError – If
fixed_cost
ordemand_rate
< 0, or ifholding_cost
orstockout_cost
<= 0.ValueError – If
disruption_rate
<= 0 orrecovery_rate
<= 0.
Equations Used (equation (9.5)):
\[g(Q) = \frac{K + hQ^2/2d + pd\psi/\mu}{Q/d + \psi/\mu},\]where
\[\psi = \frac{\lambda}{\lambda+\mu} \left(1 - e^{-\frac{(\lambda+\mu)Q}{d}}\right)\]if
approximate
isFalse
. Ifapproximate
isTrue
, then\[Q^* = \frac{\sqrt{(\psi d h)^2 + 2h\mu(Kd\mu + d^2p\psi)} - \psi dh}{h\mu},\]where
\[\psi = \frac{\lambda}{\lambda+\mu},\]and
\[g(Q^*) = hQ^*.\](See Snyder (2014).)
References
Parlar and D. Berkin. Future supply uncertainty in EOQ models. Naval Research Logistics, 38 (1):107–121, 1991.
Berk and A. Arreola-Risa. Note on “Future supply uncertainty in EOQ models”. Naval Research Logistics, 41(1):129–132, 1994.
Snyder. A tight approximation for a continuousreview inventory model with supplier disruptions. International Journal of Production Economics, 155:91–108, 2014.
Example (Example 9.1-9.2):
>>> eoq_with_disruptions(8, 0.225, 5, 1300, 1.5, 14) (772.8110739983106, 173.95000257319708) >>> eoq_with_disruptions(8, 0.225, 5, 1300, 1.5, 14, approximate=True) (773.1432417118889, 173.957229385175)
- eoq_with_disruptions_cost(order_quantity, fixed_cost, holding_cost, stockout_cost, demand_rate, disruption_rate, recovery_rate, approximate=False)[source]¶
Calculate the cost of using
order_quantity
as the solution to the economic order quantity problem with disruptions (EOQD) as presented by Parlar and Berkin (1991) and Berk and Arreola-Risa (1994).Set
approximate
toTrue
to use the approximation by Snyder (2014).- Parameters
order_quantity (float) – Order quantity for cost evaluation. [\(Q\)]
fixed_cost (float) – Fixed cost per order. [\(K\)]
holding_cost (float) – Holding cost per item per unit time. [\(h\)]
stockout_cost (float) – Stockout cost per item. [\(p\)]
demand_rate (float) – Demand (items) per unit time. [\(d\)]
disruption_rate (float) – Parameter of exponential distribution governing length of “up” intervals. [\(\lambda\)]
recovery_rate (float) – Parameter of exponential distribution governing length of “down” intervals. [\(\mu\)]
approximate (bool, optional) – Use approximate cost function?
- Returns
cost – Optimal cost per unit time. [\(g^*\)]
- Return type
float
- Raises
ValueError – If
fixed_cost
ordemand_rate
< 0, or ifholding_cost
,stockout_cost
, ororder_quantity
<= 0.ValueError – If
disruption_rate
<= 0 orrecovery_rate
<= 0.
Equations Used (equation (9.5)):
\[g(Q) = \frac{K + hQ^2/2d + pd\psi/\mu}{Q/d + \psi/\mu},\]where
\[\psi = \frac{\lambda}{\lambda+\mu} \left(1 - e^{-\frac{(\lambda+\mu)Q}{d}}\right)\]if
approximate
isFalse
and\[\psi = \frac{\lambda}{\lambda+\mu}\]if
approximate
isTrue
.References
Parlar and D. Berkin. Future supply uncertainty in EOQ models. Naval Research Logistics, 38 (1):107–121, 1991.
Berk and A. Arreola-Risa. Note on “Future supply uncertainty in EOQ models”. Naval Research Logistics, 41(1):129–132, 1994.
Snyder. A tight approximation for a continuousreview inventory model with supplier disruptions. International Journal of Production Economics, 155:91–108, 2014.
Example (Example 9.1):
>>> eoq_with_disruptions_cost(700, 8, 0.225, 5, 1300, 1.5, 14) 174.78711738886236 >>> eoq_with_disruptions_cost(700, 8, 0.225, 5, 1300, 1.5, 14, approximate=True) 174.80614234644133
- newsvendor_with_disruptions(holding_cost, stockout_cost, demand, disruption_prob, recovery_prob, base_stock_level=None)[source]¶
Solve the newsvendor problem with disruptions and deterministic demand, or (if
base_stock_level
is supplied) calculate expected cost of given solution.- Parameters
holding_cost (float) – Holding cost per item per period. [\(h\)]
stockout_cost (float) – Stockout cost per item per period. [\(p\)]
demand (float) – Demand per period. [\(d\)]
disruption_prob (float) – Probability of disruption in period \(t+1\) given that there is no disruption in period \(t\). [\(\alpha\)]
recovery_prob (float) – Probability of no disruption in period \(t+1\) given that there is a disruption in period \(t\). [\(\beta\)]
base_stock_level (float, optional) – Base-stock level for cost evaluation. If supplied, no optimization will be performed. [\(S\)]
- Returns
base_stock_level (float) – Optimal base-stock level (or base-stock level supplied). [\(S^*\)]
cost (float) – Expected cost per period attained by
base_stock_level
. [\(g^*\)]
- Raises
ValueError – If
holding_cost
,stockout_cost
, ordemand
<= 0.ValueError – If
disruption_prob
orrecovery_prob
is not in (0,1).
Equations Used ((9.18), (9.14), and Lemma 9.2):
\[S^* = d + dF^{-1}\left(\frac{p}{p+h}\right)\]\[g(S) = \sum_{n=0}^\infty \pi_n \left[h\left[S-(n+1)d\right]^+ + p\left[(n+1)d-S\right]^+\right],\]where
\[\pi_0 = \frac{\beta}{\alpha+\beta}\]\[\pi_n = \frac{\alpha\beta}{\alpha+\beta}(1-\beta)^{n-1}, \quad n \ge 1\]\[F(n) = 1 - \frac{\alpha}{\alpha+\beta}(1-\beta)^n.\]Example (Example 9.3):
>>> newsvendor_with_disruptions(0.25, 3, 2000, 0.04, 0.25) (8000, 2737.0689302470355) >>> newsvendor_with_disruptions(0.25, 3, 2000, 0.04, 0.25, base_stock_level=1200) (1200, 5710.344790717086)
- eoq_with_additive_yield_uncertainty(fixed_cost, holding_cost, demand_rate, yield_mean, yield_sd, order_quantity=None)[source]¶
Solve the EOQ problem with additive yield uncertainty and deterministic demand, or (if
order_quantity
is supplied) calculate expected cost of given solution.Note that the optimal solution depends only on the mean and standard deviation of the yield distribution, not the distribution itself.
- Parameters
fixed_cost (float) – Fixed cost per order. [\(K\)]
holding_cost (float) – Holding cost per item per unit time. [\(h\)]
demand_rate (float) – Demand (items) per unit time. [\(d\)]
yield_mean (float) – Mean of yield distribution. [\(E[Y]\)]
yield_sd (float) – Standard deviation of yield distribution. [\(\text{SD}[Y]\)]
order_quantity (float, optional) – Order quantity for cost evaluation. If supplied, no optimization will be performed. [\(Q\)]
- Returns
order_quantity (float) – Optimal order quantity (or order quantity supplied). [\(Q^*\)]
cost (float) – Expected cost per unit time attained by
order_quantity
. [\(g^*\)]
- Raises
ValueError – If
fixed_cost
ordemand_rate
, or ifholding_cost
<= 0.ValueError – If
yield_sd
< 0.
Equations Used ((9.23) and (9.22)):
\[Q^* = \sqrt{\frac{2Kd}{h} + \text{Var}[Y]} - E[Y]\]\[g(Q) = \frac{2Kd + h\text{Var}[Y]}{2(Q+E[Y])} + \frac{h(Q+E[Y])}{2}\]Example (Example 9.4):
>>> eoq_with_additive_yield_uncertainty(18500, 0.06, 75000, -15000, 9000) (230246.37046881882, 12914.78222812913) >>> eoq_with_additive_yield_uncertainty(18500, 0.06, 75000, -15000, 9000, order_quantity=300000) (300000, 13426.947368421053)
- eoq_with_multiplicative_yield_uncertainty(fixed_cost, holding_cost, demand_rate, yield_mean, yield_sd, order_quantity=None)[source]¶
Solve the EOQ problem with multiplicative yield uncertainty and deterministic demand, or (if
order_quantity
is supplied) calculate expected cost of given solution.Note that the optimal solution depends only on the mean and standard deviation of the yield distribution, not the distribution itself.
- Parameters
fixed_cost (float) – Fixed cost per order. [\(K\)]
holding_cost (float) – Holding cost per item per unit time. [\(h\)]
demand_rate (float) – Demand (items) per unit time. [\(d\)]
yield_mean (float) – Mean of yield distribution. [\(E[Z]\)]
yield_sd (float) – Standard deviation of yield distribution. [\(\text{SD}[Z]\)]
order_quantity (float, optional) – Order quantity for cost evaluation. If supplied, no optimization will be performed. [\(Q\)]
- Returns
order_quantity (float) – Optimal order quantity (or order quantity supplied). [\(Q^*\)]
cost (float) – Expected cost per unit time attained by
order_quantity
. [\(g^*\)]
- Raises
ValueError – If
fixed_cost
ordemand_rate
, or ifholding_cost
<= 0.ValueError – If
yield_sd
< 0.
Equations Used ((9.25) and (9.24)):
\[Q^* = \sqrt{\frac{2Kd}{h(\text{Var}[Z] + E[Z]^2)}}\]\[g(Q) = \frac{Kd}{QE[Z]} + \frac{hQ(\text{Var}[Z] + E[Z]^2)}{2E[Z]}\]Example (Example 9.5):
>>> eoq_with_multiplicative_yield_uncertainty(18500, 0.06, 75000, 0.8333, math.sqrt(0.0198)) (254477.46130342316, 13086.16169098594) >>> eoq_with_multiplicative_yield_uncertainty(18500, 0.06, 75000, 0.8333, math.sqrt(0.0198), order_quantity=300000) (300000, 13263.770562822512)
- newsvendor_with_additive_yield_uncertainty(holding_cost, stockout_cost, demand, yield_mean=None, yield_sd=None, yield_distribution=None, loss_function=None, base_stock_level=None)[source]¶
Solve the newsvendor problem with additive yield uncertainty and deterministic demand, or (if
base_stock_level
is supplied) calculate expected cost of given solution.Either provide
yield_mean
andyield_sd
to use a normal yield distribution, or provideyield_distribution
as a frozenrv_continuous
orrv_discrete
object. Ifyield_distribution
is provided, then loss functions are calculated usingloss_functions.continuous_loss()
orloss_functions.discrete_loss()
, unlessloss_function
is provided. (Loss functions are used in expected-cost calculation.)- Parameters
holding_cost (float) – Holding cost per item per period. [\(h\)]
stockout_cost (float) – Stockout cost per item per period. [\(p\)]
demand (float) – Demand per period. [\(d\)]
yield_mean (float, optional) – Mean of yield distribution. [\(E[Y]\)]
yield_sd (float, optional) – Standard deviation of yield distribution. [\(\text{SD}[Y]\)]
yield_distribution (rv_continuous or rv_discrete, optional) – Yield distribution. Required if
yield_mean
oryield_sd
isNone
.loss_function (function, optional) – Function that takes a single argument and returns a tuple consisting of the loss function and complementary loss function value of that argument. Ignored if
yield_distribution
isNone
.base_stock_level (float, optional) – Base-stock level for cost evaluation. If supplied, no optimization will be performed. [\(S\)]
- Returns
base_stock_level (float) – Optimal base-stock level (or base-stock level supplied). [\(S^*\)]
cost (float) – Expected cost per unit time attained by
base_stock_level
. [\(g^*\)]
- Raises
ValueError – If
holding_cost
,stockout_cost
, ordemand
<= 0.ValueError – If
yield_sd
<= 0.ValueError – If (
yield_mean
isNone
oryield_sd
isNone
) andyield_distribution
isNone
.
Equations Used ((9.28) and (9.27)):
\[S^* = d - F_Y^{-1}\left(\frac{h}{h+p}\right)\]\[g(S) = p\bar{n}(d-S) + hn(d-S),\]where \(n(\cdot)\) and \(\bar{n}(\cdot)\) are the loss function and complementary loss function, respectively, of the yield distribution.
Example (Example 9.6):
Using generic function to calculate loss functions:
>>> from scipy.stats import uniform >>> newsvendor_with_additive_yield_uncertainty(15, 75, 1.5e6, yield_distribution=uniform(-500000, 1000000)) (1833333.3333333335, 6249999.997499999)
Using
uniform_loss()
to calculate loss functions, which is more accurate:>>> from stockpyl.loss_functions import uniform_loss >>> loss_function = lambda x: uniform_loss(x, -500000, 500000) >>> newsvendor_with_additive_yield_uncertainty(15, 75, 1.5e6, yield_distribution=uniform(-500000, 1000000), loss_function=loss_function) (1833333.3333333335, 6250000.000000001)