WayneLinn's picture
Update utils.py
0b44e9e verified
raw
history blame
11.9 kB
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:
# All are sold to customer 2 in period 1
return 0
# residual_customer_2_period_1 will always be 0, so not included here
total_residual = residual_customer_1_period_1
if p2 == model.LOW_PRICE:
# if there is already enough customer before more arrivals willing to buy, so sell to them
if total_residual >= c2:
# everyone will buy at price = 1
return c2 * p2
else:
# finding the expectation of min(# of customer + residual, c2)
# expressed as min(# of customer, c2 - residual) + residual
# makes sure c2 - residual is not negative
expected_sales += total_residual
for i in range(c2 - total_residual):
# the arrival of cust 1 from period 1 is fixed so mu nvr add the proportion of cust 1 arrivals
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:
# only customer 2 buys
# residual customer 2 should be 0, since p1=2 and all customer 2 who arrived in period 1 will be buy
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):
# calculate the probablity having c2 inventory, residual_customer_1, residual_customer_2 in period 2
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:
# case when customer 2 arrival in period 1 is less than or equal to inventory in period 1
prob_1 = poisson.pmf(customer_2_period_1,mu * customer_2_prop)
# all customer 1 arrival in period 1 becomes residual_customer_1 in period 2
prob_2 = poisson.pmf(residual_customer_1,mu * customer_1_prop)
#print(f'prob_1:{prob_1}|prob_2:{prob_2}')
return prob_1 * prob_2
elif c2 == 0 and residual_customer_2 > 0:
# case when customer 2 arrival in period 1 is more than inventory in period 1
prob_2 = poisson.pmf(residual_customer_1,mu * customer_1_prop)
# prob that total customer 2 in period 1 is c1 + residual_customer_2, > c1
prob_3 = poisson.pmf(c1+residual_customer_2,mu * customer_2_prop)
#print(f'prob_2:{prob_2}|prob_3:{prob_3}')
return prob_2 * prob_3
elif c2 > 0 and residual_customer_2 > 0:
# if there is inventory in period 2, there should not be any residual customer 2
return 0
def calculate_expected_total_revenue(c2_threshold,model:ModelInfo):
total_expected_revenue_in_period_2 = 0
#inv = 0
#for RC1 in range(100):
# for RC2 in range(100):
# total_expected_revenue_in_period_2 += calculate_probability(inv,RC1,RC2) * expected_revenue_in_period_2(inv,RC1,RC2,c2_threshold=c2_threshold)
#print(total_expected_revenue_in_period_2)
RC2=0
for inv in range(1,model.STARTING_INVENTORY + 1):
# if there is no price change and remains high price, RC1 will have no effect, the expected revenue in period 2
# is solely dependent on the customer 2
customer_2_period_1 = model.STARTING_INVENTORY - inv
if inv < c2_threshold:
# prob of no of customer 2 in period * expected_rev_in_period_2 given c2=inv,RC1 is any value (since all same),RC2=0
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:
#there is price change to LOW PRICE
for RC1 in range(inv):
# when residual is less than inv in period 2, there are still different values of prob and expected rev
#inv=2 | RC1 =0,1 | RC2=0
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)
#when residual is greater than or qual to inv in period 2, the expected rev is just inv * LOW_PRICE
#inv =2 | RC1 >=2 | RC2=0 | expected rev in period 2 = inv * LOW_PIRCE = 2
#prob of the no of cutomer 2 in period 1, same for all cases of RC1, thus we can factor it out
prob_1 = poisson.pmf(customer_2_period_1,model.ARRIVAL_RATE * model.CUSTOMER_2_PROP)
# prob_1 * ( prob_2 + prob_2 + .....)
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):
# RC1 =0,1
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)
#RC1 =2,3,...
prob_1 = poisson.pmf(customer_2_period_1,model.ARRIVAL_RATE * model.CUSTOMER_2_PROP)
# prob_1 * ( prob_2 + prob_2 + .....)
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