Lindo Systems

! Mark down pricing, Two period, deterministic case;
! A single seller wants to set prices for a single product
in two periods. 
This is a full knowledge case.  The potential buyers know what 
the prices will be in each of the two periods.
Each buyer will buy the product in the period which gives the
greater consumer surplus, i.e. reservation price - purchase price.
Typically, a buyer will be willing to pay more, 
perhaps substantially, in the first period. E.g., the buyer is
willing to pay more for a ski parka at the beginning of ski season.
 If you are an economist, you can also think of this as a Stackelberg game in which the
seller is the leader who makes the first decisions: the prices,
and then the buyers optimize for themselves, given the prices.
! Parameters:
   Ri(c)= reservation price of customer group c for period i,
   NP(c)= number of people in customer group c,
   COST = vendor's cost per unit of the product.
  Decision variables:
   Pi   = price set for period i, (Seller decision)
   Yi(c)= 1 if customer group c buys in period i.(Buyer decision)
;
! Keywords: Mark Down, Revenue management, Pricing, Marketing,
  Stackelberg game;
MODEL:
SETS:
 CUSTG: R1, R2, NP, Y1, Y2, 
   P1Y1, P2Y2, P1Y2, P2Y1;
ENDSETS
DATA:
! Reservation prices for the customer groups in each period;
 CUSTG= CG1  CG2  CG3  CG4;
 R1 =   220  210  180  175; ! Period 1;
 R2 =   150  160  175  150; ! Period 2;
! Number of people in each group;
 NP =    50   90  110   35;
! Cost per unit to the vendor;
 COST = 60;
ENDDATA

SUBMODEL FINDPRICES:
  PMX1 = @MAX( CUSTG(c): R1(c));
  PMX2 = @MAX( CUSTG(c): R2(c));

@FOR( CUSTG(c):
! Linearize PiYj(c) = Pi*Yj(c);
  P1Y1(c) <= P1;
  P1Y1(c) <= PMX1*Y1(c);
  P1Y1(c) >= P1 - PMX1*(1-Y1(c));
  P2Y2(c) <= P2;
  P2Y2(c) <= PMX2*Y2(c);
  P2Y2(c) >= P2 - PMX2*(1-Y2(c));

  P2Y1(c) <= P2;
  P2Y1(c) <= PMX2*Y1(c);
  P2Y1(c) >= P2 - PMX2*(1-Y1(c));

  P1Y2(c) <= P1;
  P1Y2(c) <= PMX1*Y2(c);
  P1Y2(c) >= P1 - PMX1*(1-Y2(c));

! Customer will buy at most once;
   Y1(c) + Y2(c) <= 1;
   @BIN(Y1(c)); @BIN(Y2(c));

! If a customer buys in period i, then the 
 consumer surplus, Ri(c) - Pi >= 0, i.e.
 customer will not buy if price is above reservation price,
 (Ri(c) - Pi)*Yi(c) >= 0;
  R1(c)*Y1(c) - P1Y1(c) >= 0;
  R2(c)*Y2(c) - P2Y2(c) >= 0;
 
! If a customer buys in period i, then the 
 consumer surplus, Ri(c) - Pi must be greater
 than in the other period, e.g.,
  (R1(c) - P1)*Y1(c) >= (R2(c)- P2)*Y1(c) >=0;
! Buys in 1 rather than 2;
  R1(c)*Y1(c) - P1Y1(c) >= R2(c)*Y1(c) - P2Y1(c);
! Buys in 2 rather than 1;
  R2(c)*Y2(c) - P2Y2(c) >= R1(c)*Y2(c) - P1Y2(c);
    );

! Vendor wants to maximize revenue minus cost;
 MAX = TOTREV - TOTCOST;
  TOTREV = @SUM( CUSTG(c): NP(c)*(P1Y1(c) + P2Y2(c)));
  TOTCOST= @SUM( CUSTG(c): COST*NP(c)*(Y1(c)+ Y2(c)));
 
ENDSUBMODEL

CALC:
 @SET('TERSEO',1); ! Set output level to terse;

 @SOLVE( FINDPRICES); ! Solve submodel;

! Display a little report;
 @WRITE( 'Two Period Pricing Problem.',@NEWLINE(1));
 @WRITE( ' Reservation price',@NEWLINE(1),'  by group.',@NEWLINE(1));
 @WRITE( '            Period 1   Period 2',@NEWLINE(1));
 @FOR( CUSTG(c):
   @WRITE( '    ',CUSTG(c), '    ',
     @FORMAT(R1(c),"8.2f"),'   ',@FORMAT(R2(c),"8.2f"),@NEWLINE(1));
    );
 @WRITE('  Cost/unit= ',@FORMAT(COST, "6.2f"),@NEWLINE(1));
 @WRITE(@NEWLINE(1),' Results:   Period 1   Period 2',@NEWLINE(1));
 @WRITE( '    Price:  ', @FORMAT(P1,"8.2f"),'   ',@FORMAT(P2,"8.2f"),@NEWLINE(1));

 @WRITE(' Units purchased:',@NEWLINE(1));
 @FOR( CUSTG(c):
   @WRITE( '    ',CUSTG(c), '    ',
     @FORMAT(NP(c)*Y1(c),"8.0f"),'   ',@FORMAT(NP(c)*Y2(c),"8.0f"),@NEWLINE(1));
    );
 @WRITE(@NEWLINE(1),' Total revenue=', @FORMAT(TOTREV,'9.2f'),@NEWLINE(1));
 @WRITE(' Total cost   =', @FORMAT(TOTCOST,'9.2f'),@NEWLINE(1));
 @WRITE(' Profit       =', @FORMAT(TOTREV-TOTCOST,'9.2f'),@NEWLINE(1));
ENDCALC
END