Lindo Systems

! The inventory pre-positioning problem in LINGO.    (PrePosn.lng)
Facing random demand at 1 or more demand points,
we have 1 or more inventory sites at which we 
can place inventory (or think of it as capacity) in advance,
say before the big Christmas selling season.
Only after we have made our inventory position decisions
do we see the demands. There is
  a cost C0(i) for installing each unit of inventory at i,
  a cost C(i,j) of satisfying a unit of demand at j from i. 
How much inventory should we position where?  - and
when demand occurs, how much should we transfer from where to where?;
! Keywords: Decisionmaking under uncertainty, Inventory pre-positioning, Stochastic programming,
   Postponement, Sport Obermeyer;
SETS:
 INVPT: X0, C0, CAP;  ! Supply/inventory points;
 DEMPT: V, MU, SD;    ! Destination/demand points;
 IXD( INVPT, DEMPT): C; ! Combinations of the two;
 SCENE: PROFIT;       ! Set of scenarios;
 SXD( SCENE, DEMPT): DEM, U; ! Combinations of scenario x demand points;
 SXIXD( SCENE, IXD): X; ! Combinations of scenario, inv pt, demand pt;
ENDSETS
! Parameters:
    C0(i) = cost per unit of pre-positioning a unit
            of inventory at inventory site i,
    CAP(i) = max number of units that can be
             pre-positioned at i,
    C(i,j) = cost per unit using a unit at i to
             satisfy a unit of demand at j,
    V(j) = revenue per unit sold at demand point j,
    MU(j) = mean demand at j,
    SD(j) = standard deviation in demand at j,
    ;
! Decision variables:
   X0( i) = amount of inventory positioned in advance at i,
   X( s, i, j) = under scenario s, amount of goods
                 from i used to satisfy demand at j;

DATA:
! These data model the case of apparel manufacturer Sport Obermeyer.
   Each of the first 10 inventory points
  correspond to producing a particular product in advance.
  The last inventory point, 11, corresponds to
  a "quick response" ability to produce, at a slightly higher cost,
  a modest amount of any product "at the last minute" 
  after all demands have been observed
Ref:  Fisher and Raman.  ;

! The supply sources for the 10 different garments. The last source,
 SFLEX is a quick response flexible source;
  INVPT= SANIT SSTEP SDAPH STERI SISIS SSEDU SELEC SASSA SENTI  SGAIL SFLEX;
 ! The capacities. No limits, except for last, the Flex source;
   CAP = 99999 99999 99999 99999 99999 99999 99999 99999 99999  99999  8000;
 ! The cost/unit of each of the products produced at the beginning;
    C0 =   53    63    68    61     51     42     87      46      39     56    0; 
! The demand points, (the different garment types) ...;
 DEMPT = ANITA STEPH DAPHNE TERI  ISIS SEDUCE ELECTRA ASSAULT ENTICE  GAIL;
! Selling price/unit for each product;
     V =    93  133   148   123     99    73     173      90      80    110;
! Here are the supply->demand combinations possible and their tramsfer cost. 
 E.g. If we already produced Anita product, 
  it can be used to satisfy Anita demand at 0 additional cost, etc.
 If there is some substitutability between products, that can be
 represented by adding additional IXD pairs;
      IXD,      C =
  SANIT ANITA   0 
  SSTEP STEPH   0
  SDAPH DAPHNE  0
  STERI TERI    0
  SISIS ISIS    0
  SSEDU SEDUCE  0 
  SELEC ELECTRA 0
  SASSA ASSAULT 0
  SENTI ENTICE  0
  SGAIL GAIL    0
! If we use the Flex source, it costs extra money to satify demand;
  SFLEX ANITA   65  SFLEX STEPH   75  SFLEX DAPHNE 88 
  SFLEX TERI    72  SFLEX ISIS    63  SFLEX SEDUCE 43 
  SFLEX ELECTRA 95  SFLEX ASSAULT 56  SFLEX ENTICE 48  SFLEX GAIL 66
  ;  

 ! Mean and standard deviation in demand for each product;
     MU = 3296  1113  2383  1100  1042   4017   2150    2525   1358   1017;
     SD = 2094  1048  1394   762   646   1113    807     680    496    388;

! Here we get ready to generate the random demand;
! The accuracy of this model can be improved at the expense of increasing the
  size by increasing the number of scenarios;
!  SCENE = 1..512;
  SCENE = 1..256;
  U = @QRAND(342);  ! Generate matrix of uniforms, using an arbitrary seed;
ENDDATA 
   
SUBMODEL PRE_POS:
! Maximize expected profits. All scenarios considered equally likely;
   MAX = EPROFIT;
    EPROFIT = @SUM( SCENE( s): PROFIT( s))/ NS;
    @FREE( EPROFIT); ! In case EPROFIT could be < 0;

! Cannot exceed capacity at an inventory site;
     @FOR( INVPT(i):
         X0( i) <= CAP( i);
         );

  @FOR( SCENE( s):  ! Compute profit under each scenario;
     @FREE( PROFIT(s)); ! In case PROFIT( s) < 0;
    [CPROF] PROFIT( s) =  @SUM( IXD( i, j): ( V( j)- C( i, j))* X( s, i, j))
                        - @SUM( INVPT( i): C0( i)* X0( i));

! Cannot ship out from i more than we made available;
   @FOR(INVPT(i):
   [CSUP] @SUM( IXD( i, j): X( s, i, j)) <= X0(i);
            );
! Cannot sell more than was demanded in this scenario;
   @FOR(DEMPT(j):
     [CDEM] @SUM( IXD( i, j): X( s, i, j)) <= DEM( s, j);
          );
       );
 ENDSUBMODEL

 CALC:
   @SET( 'TERSEO', 1);   ! Output level (0:verb, 1:terse, 2:only errors, 3:none);
   NS = @SIZE(SCENE); ! Count number scenarios user requested;
! Generate the Normal demands (using @NORMSINV) ,
   round (using @FLOOR), and change < 0 to 0 (using @SMAX); 
   @FOR( SXD(s,j):
      DEM(s,j) = @SMAX(0, @FLOOR( 0.5 + MU(j) + SD( J)* @NORMSINV( U( s, j))));
       );
  
   @SET( 'SOLVEL', 3);  ! Linear solver (0:LINGO decides, 1:primal, 2:dual, 3:barrier);
   @SOLVE( PRE_POS);

! Write a little report;
   COSTINIT = @SUM( INVPT( i): C0(i)* X0( i));
   @WRITE( @FORMAT( COSTINIT, '12.2f'),' = Initial Expenditure', @NEWLINE( 1));
   @WRITE( @FORMAT( EPROFIT, '12.2f'),' = Expected Final Net Profit', @NEWLINE( 1));
   @WRITE( @NEWLINE( 1));
   @WRITE( '  Initial Production Decisions', @NEWLINE( 1));
   @WRITE(' SupplyPoint   Initial_Inventory', @newline( 1));
   @FOR( INVPT( i):
    @WRITE( @FORMAT( INVPT( i),'9s'),' ', @FORMAT( X0( i), '14.1f'), @newline( 1));
      );

   @CHARTHISTO( 'Profit Histogram', 'Profit', 'Frequency', 'OutcomesInBin',
         7, ! Number bins;
         PROFIT);
 ENDCALC