Library
Newsvendor Model
NewsvendorModel.NVModel
— TypeNVModel(demand, cost, price, salvage=0)
Captures a Newsvendor model with unit cost
, unit selling price
, and demand
distribution. The demand can be any univariate distribution from the package Distributions.jl.
Examples
julia> using Distributions
julia> nvm = NVModel(demand = Normal(50, 20), cost = 5, price = 7)
Data of the Newsvendor Model
* Demand distribution: Normal{Float64}(μ=50.0, σ=20.0)
* Unit cost: 5.00
* Unit selling price: 7.00
This defines a model with unit cost 5 and unit price 7, where uncertain demand draws from a normal distribution with mean 50 and standard deviation 20.
Optional keyword arguments and their defaults:
NVModel(demand [; kwargs])
cost
for a unit; defaults to0.0
price
for selling a unit; defaults to0.0
salvage
value obtained from scraping a leftover unit; defaults to0.0
holding
cost induced by a leftover unit, e.g., extra captial cost or warehousing cost; essentially a negative salvage value; defaults to0.0
backorder
penalty for being short a unit, e.g., contractual penalty for missing delivery targets or missed future profit of an unserved customer; defaults to0.0
substitute
benefit received from selling an alternative to an unserved customer, e.g., when selling another product or serving in the future; essentially a negative backorder penalty; defaults to0.0
fixcost
fixed cost of operations; defaults to0.0
q_min
minimal feasible quantity, e.g., due to production limits; must be nonnegative; defaults to0
q_max
maximal feasible quantity, e.g., due to production limits; must be greater than or equal toq_min
; defaults toInf
Examples
Define a newsvendor problem with unit cost 5, unit price 7, uniform demand between 50 and 80, where a unit salvages for 0.5, and backorder comes at a penalty of 2 per unit, and the operations incur a fixed cost of 100, as follows:
julia> nvm2 = NVModel(demand = Uniform(50, 80), cost = 5, price = 7, salvage = 0.5, backorder = 2, fixcost = 100)
Data of the Newsvendor Model
* Demand distribution: Uniform{Float64}(a=50.0, b=80.0)
* Unit cost: 5.00
* Unit selling price: 7.00
* Unit salvage value: 0.50
* Unit backorder penalty: 2.00
* Fixed cost: 100.00
Note that demand is a necessary argument that can be passed without keyword in first place. Moreover, only values that differ from the default will be shown.
Define a newsvendor problem with unit cost 5, unit price 7, uniform demand between 50 and 80, where a unit salvages for 0.5, and backorder comes at a penalty of 2 per unit, and the operations incur a fixed cost of 100, as follows:
julia> nvm3 = NVModel(Uniform(50, 80), 5, 7, 0.5, backorder = 2, fixcost = 100, q_min=0)
Data of the Newsvendor Model
* Demand distribution: Uniform{Float64}(a=50.0, b=80.0)
* Unit cost: 5.00
* Unit selling price: 7.00
* Unit salvage value: 0.50
* Unit backorder penalty: 2.00
* Fixed cost: 100.00
julia> nvm3 == nvm2
true
Holding cost is essentially overage cost. Backorder penalty is essentially underage cost. The beer game has holding cost of 0.5 USD and backlog costs 1 USD per unit. Demand is assumed to be uniform betwen 0 and 300.
julia> beer = NVModel(Uniform(0, 300), backorder = 1, holding = 0.5)
Data of the Newsvendor Model
* Demand distribution: Uniform{Float64}(a=0.0, b=300.0)
* Unit cost: 0.00
* Unit selling price: 0.00
* Unit holding cost: 0.50
* Unit backorder penalty: 1.00
Optimizing expected profit
NewsvendorModel.solve
— Functionsolve(nvm::NVModel [; rounded = true])
Compute the stocking quantity q_opt(nvm)
that maximizes the expected profit as well as further the metrics when using it. The optimal quantity is rounded up to the next integer by default. To get the exact real number, set the keyword arguement rounded = false
.
Quantities
NewsvendorModel.q_opt
— Functionq_opt(anp::AbstractNewsvendorProblem; rounded = true)
Compute the quanitity that maximizes expected profit for a newsvendor problem (i.e., where critical fractile equals in-stock probability). Attempts to solve
\[F(q_{opt}) = \textrm{critical fractile} \]
where F is the c.d.f. of the demand distribution. Returns closest next integer unless rounded=false
; then, it returns exact real. Clamps at qmin and qmax.
q_opt(res::NVResult)
Get optimal quantity from a stored result.
NewsvendorModel.q_scarf
— Functionq_scarf(anp::AbstractNewsvendorProblem)
Compute the quanitity that maximizes the minimal expected profit among all distributions with the same mean and variance. Worst-case solution (Scarf, 1958)
\[q_{scarf} = \mu + \sigma/2 * (\sqrt{r} - \sqrt{1/r})\]
where
\[r = \frac{\textrm{underage cost}}{ \textrm{overage cost}}\]
Metrics
NewsvendorModel.critical_fractile
— Functioncritical_fractile(anp::AbstractNewsvendorProblem)
Compute the critical fractile for a newsvendor problem:
\[\textrm{critical fractile} = \frac{\textrm{underage cost}}{\textrm{underage cost} + \textrm{overage cost}}\]
critical_fractile(res::NVResult)
Get critical fractile from a stored result.
NewsvendorModel.profit
— Functionprofit(anp::AbstractNewsvendorProblem)
Compute expected profit when stocking quantity q_opt.
profit(anp::AbstractNewsvendorProblem, q)
Compute expected profit when stocking quantity q. It is given by
\[E[\textrm{profit}] = \textrm{underage cost} \times \mu - E[\textrm{mismatch cost}] + \textrm{profit shift}\]
profit(res::NVResult)
Get expected profit from a stored result.
NewsvendorModel.mismatch_cost
— Functionmismatch_cost(anp::AbstractNewsvendorProblem, q)
Compute expected mismatch cost when stocking quantity q. It is given by
\[E[\textrm{mismatch cost}] = \textrm{underage cost} \times E[\textrm{lost sales}] + \textrm{overage cost} \times E[\textrm{leftover}]\]
NewsvendorModel.lost_sales
— Functionlost_sales(anp::AbstractNewsvendorProblem, q)
Compute expected lost sales when stocking quantity q.
lost_sales(res::NVResult)
Get expected lost sales model from a stored result.
NewsvendorModel.sales
— Functionsales(anp::AbstractNewsvendorProblem, q)
Compute expected sales when stocking quantity q.
sales(res::NVResult)
Get expected sales from a stored result.
NewsvendorModel.leftover
— Functionleftover(anp::AbstractNewsvendorProblem, q)
Compute expected leftover inventory when stocking quantity q.
\[E[\textrm{leftover}] = \int_{-\infty}0^q(q - x)f(x)dx \]
leftover(res::NVResult)
Get expected leftover from a stored result.
AbstractNewsvendorProblem
NewsvendorModel.AbstractNewsvendorProblem
— TypeAn abstract newsvendor problem is essentially described by having a
- demand distribution,
- cost of overage,
- cost of underage, and
- profit_shift term.
Its standard concrete type might come as a NewsvendorModel, defined by unit cost, uni selling price etc.
Required functions that determine a newsvendor problem
NewsvendorModel.overage_cost
— Methodoverage_cost(anp::AbstractNewsvendorProblem)
Get the cost of overage of a newsvendor problem.
NewsvendorModel.underage_cost
— Methodunderage_cost(anp::AbstractNewsvendorProblem)
Get the cost of underage of a newsvendor problem.
NewsvendorModel.distr
— Methoddistr(anp::AbstractNewsvendorProblem)
Get the demand distribution of a newsvendor problem.
NewsvendorModel.profit_shift
— Methodprofit_shift(anp::AbstractNewsvendorProblem)
Define how profit is shifted because of fixed cost, penalty, etc.; defautls to 0.0.