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
approximatetoTrueto 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_costordemand_rate< 0, or ifholding_costorstockout_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
approximateisFalse. IfapproximateisTrue, 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_quantityas 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
approximatetoTrueto 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_costordemand_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
approximateisFalseand\[\psi = \frac{\lambda}{\lambda+\mu}\]if
approximateisTrue.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_levelis 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_proborrecovery_probis 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_quantityis 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_costordemand_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_quantityis 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_costordemand_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_levelis supplied) calculate expected cost of given solution.Either provide
yield_meanandyield_sdto use a normal yield distribution, or provideyield_distributionas a frozenrv_continuousorrv_discreteobject. Ifyield_distributionis provided, then loss functions are calculated usingloss_functions.continuous_loss()orloss_functions.discrete_loss(), unlessloss_functionis 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_meanoryield_sdisNone.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_distributionisNone.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_meanisNoneoryield_sdisNone) andyield_distributionisNone.
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)