Yield Management (mpex24)
/***************************************************************/
/* */
/* S A S S A M P L E L I B R A R Y */
/* */
/* NAME: mpex24 */
/* TITLE: Yield Management (mpex24) */
/* PRODUCT: OR */
/* SYSTEM: ALL */
/* PROCS: OPTMODEL */
/* DATA: */
/* */
/* SUPPORT: UPDATE: */
/* REF: */
/* MISC: Example 24 from the Mathematical Programming */
/* Examples book. */
/* */
/***************************************************************/
data class_data;
input class $9. num_seats;
datalines;
First 37
Business 38
Economy 47
;
data price_data;
input period class $9. price1-price3;
datalines;
1 First 1200 1000 950
1 Business 900 800 600
1 Economy 500 300 200
2 First 1400 1300 1150
2 Business 1100 900 750
2 Economy 700 400 350
3 First 1500 900 850
3 Business 820 800 500
3 Economy 480 470 450
;
data scenario_data;
input prob;
datalines;
0.1
0.7
0.2
;
data demand_data;
input period scenario class $9. demand1-demand3;
datalines;
1 1 First 10 15 20
1 1 Business 20 25 35
1 1 Economy 45 55 60
1 2 First 20 25 35
1 2 Business 40 42 45
1 2 Economy 50 52 63
1 3 First 45 50 60
1 3 Business 45 46 47
1 3 Economy 55 56 64
2 1 First 20 25 35
2 1 Business 42 45 46
2 1 Economy 50 52 60
2 2 First 10 40 50
2 2 Business 50 60 80
2 2 Economy 60 65 90
2 3 First 50 55 80
2 3 Business 20 30 50
2 3 Economy 10 40 60
3 1 First 30 35 40
3 1 Business 40 50 55
3 1 Economy 50 60 80
3 2 First 30 40 60
3 2 Business 10 40 45
3 2 Economy 50 60 70
3 3 First 50 70 80
3 3 Business 40 45 60
3 3 Economy 60 65 70
;
data actual_demand_data;
input period class $9. demand1-demand3;
datalines;
1 First 25 30 40
1 Business 50 40 45
1 Economy 50 53 65
2 First 22 45 50
2 Business 45 55 75
2 Economy 50 60 80
3 First 45 60 75
3 Business 20 40 50
3 Economy 55 60 75
;
%let num_periods = 3;
%let num_planes = 6;
%let plane_cost = 50000;
%let transfer_fraction_ub = 0.10;
%let num_options = 3;
proc optmodel;
set PERIODS = 1..&num_periods;
set <str> CLASSES;
num num_seats {CLASSES};
read data class_data into CLASSES=[class] num_seats;
set OPTIONS = 1..&num_options;
num price {PERIODS, CLASSES, OPTIONS};
read data price_data into [period class]
{option in OPTIONS} <price[period,class,option]=col('price'||option)>;
set SCENARIOS;
num prob {SCENARIOS};
read data scenario_data into SCENARIOS=[_N_] prob;
set SCENARIOS2 = SCENARIOS cross SCENARIOS;
set SCENARIOS3 = SCENARIOS2 cross SCENARIOS;
num demand {PERIODS, SCENARIOS, CLASSES, OPTIONS};
read data demand_data into [period scenario class]
{option in OPTIONS}
<demand[period,scenario,class,option]=col('demand'||option)>;
num actual_demand {PERIODS, CLASSES, OPTIONS};
read data actual_demand_data into [period class]
{option in OPTIONS}
<actual_demand[period,class,option]=col('demand'||option)>;
num actual_price {PERIODS, CLASSES};
num actual_sales {PERIODS, CLASSES};
num actual_revenue {PERIODS, CLASSES};
num current_period;
var P1 {CLASSES, OPTIONS} binary;
var P2 {SCENARIOS, CLASSES, OPTIONS} binary;
var P3 {SCENARIOS2, CLASSES, OPTIONS} binary;
var S1 {SCENARIOS, CLASSES, OPTIONS} >= 0;
var S2 {SCENARIOS2, CLASSES, OPTIONS} >= 0;
var S3 {SCENARIOS3, CLASSES, OPTIONS} >= 0;
var R1 {SCENARIOS, CLASSES, OPTIONS} >= 0;
var R2 {SCENARIOS2, CLASSES, OPTIONS} >= 0;
var R3 {SCENARIOS3, CLASSES, OPTIONS} >= 0;
var TransferFrom {SCENARIOS3, CLASSES} >= 0;
var TransferTo {SCENARIOS3, CLASSES} >= 0;
var NumPlanes >= 0 <= &num_planes integer;
con NumPlanes_con {<i,j,k> in SCENARIOS3, class in CLASSES}:
sum {option in OPTIONS}
(S1[i,class,option] + S2[i,j,class,option] + S3[i,j,k,class,option])
+ TransferFrom[i,j,k,class] - TransferTo[i,j,k,class]
<= num_seats[class] * NumPlanes;
for {<i,j,k> in SCENARIOS3, class in CLASSES} do;
TransferFrom[i,j,k,class].ub = &transfer_fraction_ub * num_seats[class];
TransferTo[i,j,k,class].ub = &transfer_fraction_ub * num_seats[class];
end;
con Balance_con {<i,j,k> in SCENARIOS3}:
sum {class in CLASSES} TransferFrom[i,j,k,class]
= sum {class in CLASSES} TransferTo[i,j,k,class];
con P1_con {class in CLASSES}:
sum {option in OPTIONS} P1[class,option] = 1;
con P2_con {i in SCENARIOS, class in CLASSES}:
sum {option in OPTIONS} P2[i,class,option] = 1;
con P3_con {<i,j> in SCENARIOS2, class in CLASSES}:
sum {option in OPTIONS} P3[i,j,class,option] = 1;
con S1_con {i in SCENARIOS, class in CLASSES, option in OPTIONS}:
S1[i,class,option] <= demand[1,i,class,option] * P1[class,option];
con S2_con {<i,j> in SCENARIOS2, class in CLASSES, option in OPTIONS}:
S2[i,j,class,option] <= demand[2,j,class,option] * P2[i,class,option];
con S3_con {<i,j,k> in SCENARIOS3, class in CLASSES, option in OPTIONS}:
S3[i,j,k,class,option] <= demand[3,k,class,option] *
P3[i,j,class,option];
/* R1[i,class,option] =
price[1,class,option] * P1[class,option] * S1[i,class,option] */
con R1_con_a {i in SCENARIOS, class in CLASSES, option in OPTIONS}:
R1[i,class,option] <= price[1,class,option] * S1[i,class,option];
con R1_con_b {i in SCENARIOS, class in CLASSES, option in OPTIONS}:
price[1,class,option] * S1[i,class,option] - R1[i,class,option]
<= price[1,class,option] * demand[1,i,class,option] *
(1 - P1[class,option]);
/* R2[i,j,class,option] =
price[2,class,option] * P2[i,class,option] * S2[i,j,class,option] */
con R2_con_a {<i,j> in SCENARIOS2, class in CLASSES, option in OPTIONS}:
R2[i,j,class,option] <= price[2,class,option] * S2[i,j,class,option];
con R2_con_b {<i,j> in SCENARIOS2, class in CLASSES, option in OPTIONS}:
price[2,class,option] * S2[i,j,class,option] - R2[i,j,class,option]
<= price[2,class,option] * demand[2,j,class,option] *
(1 - P2[i,class,option]);
/* R3[i,j,k,class,option] =
price[3,class,option] * P3[i,j,class,option] * S3[i,j,k,class,option] */
con R3_con_a {<i,j,k> in SCENARIOS3, class in CLASSES, option in OPTIONS}:
R3[i,j,k,class,option] <= price[3,class,option] * S3[i,j,k,class,option];
con R3_con_b {<i,j,k> in SCENARIOS3, class in CLASSES, option in OPTIONS}:
price[3,class,option] * S3[i,j,k,class,option] - R3[i,j,k,class,option]
<= price[3,class,option] * demand[3,k,class,option] *
(1 - P3[i,j,class,option]);
max ExpectedYield =
(if current_period <= 1
then sum {i in SCENARIOS, class in CLASSES, option in OPTIONS}
prob[i] * R1[i,class,option])
+ (if current_period <= 2
then sum {<i,j> in SCENARIOS2, class in CLASSES, option in OPTIONS}
prob[i] * prob[j] * R2[i,j,class,option])
+ (if current_period <= 3
then sum {<i,j,k> in SCENARIOS3, class in CLASSES, option in OPTIONS}
prob[i] * prob[j] * prob[k] * R3[i,j,k,class,option])
+ sum {period in 1..current_period-1, class in CLASSES}
actual_revenue[period,class]
- &plane_cost * NumPlanes;
num price_sol_1 {class in CLASSES} =
sum {option in OPTIONS} price[1,class,option] * P1[class,option].sol;
num price_sol_2 {class in CLASSES, i in SCENARIOS} =
sum {option in OPTIONS} price[2,class,option] * P2[i,class,option].sol;
num price_sol_3 {class in CLASSES, <i,j> in SCENARIOS2} =
sum {option in OPTIONS} price[3,class,option] * P3[i,j,class,option].sol;
num remaining_seats {class in CLASSES} =
num_seats[class] * NumPlanes.sol
- sum {period in 1..current_period-1} actual_sales[period,class];
num sell_up_to_1 {class in CLASSES} =
min(
max {i in SCENARIOS, option in OPTIONS} S1[i,class,option].sol,
remaining_seats[class]);
num sell_up_to_2 {class in CLASSES} =
min(
max {<i,j> in SCENARIOS2, option in OPTIONS} S2[i,j,class,option].sol,
remaining_seats[class]);
num sell_up_to_3 {class in CLASSES} =
min(
max {<i,j,k> in SCENARIOS3, option in OPTIONS}
S3[i,j,k,class,option].sol, remaining_seats[class]);
current_period = 1;
solve;
for {i in SCENARIOS, class in CLASSES, option in OPTIONS}
S1[i,class,option] = round(S1[i,class,option].sol);
print price_sol_1;
print sell_up_to_1;
print {i in SCENARIOS, class in CLASSES, option in OPTIONS:
S1[i,class,option].sol > 0} S1;
print price_sol_2;
print price_sol_3;
print NumPlanes ExpectedYield;
for {class in CLASSES, option in OPTIONS} do;
if P1[class,option].sol > 0.5 then do;
fix P1[class,option] = 1;
actual_price[1,class] = price_sol_1[class];
actual_sales[1,class] =
min(sell_up_to_1[class], actual_demand[1,class,option]);
for {i in SCENARIOS} fix S1[i,class,option] = actual_sales[1,class];
end;
else fix P1[class,option] = 0;
end;
for {class in CLASSES}
actual_revenue[1,class] = actual_price[1,class] * actual_sales[1,class];
print actual_price actual_sales actual_revenue;
drop P1_con S1_con R1_con_a R1_con_b;
current_period = 2;
solve;
for {<i,j> in SCENARIOS2, class in CLASSES, option in OPTIONS}
S2[i,j,class,option] = round(S2[i,j,class,option].sol);
print price_sol_2;
print sell_up_to_2;
print {<i,j> in SCENARIOS2, class in CLASSES, option in OPTIONS:
i = 1 and S2[1,j,class,option].sol > 0} S2;
print price_sol_3;
print NumPlanes ExpectedYield;
for {i in SCENARIOS, class in CLASSES, option in OPTIONS} do;
if P2[i,class,option].sol > 0.5 then do;
fix P2[i,class,option] = 1;
actual_price[2,class] = price_sol_2[class,i];
actual_sales[2,class] =
min(sell_up_to_2[class], actual_demand[2,class,option]);
for {j in SCENARIOS} fix S2[i,j,class,option] = actual_sales[2,class];
end;
else fix P2[i,class,option] = 0;
end;
for {class in CLASSES}
actual_revenue[2,class] = actual_price[2,class] * actual_sales[2,class];
print actual_price actual_sales actual_revenue;
current_period = 3;
drop P2_con S2_con R2_con_a R2_con_b;
solve;
for {<i,j,k> in SCENARIOS3, class in CLASSES, option in OPTIONS}
S3[i,j,k,class,option] = round(S3[i,j,k,class,option].sol);
print price_sol_3;
print sell_up_to_3;
print {<i,j,k> in SCENARIOS3, class in CLASSES, option in OPTIONS:
<i,j> in {<1,1>} and S3[i,j,k,class,option].sol > 0} S3;
print NumPlanes ExpectedYield;
for {<i,j> in SCENARIOS2, class in CLASSES, option in OPTIONS} do;
if P3[i,j,class,option].sol > 0.5 then do;
fix P3[i,j,class,option] = 1;
actual_price[3,class] = price_sol_3[class,i,j];
actual_sales[3,class] =
min(sell_up_to_3[class], actual_demand[3,class,option]);
for {k in SCENARIOS} fix S3[i,j,k,class,option]
= actual_sales[3,class];
end;
else fix P3[i,j,class,option] = 0;
end;
for {class in CLASSES}
actual_revenue[3,class] = actual_price[3,class] * actual_sales[3,class];
print actual_price actual_sales actual_revenue;
current_period = 4;
print ExpectedYield;
quit;
proc optmodel;
set PERIODS = 1..&num_periods;
set <str> CLASSES;
num num_seats {CLASSES};
read data class_data into CLASSES=[class] num_seats;
set OPTIONS = 1..&num_options;
num price {PERIODS, CLASSES, OPTIONS};
read data price_data into [period class]
{option in OPTIONS} <price[period,class,option]=col('price'||option)>;
set SCENARIOS;
num prob {SCENARIOS};
read data scenario_data into SCENARIOS=[_N_] prob;
num demand {PERIODS, SCENARIOS, CLASSES, OPTIONS};
read data demand_data into [period scenario class]
{option in OPTIONS}
<demand[period,scenario,class,option]=col('demand'||option)>;
num actual_demand {PERIODS, CLASSES, OPTIONS};
read data actual_demand_data into [period class]
{option in OPTIONS}
<actual_demand[period,class,option]=col('demand'||option)>;
num actual_price {PERIODS, CLASSES};
num actual_sales {PERIODS, CLASSES};
num actual_revenue {PERIODS, CLASSES};
num expected_demand {period in PERIODS, class in CLASSES, option in OPTIONS}
= sum {scenario in SCENARIOS}
prob[scenario] * demand[period,scenario,class,option];
var P {PERIODS, CLASSES, OPTIONS} binary;
var S {PERIODS, CLASSES, OPTIONS} >= 0;
var R {PERIODS, CLASSES, OPTIONS} >= 0;
var TransferFrom {CLASSES} >= 0;
var TransferTo {CLASSES} >= 0;
var NumPlanes >= 0 <= &num_planes integer;
con NumPlanes_con {class in CLASSES}:
sum {period in PERIODS, option in OPTIONS} S[period,class,option]
+ TransferFrom[class] - TransferTo[class]
<= num_seats[class] * NumPlanes;
for {class in CLASSES} do;
TransferFrom[class].ub = &transfer_fraction_ub * num_seats[class];
TransferTo[class].ub = &transfer_fraction_ub * num_seats[class];
end;
con Balance_con:
sum {class in CLASSES} TransferFrom[class]
= sum {class in CLASSES} TransferTo[class];
con P_con {period in PERIODS, class in CLASSES}:
sum {option in OPTIONS} P[period,class,option] = 1;
con S_con {period in PERIODS, class in CLASSES, option in OPTIONS}:
S[period,class,option]
<= expected_demand[period,class,option] * P[period,class,option];
/* R[period,class,option] =
price[period,class,option] * P[period,class,option] *
S[period,class,option] */
con R_con_a {period in PERIODS, class in CLASSES, option in OPTIONS}:
R[period,class,option] <= price[period,class,option] *
S[period,class,option];
con R_con_b {period in PERIODS, class in CLASSES, option in OPTIONS}:
price[period,class,option] * S[period,class,option] -
R[period,class,option]
<= price[period,class,option] * expected_demand[period,class,option]
* (1 - P[period,class,option]);
max Yield =
sum {period in PERIODS, class in CLASSES, option in OPTIONS}
R[period,class,option]
- &plane_cost * NumPlanes;
num price_sol {period in PERIODS, class in CLASSES} =
sum {option in OPTIONS} price[period,class,option] *
P[period,class,option].sol;
solve;
for {period in PERIODS, class in CLASSES, option in OPTIONS}
S[period,class,option] = round(S[period,class,option].sol);
print price_sol;
print {period in PERIODS, class in CLASSES, option in OPTIONS:
S[period,class,option].sol > 0} S;
print NumPlanes Yield;
for {period in PERIODS, class in CLASSES, option in OPTIONS} do;
if P[period,class,option].sol > 0.5 then do;
actual_price[period,class] = price_sol[period,class];
actual_sales[period,class] =
min(S[period,class,option], actual_demand[period,class,option]);
actual_revenue[period,class] =
actual_price[period,class] * actual_sales[period,class];
R[period,class,option] = actual_revenue[period,class];
end;
end;
print actual_price actual_sales actual_revenue;
print Yield;
drop S_con R_con_b;
con S_con_actual {period in PERIODS, class in CLASSES, option in OPTIONS}:
S[period,class,option]
<= actual_demand[period,class,option] * P[period,class,option];
con R_con_b_actual {period in PERIODS, class in CLASSES, option in OPTIONS}:
price[period,class,option] * S[period,class,option] -
R[period,class,option]
<= price[period,class,option] * actual_demand[period,class,option]
* (1 - P[period,class,option]);
solve;
for {period in PERIODS, class in CLASSES, option in OPTIONS}
S[period,class,option] = round(S[period,class,option].sol);
print price_sol;
print {period in PERIODS, class in CLASSES, option in OPTIONS:
S[period,class,option].sol > 0} S;
print NumPlanes Yield;
for {period in PERIODS, class in CLASSES, option in OPTIONS} do;
if P[period,class,option].sol > 0.5 then do;
actual_price[period,class] = price_sol[period,class];
actual_sales[period,class] = S[period,class,option];
actual_revenue[period,class] =
actual_price[period,class] * actual_sales[period,class];
R[period,class,option] = actual_revenue[period,class];
end;
end;
print actual_price actual_sales actual_revenue;
quit;