|
from scipy.stats import poisson |
|
import math |
|
|
|
class ModelInfo: |
|
|
|
def __init__(self,ARRIVAL_RATE,STARTING_INVENTORY,CUSTOMER_1_PROP,CUSTOMER_2_PROP, |
|
HIGH_PRICE,LOW_PRICE) -> None: |
|
self.ARRIVAL_RATE =ARRIVAL_RATE |
|
self.STARTING_INVENTORY = STARTING_INVENTORY |
|
self.CUSTOMER_1_PROP = CUSTOMER_1_PROP |
|
self.CUSTOMER_2_PROP = CUSTOMER_2_PROP |
|
self.HIGH_PRICE = HIGH_PRICE |
|
self.LOW_PRICE = LOW_PRICE |
|
|
|
def set_ARRIVAL_RATE(self,ARRIVAL_RATE): |
|
return ModelInfo(ARRIVAL_RATE=ARRIVAL_RATE, |
|
STARTING_INVENTORY=self.STARTING_INVENTORY, |
|
CUSTOMER_1_PROP=self.CUSTOMER_1_PROP, |
|
CUSTOMER_2_PROP=self.CUSTOMER_2_PROP, |
|
HIGH_PRICE=self.HIGH_PRICE, |
|
LOW_PRICE=self.LOW_PRICE) |
|
|
|
def set_STARTING_INVENTORY(self,STARTING_INVENTORY): |
|
return ModelInfo(ARRIVAL_RATE=self.ARRIVAL_RATE, |
|
STARTING_INVENTORY=STARTING_INVENTORY, |
|
CUSTOMER_1_PROP=self.CUSTOMER_1_PROP, |
|
CUSTOMER_2_PROP=self.CUSTOMER_2_PROP, |
|
HIGH_PRICE=self.HIGH_PRICE, |
|
LOW_PRICE=self.LOW_PRICE) |
|
|
|
def set_CUSTOMER_1_PROP(self,CUSTOMER_1_PROP): |
|
return ModelInfo(ARRIVAL_RATE=self.ARRIVAL_RATE, |
|
STARTING_INVENTORY=self.STARTING_INVENTORY, |
|
CUSTOMER_1_PROP=CUSTOMER_1_PROP, |
|
CUSTOMER_2_PROP=self.CUSTOMER_2_PROP, |
|
HIGH_PRICE=self.HIGH_PRICE, |
|
LOW_PRICE=self.LOW_PRICE) |
|
|
|
def set_CUSTOMER_2_PROP(self,CUSTOMER_2_PROP): |
|
return ModelInfo(ARRIVAL_RATE=self.ARRIVAL_RATE, |
|
STARTING_INVENTORY=self.STARTING_INVENTORY, |
|
CUSTOMER_1_PROP=self.CUSTOMER_1_PROP, |
|
CUSTOMER_2_PROP=CUSTOMER_2_PROP, |
|
HIGH_PRICE=self.HIGH_PRICE, |
|
LOW_PRICE=self.LOW_PRICE) |
|
|
|
def set_HIGH_PRICE(self,HIGH_PRICE): |
|
return ModelInfo(ARRIVAL_RATE=self.ARRIVAL_RATE, |
|
STARTING_INVENTORY=self.STARTING_INVENTORY, |
|
CUSTOMER_1_PROP=self.CUSTOMER_1_PROP, |
|
CUSTOMER_2_PROP=self.CUSTOMER_2_PROP, |
|
HIGH_PRICE=HIGH_PRICE, |
|
LOW_PRICE=self.LOW_PRICE) |
|
|
|
def set_LOW_PRICE(self,LOW_PRICE): |
|
return ModelInfo(ARRIVAL_RATE=self.ARRIVAL_RATE, |
|
STARTING_INVENTORY=self.STARTING_INVENTORY, |
|
CUSTOMER_1_PROP=self.CUSTOMER_1_PROP, |
|
CUSTOMER_2_PROP=self.CUSTOMER_2_PROP, |
|
HIGH_PRICE=self.HIGH_PRICE, |
|
LOW_PRICE=LOW_PRICE) |
|
|
|
def expected_revenue_in_period_1(model:ModelInfo): |
|
mu = model.ARRIVAL_RATE |
|
c1 = model.STARTING_INVENTORY |
|
customer_1_prop = model.CUSTOMER_1_PROP |
|
customer_2_prop = model.CUSTOMER_2_PROP |
|
p1 = model.HIGH_PRICE |
|
|
|
expected_sales = 0 |
|
|
|
for i in range(c1): |
|
expected_sales += i * poisson.pmf(i,mu * customer_2_prop) |
|
expected_sales += c1 * (1 - poisson.cdf(c1-1,mu * customer_2_prop)) |
|
|
|
return expected_sales * p1 |
|
|
|
def expected_revenue_in_period_2_given_price(c2,p2,residual_customer_1_period_1,residual_customer_2_period_1,model:ModelInfo): |
|
mu = model.ARRIVAL_RATE |
|
customer_1_prop = model.CUSTOMER_1_PROP |
|
customer_2_prop = model.CUSTOMER_2_PROP |
|
expected_sales = 0 |
|
if residual_customer_2_period_1 > 0: |
|
|
|
return 0 |
|
|
|
|
|
total_residual = residual_customer_1_period_1 |
|
|
|
if p2 == model.LOW_PRICE: |
|
|
|
|
|
if total_residual >= c2: |
|
|
|
return c2 * p2 |
|
else: |
|
|
|
|
|
|
|
expected_sales += total_residual |
|
for i in range(c2 - total_residual): |
|
|
|
expected_sales += i * poisson.pmf(i,mu) |
|
expected_sales += (c2 - total_residual) * (1 - poisson.cdf(c2 - total_residual - 1,mu)) |
|
|
|
return expected_sales * p2 |
|
|
|
|
|
elif p2 == model.HIGH_PRICE: |
|
|
|
|
|
for i in range(c2): |
|
expected_sales += i * poisson.pmf(i,mu * customer_2_prop) |
|
expected_sales += c2 * (1 - poisson.cdf(c2 - 1, mu * customer_2_prop)) |
|
return expected_sales * p2 |
|
|
|
elif p2 > model.HIGH_PRICE or p2 <=0: |
|
return 0 |
|
|
|
def expected_revenue_in_period_2(c2,residual_customer_1_period_1,residual_customer_2_period_1,c2_threshold,model:ModelInfo): |
|
if c2 >= c2_threshold: |
|
p2 = model.LOW_PRICE |
|
else: |
|
p2 = model.HIGH_PRICE |
|
return expected_revenue_in_period_2_given_price(c2=c2,p2=p2, |
|
residual_customer_1_period_1=residual_customer_1_period_1, |
|
residual_customer_2_period_1=residual_customer_2_period_1, |
|
model=model) |
|
|
|
def calculate_probability(c2,residual_customer_1,residual_customer_2,model:ModelInfo): |
|
|
|
mu = model.ARRIVAL_RATE |
|
customer_1_prop = model.CUSTOMER_1_PROP |
|
customer_2_prop = model.CUSTOMER_2_PROP |
|
c1 = model.STARTING_INVENTORY |
|
|
|
customer_2_period_1 = c1 - c2 + residual_customer_2 |
|
if c2 >= 0 and residual_customer_2 == 0: |
|
|
|
prob_1 = poisson.pmf(customer_2_period_1,mu * customer_2_prop) |
|
|
|
prob_2 = poisson.pmf(residual_customer_1,mu * customer_1_prop) |
|
|
|
return prob_1 * prob_2 |
|
elif c2 == 0 and residual_customer_2 > 0: |
|
|
|
prob_2 = poisson.pmf(residual_customer_1,mu * customer_1_prop) |
|
|
|
prob_3 = poisson.pmf(c1+residual_customer_2,mu * customer_2_prop) |
|
|
|
return prob_2 * prob_3 |
|
elif c2 > 0 and residual_customer_2 > 0: |
|
|
|
return 0 |
|
|
|
|
|
|
|
def calculate_expected_total_revenue(c2_threshold,model:ModelInfo): |
|
total_expected_revenue_in_period_2 = 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
RC2=0 |
|
for inv in range(1,model.STARTING_INVENTORY + 1): |
|
|
|
|
|
customer_2_period_1 = model.STARTING_INVENTORY - inv |
|
if inv < c2_threshold: |
|
|
|
total_expected_revenue_in_period_2 += poisson.pmf(customer_2_period_1,model.ARRIVAL_RATE * model.CUSTOMER_2_PROP) * expected_revenue_in_period_2(inv,0,0,c2_threshold,model=model) |
|
elif inv >= c2_threshold: |
|
|
|
for RC1 in range(inv): |
|
|
|
|
|
total_expected_revenue_in_period_2 += calculate_probability(inv,RC1,RC2,model=model) * expected_revenue_in_period_2(inv,RC1,RC2,c2_threshold=c2_threshold,model=model) |
|
|
|
|
|
|
|
prob_1 = poisson.pmf(customer_2_period_1,model.ARRIVAL_RATE * model.CUSTOMER_2_PROP) |
|
|
|
cum_prob_2 = 1-poisson.cdf(inv-1,model.ARRIVAL_RATE * model.CUSTOMER_1_PROP) |
|
total_expected_revenue_in_period_2 += prob_1 * cum_prob_2 * expected_revenue_in_period_2(inv,inv,0,c2_threshold,model=model) |
|
|
|
return total_expected_revenue_in_period_2 + expected_revenue_in_period_1(model=model) |
|
|
|
def calculate_expected_total_revenue_given_low_low(model:ModelInfo): |
|
mu = 2*model.ARRIVAL_RATE |
|
c1 = model.STARTING_INVENTORY |
|
p = model.LOW_PRICE |
|
|
|
expected_sales = 0 |
|
|
|
for i in range(c1): |
|
expected_sales += i * poisson.pmf(i,mu) |
|
expected_sales += c1 * (1 - poisson.cdf(c1-1,mu)) |
|
|
|
return expected_sales * p |
|
|
|
|
|
|
|
def calculate_expected_total_revenue_given_price(p2,model:ModelInfo): |
|
total_expected_revenue_in_period_2 = 0 |
|
|
|
|
|
RC2=0 |
|
for inv in range(1,model.STARTING_INVENTORY+1): |
|
customer_2_period_1 = model.STARTING_INVENTORY - inv |
|
for RC1 in range(inv): |
|
|
|
total_expected_revenue_in_period_2 += calculate_probability(inv,RC1,RC2,model=model) * expected_revenue_in_period_2_given_price(inv,p2,RC1,RC2,model=model) |
|
|
|
prob_1 = poisson.pmf(customer_2_period_1,model.ARRIVAL_RATE * model.CUSTOMER_2_PROP) |
|
|
|
cum_prob_2 = 1-poisson.cdf(inv-1,model.ARRIVAL_RATE * model.CUSTOMER_1_PROP) |
|
total_expected_revenue_in_period_2 += prob_1 * cum_prob_2 * expected_revenue_in_period_2_given_price(inv,p2,inv,0,model=model) |
|
|
|
return total_expected_revenue_in_period_2 + expected_revenue_in_period_1(model=model) |
|
|
|
def get_best_dynamic_threshold(model:ModelInfo): |
|
best_rev = 0 |
|
for inv in range(model.STARTING_INVENTORY+2): |
|
curr_rev = calculate_expected_total_revenue(c2_threshold=inv,model=model) |
|
if curr_rev >= best_rev: |
|
best_rev = curr_rev |
|
best_inv = inv |
|
return (best_inv,best_rev) |
|
|
|
def get_static_pricing(model:ModelInfo): |
|
return (calculate_expected_total_revenue_given_low_low(model=model),calculate_expected_total_revenue_given_price(model.HIGH_PRICE,model=model),calculate_expected_total_revenue_given_price(model.LOW_PRICE,model=model)) |
|
|
|
|
|
def gap_between_dynamic_and_static(model:ModelInfo): |
|
tmp =max(get_static_pricing(model=model)) |
|
return calculate_expected_total_revenue(get_best_dynamic_threshold(model=model)[0],model=model)-tmp |
|
|
|
|
|
def High_Low_Gap(high_low,model:ModelInfo): |
|
res = gap_between_dynamic_and_static(model=model.set_HIGH_PRICE(HIGH_PRICE=high_low[0]).set_LOW_PRICE(high_low[1])) |
|
return res |
|
|
|
|
|
def Starting_Inv_Arrival_Gap(starting_inv_arrival,model:ModelInfo): |
|
res = gap_between_dynamic_and_static(model=model.set_STARTING_INVENTORY(starting_inv_arrival[0]).set_ARRIVAL_RATE(starting_inv_arrival[1])) |
|
return res |
|
|
|
|