WayneLinn commited on
Commit
ecdf2dd
1 Parent(s): b5ecbe8

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +32 -0
  2. requirements.txt +2 -0
  3. utils.py +249 -0
app.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from utils import *
3
+
4
+
5
+ def gap_func(demand,inventory,customer_1_proportion_percent,high_price,low_price):
6
+ customer_1_prop = customer_1_proportion_percent / 100
7
+ model = ModelInfo(ARRIVAL_RATE=demand,
8
+ STARTING_INVENTORY=inventory,
9
+ CUSTOMER_1_PROP=customer_1_prop,
10
+ CUSTOMER_2_PROP=1-customer_1_prop,
11
+ HIGH_PRICE=high_price,
12
+ LOW_PRICE=low_price)
13
+ gap = gap_between_dynamic_and_static(model=model)
14
+ dynamic = get_best_dynamic_threshold(model=model)
15
+ dynamic_rev,inv_threshold = dynamic
16
+ static_result= get_static_pricing(model=model)
17
+ low_low,high_high,high_low = static_result
18
+
19
+ return gap,dynamic_rev,inv_threshold,low_low,high_high,high_low
20
+
21
+
22
+ demo = gr.Interface(fn=gap_func,
23
+ inputs=["number","number",gr.Slider(0, 100,value=50),"number","number"],
24
+ outputs=[gr.Textbox(label='Gap'),
25
+ gr.Textbox(label='Dynamic'),
26
+ gr.Textbox(label='inventory_threshold'),
27
+ gr.Textbox(label='Low-Low'),
28
+ gr.Textbox(label='High-High'),
29
+ gr.Textbox(label='High-Low')])
30
+
31
+ if __name__ == "__main__":
32
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio
2
+ scipy
utils.py ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from scipy.stats import poisson
2
+ import math
3
+
4
+ class ModelInfo:
5
+
6
+ def __init__(self,ARRIVAL_RATE,STARTING_INVENTORY,CUSTOMER_1_PROP,CUSTOMER_2_PROP,
7
+ HIGH_PRICE,LOW_PRICE) -> None:
8
+ self.ARRIVAL_RATE =ARRIVAL_RATE
9
+ self.STARTING_INVENTORY = STARTING_INVENTORY
10
+ self.CUSTOMER_1_PROP = CUSTOMER_1_PROP
11
+ self.CUSTOMER_2_PROP = CUSTOMER_2_PROP
12
+ self.HIGH_PRICE = HIGH_PRICE
13
+ self.LOW_PRICE = LOW_PRICE
14
+
15
+ def set_ARRIVAL_RATE(self,ARRIVAL_RATE):
16
+ return ModelInfo(ARRIVAL_RATE=ARRIVAL_RATE,
17
+ STARTING_INVENTORY=self.STARTING_INVENTORY,
18
+ CUSTOMER_1_PROP=self.CUSTOMER_1_PROP,
19
+ CUSTOMER_2_PROP=self.CUSTOMER_2_PROP,
20
+ HIGH_PRICE=self.HIGH_PRICE,
21
+ LOW_PRICE=self.LOW_PRICE)
22
+
23
+ def set_STARTING_INVENTORY(self,STARTING_INVENTORY):
24
+ return ModelInfo(ARRIVAL_RATE=self.ARRIVAL_RATE,
25
+ STARTING_INVENTORY=STARTING_INVENTORY,
26
+ CUSTOMER_1_PROP=self.CUSTOMER_1_PROP,
27
+ CUSTOMER_2_PROP=self.CUSTOMER_2_PROP,
28
+ HIGH_PRICE=self.HIGH_PRICE,
29
+ LOW_PRICE=self.LOW_PRICE)
30
+
31
+ def set_CUSTOMER_1_PROP(self,CUSTOMER_1_PROP):
32
+ return ModelInfo(ARRIVAL_RATE=self.ARRIVAL_RATE,
33
+ STARTING_INVENTORY=self.STARTING_INVENTORY,
34
+ CUSTOMER_1_PROP=CUSTOMER_1_PROP,
35
+ CUSTOMER_2_PROP=self.CUSTOMER_2_PROP,
36
+ HIGH_PRICE=self.HIGH_PRICE,
37
+ LOW_PRICE=self.LOW_PRICE)
38
+
39
+ def set_CUSTOMER_2_PROP(self,CUSTOMER_2_PROP):
40
+ return ModelInfo(ARRIVAL_RATE=self.ARRIVAL_RATE,
41
+ STARTING_INVENTORY=self.STARTING_INVENTORY,
42
+ CUSTOMER_1_PROP=self.CUSTOMER_1_PROP,
43
+ CUSTOMER_2_PROP=CUSTOMER_2_PROP,
44
+ HIGH_PRICE=self.HIGH_PRICE,
45
+ LOW_PRICE=self.LOW_PRICE)
46
+
47
+ def set_HIGH_PRICE(self,HIGH_PRICE):
48
+ return ModelInfo(ARRIVAL_RATE=self.ARRIVAL_RATE,
49
+ STARTING_INVENTORY=self.STARTING_INVENTORY,
50
+ CUSTOMER_1_PROP=self.CUSTOMER_1_PROP,
51
+ CUSTOMER_2_PROP=self.CUSTOMER_2_PROP,
52
+ HIGH_PRICE=HIGH_PRICE,
53
+ LOW_PRICE=self.LOW_PRICE)
54
+
55
+ def set_LOW_PRICE(self,LOW_PRICE):
56
+ return ModelInfo(ARRIVAL_RATE=self.ARRIVAL_RATE,
57
+ STARTING_INVENTORY=self.STARTING_INVENTORY,
58
+ CUSTOMER_1_PROP=self.CUSTOMER_1_PROP,
59
+ CUSTOMER_2_PROP=self.CUSTOMER_2_PROP,
60
+ HIGH_PRICE=self.HIGH_PRICE,
61
+ LOW_PRICE=LOW_PRICE)
62
+
63
+ def expected_revenue_in_period_1(model:ModelInfo):
64
+ mu = model.ARRIVAL_RATE
65
+ c1 = model.STARTING_INVENTORY
66
+ customer_1_prop = model.CUSTOMER_1_PROP
67
+ customer_2_prop = model.CUSTOMER_2_PROP
68
+ p1 = model.HIGH_PRICE
69
+
70
+ expected_sales = 0
71
+
72
+ for i in range(c1):
73
+ expected_sales += i * poisson.pmf(i,mu * customer_2_prop)
74
+ expected_sales += c1 * (1 - poisson.cdf(c1-1,mu * customer_2_prop))
75
+
76
+ return expected_sales * p1
77
+
78
+ def expected_revenue_in_period_2_given_price(c2,p2,residual_customer_1_period_1,residual_customer_2_period_1,model:ModelInfo):
79
+ mu = model.ARRIVAL_RATE
80
+ customer_1_prop = model.CUSTOMER_1_PROP
81
+ customer_2_prop = model.CUSTOMER_2_PROP
82
+ expected_sales = 0
83
+ if residual_customer_2_period_1 > 0:
84
+ # All are sold to customer 2 in period 1
85
+ return 0
86
+
87
+ # residual_customer_2_period_1 will always be 0, so not included here
88
+ total_residual = residual_customer_1_period_1
89
+
90
+ if p2 == model.LOW_PRICE:
91
+
92
+ # if there is already enough customer before more arrivals willing to buy, so sell to them
93
+ if total_residual >= c2:
94
+ # everyone will buy at price = 1
95
+ return c2 * p2
96
+ else:
97
+ # finding the expectation of min(# of customer + residual, c2)
98
+ # expressed as min(# of customer, c2 - residual) + residual
99
+ # makes sure c2 - residual is not negative
100
+ expected_sales += total_residual
101
+ for i in range(c2 - total_residual):
102
+ # the arrival of cust 1 from period 1 is fixed so mu nvr add the proportion of cust 1 arrivals
103
+ expected_sales += i * poisson.pmf(i,mu)
104
+ expected_sales += (c2 - total_residual) * (1 - poisson.cdf(c2 - total_residual - 1,mu))
105
+
106
+ return expected_sales * p2
107
+
108
+
109
+ elif p2 == model.HIGH_PRICE:
110
+ # only customer 2 buys
111
+ # residual customer 2 should be 0, since p1=2 and all customer 2 who arrived in period 1 will be buy
112
+ for i in range(c2):
113
+ expected_sales += i * poisson.pmf(i,mu * customer_2_prop)
114
+ expected_sales += c2 * (1 - poisson.cdf(c2 - 1, mu * customer_2_prop))
115
+ return expected_sales * p2
116
+
117
+ elif p2 > model.HIGH_PRICE or p2 <=0:
118
+ return 0
119
+
120
+ def expected_revenue_in_period_2(c2,residual_customer_1_period_1,residual_customer_2_period_1,c2_threshold,model:ModelInfo):
121
+ if c2 >= c2_threshold:
122
+ p2 = model.LOW_PRICE
123
+ else:
124
+ p2 = model.HIGH_PRICE
125
+ return expected_revenue_in_period_2_given_price(c2=c2,p2=p2,
126
+ residual_customer_1_period_1=residual_customer_1_period_1,
127
+ residual_customer_2_period_1=residual_customer_2_period_1,
128
+ model=model)
129
+
130
+ def calculate_probability(c2,residual_customer_1,residual_customer_2,model:ModelInfo):
131
+ # calculate the probablity having c2 inventory, residual_customer_1, residual_customer_2 in period 2
132
+ mu = model.ARRIVAL_RATE
133
+ customer_1_prop = model.CUSTOMER_1_PROP
134
+ customer_2_prop = model.CUSTOMER_2_PROP
135
+ c1 = model.STARTING_INVENTORY
136
+
137
+ customer_2_period_1 = c1 - c2 + residual_customer_2
138
+ if c2 >= 0 and residual_customer_2 == 0:
139
+ # case when customer 2 arrival in period 1 is less than or equal to inventory in period 1
140
+ prob_1 = poisson.pmf(customer_2_period_1,mu * customer_2_prop)
141
+ # all customer 1 arrival in period 1 becomes residual_customer_1 in period 2
142
+ prob_2 = poisson.pmf(residual_customer_1,mu * customer_1_prop)
143
+ #print(f'prob_1:{prob_1}|prob_2:{prob_2}')
144
+ return prob_1 * prob_2
145
+ elif c2 == 0 and residual_customer_2 > 0:
146
+ # case when customer 2 arrival in period 1 is more than inventory in period 1
147
+ prob_2 = poisson.pmf(residual_customer_1,mu * customer_1_prop)
148
+ # prob that total customer 2 in period 1 is c1 + residual_customer_2, > c1
149
+ prob_3 = poisson.pmf(c1+residual_customer_2,mu * customer_2_prop)
150
+ #print(f'prob_2:{prob_2}|prob_3:{prob_3}')
151
+ return prob_2 * prob_3
152
+ elif c2 > 0 and residual_customer_2 > 0:
153
+ # if there is inventory in period 2, there should not be any residual customer 2
154
+ return 0
155
+
156
+
157
+
158
+ def calculate_expected_total_revenue(c2_threshold,model:ModelInfo):
159
+ total_expected_revenue_in_period_2 = 0
160
+ #inv = 0
161
+ #for RC1 in range(100):
162
+ # for RC2 in range(100):
163
+ # total_expected_revenue_in_period_2 += calculate_probability(inv,RC1,RC2) * expected_revenue_in_period_2(inv,RC1,RC2,c2_threshold=c2_threshold)
164
+ #print(total_expected_revenue_in_period_2)
165
+
166
+ RC2=0
167
+ for inv in range(1,model.STARTING_INVENTORY + 1):
168
+ # if there is no price change and remains high price, RC1 will have no effect, the expected revenue in period 2
169
+ # is solely dependent on the customer 2
170
+ customer_2_period_1 = model.STARTING_INVENTORY - inv
171
+ if inv < c2_threshold:
172
+ # 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
173
+ 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)
174
+ elif inv >= c2_threshold:
175
+ #there is price change to LOW PRICE
176
+ for RC1 in range(inv):
177
+ # when residual is less than inv in period 2, there are still different values of prob and expected rev
178
+ #inv=2 | RC1 =0,1 | RC2=0
179
+ 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)
180
+ #when residual is greater than or qual to inv in period 2, the expected rev is just inv * LOW_PRICE
181
+ #inv =2 | RC1 >=2 | RC2=0 | expected rev in period 2 = inv * LOW_PIRCE = 2
182
+ #prob of the no of cutomer 2 in period 1, same for all cases of RC1, thus we can factor it out
183
+ prob_1 = poisson.pmf(customer_2_period_1,model.ARRIVAL_RATE * model.CUSTOMER_2_PROP)
184
+ # prob_1 * ( prob_2 + prob_2 + .....)
185
+ cum_prob_2 = 1-poisson.cdf(inv-1,model.ARRIVAL_RATE * model.CUSTOMER_1_PROP)
186
+ total_expected_revenue_in_period_2 += prob_1 * cum_prob_2 * expected_revenue_in_period_2(inv,inv,0,c2_threshold,model=model)
187
+
188
+ return total_expected_revenue_in_period_2 + expected_revenue_in_period_1(model=model)
189
+
190
+ def calculate_expected_total_revenue_given_low_low(model:ModelInfo):
191
+ mu = 2*model.ARRIVAL_RATE
192
+ c1 = model.STARTING_INVENTORY
193
+ p = model.LOW_PRICE
194
+
195
+ expected_sales = 0
196
+
197
+ for i in range(c1):
198
+ expected_sales += i * poisson.pmf(i,mu)
199
+ expected_sales += c1 * (1 - poisson.cdf(c1-1,mu))
200
+
201
+ return expected_sales * p
202
+
203
+
204
+
205
+ def calculate_expected_total_revenue_given_price(p2,model:ModelInfo):
206
+ total_expected_revenue_in_period_2 = 0
207
+
208
+
209
+ RC2=0
210
+ for inv in range(1,model.STARTING_INVENTORY+1):
211
+ customer_2_period_1 = model.STARTING_INVENTORY - inv
212
+ for RC1 in range(inv):
213
+ # RC1 =0,1
214
+ 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)
215
+ #RC1 =2,3,...
216
+ prob_1 = poisson.pmf(customer_2_period_1,model.ARRIVAL_RATE * model.CUSTOMER_2_PROP)
217
+ # prob_1 * ( prob_2 + prob_2 + .....)
218
+ cum_prob_2 = 1-poisson.cdf(inv-1,model.ARRIVAL_RATE * model.CUSTOMER_1_PROP)
219
+ total_expected_revenue_in_period_2 += prob_1 * cum_prob_2 * expected_revenue_in_period_2_given_price(inv,p2,inv,0,model=model)
220
+
221
+ return total_expected_revenue_in_period_2 + expected_revenue_in_period_1(model=model)
222
+
223
+ def get_best_dynamic_threshold(model:ModelInfo):
224
+ best_rev = 0
225
+ for inv in range(model.STARTING_INVENTORY+1):
226
+ curr_rev = calculate_expected_total_revenue(c2_threshold=inv,model=model)
227
+ if curr_rev >= best_rev:
228
+ best_rev = curr_rev
229
+ best_inv = inv
230
+ return (best_inv,best_rev)
231
+
232
+ def get_static_pricing(model:ModelInfo):
233
+ 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))
234
+
235
+
236
+ def gap_between_dynamic_and_static(model:ModelInfo):
237
+ tmp =max(get_static_pricing(model=model))
238
+ return calculate_expected_total_revenue(get_best_dynamic_threshold(model=model)[0],model=model)-tmp
239
+
240
+
241
+ def High_Low_Gap(high_low,model:ModelInfo):
242
+ res = gap_between_dynamic_and_static(model=model.set_HIGH_PRICE(HIGH_PRICE=high_low[0]).set_LOW_PRICE(high_low[1]))
243
+ return res
244
+
245
+
246
+ def Starting_Inv_Arrival_Gap(starting_inv_arrival,model:ModelInfo):
247
+ res = gap_between_dynamic_and_static(model=model.set_STARTING_INVENTORY(starting_inv_arrival[0]).set_ARRIVAL_RATE(starting_inv_arrival[1]))
248
+ return res
249
+