! Compute critical paths, earliest and latest start times (CritPath.lng);
! Keywords: CPM, Critical path, PERT, Project Management, Scheduling, Slack;
SETS:
TASK: PTIME, ES, LS
, WORK1, WORK2 ;
! The precedence relations, the first task in the
precedence relationship needs to be completed before the
second task can be started;
PRED( TASK, TASK);
ENDSETS DATA:
! Case: Prepare a marketing plan for a product;
! The precedence diagram is:
! /FCAST\---SCHED----COSTOUT\
! / \ \
! FIRST \ \
! \ \ \
! \SURVEY-PRICE-----------FINAL;
! The activities, or tasks;
!CaseMP TASK= FIRST, FCAST, SURVEY, PRICE, SCHED, COSTOUT, FINAL;
! and their processing times;
!CaseMP PTIME= 0, 14, 3, 3, 7, 4, 10;
! Here are the predecessor -> successor pairs;
!CaseMP PRED= FIRST, FCAST, FIRST, SURVEY,
FCAST, PRICE, FCAST, SCHED,
SURVEY, PRICE,
SCHED, COSTOUT,
PRICE, FINAL,
COSTOUT, FINAL;
! Case Build a Home: Dig basement, Pour Foundation, Pour basement floor,
Install floor joists, Wall studding, Rough out interior, Roof, Landscaping;
! The activities, or tasks and their processing times;
!CaseBH; TASK PTIME =
DIG 3
FOUND 4
POURB 2
JOISTS 3
WALLS 5
RAFTERS 3
FLOOR 4
ROUGH 6
ROOF 7
FINISH 5
SCAPE 2 ;
! Here are the predecessor -> successor pairs;
!CaseBH; PRED =
DIG FOUND
FOUND POURB
FOUND JOISTS
FOUND WALLS
WALLS RAFTERS
POURB RAFTERS
JOISTS FLOOR
FLOOR ROUGH
RAFTERS ROOF
ROUGH FINISH
ROOF FINISH
POURB SCAPE
WALLS SCAPE
;
ENDDATA
PROCEDURE ErlStrtGet:
! Compute Earliest start times for each TASK.
Inputs:
PTIME( j) = processing time for task j,
PRED( i, j) = predecessor->successor pairs,
ES( j) = initial lower bound on earliest start of task j,
Outputs:
ErlStrt = 0 if successful,
1 if the PRED graph has cycles
ES( j) = earliest start time for j, taking into account PRED;
! Count number of predecessors, WORK1( j) of each task j;
@FOR( TASK( j) : WORK1( j) = 0);
@FOR( PRED( j, k): WORK1( k) = WORK1( k) +1);
! Put all tasks with 0 preds on the WORK2 stack;
stacknum = 0;
@FOR( TASK( j) | WORK1( j) #EQ# 0:
stacknum = stacknum + 1;
WORK2( stacknum ) = j;
);
! While there is a task j on the stack;
@WHILE( stacknum #GT# 0:
! Remove task j from stack. We now know its ES;
jnow = WORK2( stacknum);
stacknum = stacknum - 1;
@FOR( PRED( j, k) | j #EQ# jnow:
ES( k) = @SMAX( ES( k), ES( j) + PTIME( j)); ! Update ES( k) for all successors k;
WORK1( k) = WORK1( k) - 1; ! decrement number unprocessed predecesors;
@IFC( WORK1( k) #EQ# 0: ! Is k now ready for the ready stack?;
stacknum = stacknum + 1; ! Yes, put on stack;
WORK2( stacknum) = k;
);
);
);
ENDPROCEDURE
PROCEDURE LstStrtGet:
! Compute Latest start times for each TASK.
Inputs:
PTIME( j) = processing time for task j,
PRED( i, j) = predecessor successor pairs,
LS( j) = initial upper bound on latest start of task j,
Outputs:
LstStrt = 0 if successful,
1 if the PRED graph has cycles
LS( j) = earliest start time for j, taking into account PRED;
! Count number of successors, WORK1( j) of each task j;
@FOR( TASK( j) : WORK1( j) = 0);
@FOR( PRED( j, k): WORK1( j) = WORK1( j) +1);
! Put all tasks with 0 successors on the WORK2 stack;
stacknum = 0;
@FOR( TASK( k) | WORK1( k) #EQ# 0:
stacknum = stacknum + 1;
WORK2( stacknum ) = k;
);
! While there is a task k on the stack;
@WHILE( stacknum #GT# 0:
! Remove task k from stack, we now know its LS;
know = WORK2( stacknum);
stacknum = stacknum - 1;
@FOR( PRED( j, k) | k #EQ# know:
LS( j) = @SMIN( LS( j), LS( k) - PTIME( j)); ! Update LS( j) for all predecessors j;
WORK1( j) = WORK1( j) - 1; ! decrement number unprocessed successors;
@IFC( WORK1( j) #EQ# 0: ! Is j now ready for the ready stack?;
stacknum = stacknum + 1;
WORK2( stacknum) = j;
);
);
);
ENDPROCEDURE
CALC:
@SET( 'OROUTE',1); ! Buffer size for routing output to window;
@SET( 'TERSEO',1); ! Output level (0:verb, 1:terse, 2:only errors, 3:none);
@SET( 'WNLINE',10000);! Max command window lines saved (Windows only);
! Begin: critical path, slack analysis;
@FOR( TASK( j): ES( j) = 0); ! Initially, assume every task is available at time 0;
ErlStrtGet; ! Get earliest start times;
! Compute latest starts;
! Earliest finish for project;
PROJLEN = @MAX( TASK( j): ES( j) + PTIME( j));
@FOR( TASK( j): LS( j) = PROJLEN - PTIME( j)); ! Initial latest finish for all tasks;
LstStrtGet; ! Get latest start times;
! Compute slack for each task;
@FOR( TASK( j): WORK1( j) = LS( j) - ES( j));
! Write a simple report;
! Put sort order in WORK2( j), sorted 1st on slack, 2nd on early start;
WORK2 = @SORT( WORK1, ES);
@WRITE(' Project length= ', PROJLEN, @NEWLINE( 1));
@WRITE(' Task Slack EarlyStart LateStart PTime', @NEWLINE( 1));
@FOR( TASK( j): ! Display sorted list;
jnow = WORK2( j);
xrow = j + 2;
@WRITE( @FORMAT( TASK( jnow),'10s'), ' ', @FORMAT( WORK1( jnow) ,'6.1f') ,
' ', @FORMAT( ES( jnow),'6.1f'), ' ', @FORMAT( LS( jnow),'6.1f'),
' ', @FORMAT( PTIME( jnow),'6.1f'), @NEWLINE( 1));
);
! End: critical path, slack analysis;
ENDCALC
|