! 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