MODEL:
 ! Illustration of Multi-criteria/Lexico/Pre-emptive goal programming
 ! for a staffing problem with multiple work patterns available;
!   Step 1:
       - Minimize total cost
    Step 2:
       - Given total costs minimized from Step 1
       - Solve to minimize max staff overage
         on any given day, thereby spreading
         excess staff through the week
    Step 3:
       - Fix max overage to it's level from  
         Step 2
       - Solve to minimize the number working
         on a Sunday  ;

! Any Preemptive optimal solution is also Pareto optimal;
! Keywords:  Goal programming, Lexico-goal programming, Multi-criteria,
   Preemptimve criteria, Pareto optimal, Staff scheduling;

SETS:
   DAY :  NEED,  EXCESS;
   PTRN : DAYSON, HRPDAY, COST, PUB;
   PXD( PTRN, DAY): START;
   LOBJ: OBJ, WGT, SUB; ! The set of objectives;
 ENDSETS
DATA: PTRN = P5X8 P4X10; ! The names of the work patterns; DAYSON = 5 4; ! Number days on per week for each; HRPDAY = 8 10; ! Hours per day for each pattern type; PUB = 999 999; ! Upper bound on number that can be hired; COST = 400 400; ! Cost per week for each; ! Days of the week; DAY = MON, TUE, WED, THU, FRI, SAT, SUN; ! Hours needed each day; NEED = 1520, 1360, 1200, 1520, 1360, 1120, 1008; ! The Lexico objectives; LOBJ = Total_Cost Max_Excess Work_Sunday; ENDDATA SUBMODEL STAFFEM: ! Minimize the appropriate objective, based on WGT(); MIN = @SUM( LOBJ( p) : WGT( p) * OBJ( p)); ! Compute cost; TTL_COST = @SUM( PXD(p,d) : COST(p)*START( p,D) ); ! The constraints; ! We must satisfy the number hours needed each day. Hours provided = hours needed + excess; @FOR( DAY( D): @SUM( PTRN( p): @SUM( DAY( COUNT)| COUNT #LE# DAYSON(p): HRPDAY( p)*START( p, @WRAP( D - COUNT + 1, @SIZE( DAY))))) = NEED( D) + EXCESS( D) ; ); ! Cannot use more than allowed of each pattern; @FOR( PTRN(p): @SUM( PXD( p,d): START(p,d)) <= PUB( p); ); ! Computes the maximum staff excess; @FOR( DAY( D): MAX_EXCESS >= EXCESS( D)); ! Starts must be integral; @FOR( PXD(p,d): @GIN( START(p,d))); ! A redundant but helpful constraint, Total staff >= total need; @SUM( DAY( d): @SUM( PTRN( p): @SUM( DAY( COUNT)| COUNT #LE# DAYSON(p): HRPDAY( p)*START( p, @WRAP( D - COUNT + 1, @SIZE( DAY)))))) >= @SUM( DAY( d): NEED( D)) ; ! Objective related stuff; ! Total cost; ! Compute cost; TTL_COST = @SUM( PXD(p,d) : COST(p)*START( p,D) ); ! Excess maximum staff ; @FOR( DAY( D): MAX_EXCESS >= EXCESS( D)); ! The Lexico stuff. Define the objectives; OBJ( 1) = TTL_COST; OBJ( 2) = MAX_EXCESS; OBJ( 3) = EXCESS( @INDEX( DAY, SUN)); ! Their bounds, if any; @FOR( LOBJ( p): OBJ( p) <= SUB( p); ); ENDSUBMODEL
PROCEDURE REPORT: @WRITE(' '); @FOR( DAY( D): @WRITE( DAY( D), ' ')); @WRITE( 'TOTAL COST', @NEWLINE( 1)); @FOR( PTRN( p): @WRITE( ' Start ',@FORMAT( PTRN(p),"5s"),':'); @FOR( DAY( d): @WRITE( @FORMAT( START( p, d), '6.0f'))); @WRITE( @FORMAT( @SUM( DAY(d): COST( p)* START( p, d)), '8.0f')); @WRITE( @NEWLINE( 1)); ); @WRITE( 'Hrs provided:'); @FOR( DAY: @WRITE( @FORMAT( NEED + EXCESS, '6.0f'))); @WRITE( @NEWLINE( 1)); @WRITE( ' Required:'); @FOR( DAY: @WRITE( @FORMAT( NEED, '6.0f'))); @WRITE( @NEWLINE( 1)); @WRITE( ' Excess:'); @FOR( DAY: @WRITE( @FORMAT( EXCESS, '6.0f'))); @WRITE( @NEWLINE( 2)); ENDPROCEDURE CALC: !Run in quiet mode; @SET( 'TERSEO', 2); @WRITE('Illustrate preemptive objectives in staffing:', @NEWLINE(1), ' 1) Minimize cost, 2) Minimize max excess any day, 3) Minimize Sunday staffing.',@NEWLINE(2)); ! Initially no weights on any objective; @FOR( LOBJ( p): WGT( p) = 0); ! Loop over the objectives; @FOR( LOBJ( p): @IFC( p #GT# 1: WGT( p-1) = 0); WGT( p) = 1; ! Put all wgt on current obj; @SOLVE( STAFFEM); @WRITE( 'Minimize ', LOBJ( p), @NEWLINE( 1)); REPORT; ! Display standard report; SUB( p) = OBJ( p); !Fix current objective; ); ENDCALC END