! Enumerate all feasible nonempty subsets of a partially ordered set.
 A subset is feasible if for every task j in the subset,
 all predecessors of j are also in the subset;
! This implements the algorithm on page 445 of:
  Schrage, L. and K. Baker(1978),"Dynamic Programming Solution 
 of Sequencing Problems with Precedence Constraints", Operations Research,
 vol. 26, no. 3, pp. 444-449;
! Keywords: Dynamic programming, Enumeration, Partial ordered set, Subset enumeration;
SETS:
 TASK: PREDEND, TASKRDR, M, NSUCC;
 PLIST: PREDLIST;
ENDSETS
DATA: ! Case 1; ! The network: 1 ---> 2 --> 3 -------> 4 \ \ \ / / \ \ / \ / \ 5 6 --> 7 \ \ / \ \ / 8 ---> 9; TASK = 1..9; ! Set of Tasks; PLIST = 1..12; ! Set/list of prededessors; ! The list of predecessors for each task; PREDLIST = 1 ! 2; 2, 5 ! 3; 3, 7 ! 4; 1 ! 5; 2 ! 6; 6, 9 ! 7; 1 ! 8; 5, 8;! 9; ! Pointers into PREDLIST of end point of predecessors for each task; PREDEND = 0 ! 1; 1 ! 2; 3 ! 3; 5 ! 4; 6 ! 5; 7 ! 6; 9 ! 7; 10 ! 8; 12;! 9; ! Case 2; ! A smaller set with no precedence constraints; ! TASK = 1..4; ! PLIST = 1..1; ! PREDEND = 0 0 0 0; ENDDATA PROCEDURE ADJNSUCC: ! Adjust number of successors count of predecessors of task kk, increasing NSUCC(), (case DELTA = 1) if task kk is added, or decreasing NSUCC(), (case DELTA = -1) if task kk is removed from the current set; LSTPRD = PREDEND(kk); jj = 0; @IFC( kk #GT# 1: jj = PREDEND(kk-1)); @WHILE( jj #LT# LSTPRD: jj = jj + 1; jp = PREDLIST( jj); NSUCC( jp) = NSUCC( jp) + DELTA; ); ENDPROCEDURE CALC: NTASK = @SIZE( TASK); ! Number of tasks; ! Construct an ordering, TASKRDR() of the tasks so that for any j, all its predecessors appear earlier; @FOR( TASK( j): NSUCC(j) = 0; ! No task has any successors in the set; ); DELTA = 1; ! Count number of successors of each task; @FOR( TASK( j): kk = j; ADJNSUCC; ); ! Find all tasks with no successors, use M(i) as a stack; NUMZ = 0; @FOR( TASK( j): @IFC( NSUCC(j) #EQ# 0: NUMZ = NUMZ + 1; M( NUMZ) = j; ); ); ! Now fill TASKRDR() from the top down; @WRITE(' A sequence in which all predecessors of j appear earlier than j.',@NEWLINE(1)); NXTPOS = NTASK; @FOR( TASK( j): kk = M( NUMZ); ! Take a task off stack; NUMZ = NUMZ - 1; ! One less task on stack; TASKRDR( NXTPOS) = kk; ! Put kk next from rear; @WRITE(' TASKRDR(',NXTPOS,')= ', kk, @NEWLINE(1)); NXTPOS = NXTPOS - 1; ! Decrement successor counts of predecessors of kk; LSTPRD = PREDEND(kk); jj = 0; @IFC( kk #GT# 1: jj = PREDEND(kk-1)); @WHILE( jj #LT# LSTPRD: jj = jj + 1; jp = PREDLIST( jj); NSUCC( jp) = NSUCC( jp) -1; @IFC( NSUCC(jp) #EQ# 0: ! If successor count hits 0...; NUMZ = NUMZ + 1; M( NUMZ) = jp; ! put it on the stack; ); ); ); ! Now almost ready to enumerate feasible subsets; @FOR( TASK( j): M(j) = 0; ! No tasks are members, we start with empty set; NSUCC(j) = 0; ! No task has any successors in the set; ); ! Loop to enumerate all feasible subsets satisfying precedence constraints; @WRITE(' A listing of all feasible subsets.',@NEWLINE(1)); NSUBSETS = 0; ! Number subsets so far; LOZ = 0; ! Lower bound -1 on location of first M(i) = 0; MORE = 1; ! MORE = 0 when we have enumerated all subsets; @WHILE( MORE: ! Find smallest i, such that M(i) = 0; ZFRST = 0; ! Location of first M(i) = 0; ii = LOZ; @WHILE( ZFRST #EQ# 0 #AND# ii #LT# NTASK: ii = ii + 1; @IFC( M( TASKRDR( ii)) #EQ# 0: ZFRST = ii; ); ); ! If ZFRST = 0, it means all the M(i) = 1; @IFC( ZFRST #EQ# 0: MORE = 0; ! We found no lowest task with M(i) = 0; @ELSE kk = TASKRDR( ZFRST); ! We found a lowest M(i) = 0; M( kk) = 1; ! Make it a member of current set; LOZ = ZFRST; ! Adjust lower bound -1 of lowest M(i) = 0; DELTA = 1; ! Increment ...; ADJNSUCC; ! successor counts of predecessors of kk; ! Drop any j lower than ZFRST if it has no successors k with M(k) = 1; DELTA = -1; ii = ZFRST; @WHILE( ii #GT# 1: ii = ii - 1; kk = TASKRDR( ii); @IFC( NSUCC( kk) #EQ# 0: M(kk) = 0; ! Drop kk from the set; LOZ = ii-1; ! Adjust lower bound-1 of location of first M(i) = 0; ADJNSUCC; ! Decrement successor counts of predecessor of kk; ); ); NSUBSETS = NSUBSETS + 1; @WRITE(' Current set(', NSUBSETS,')= '); @FOR( TASK(i) | M( TASKRDR(i)) #EQ# 1: @WRITE( ' ', TASKRDR(i)); ); @WRITE( @NEWLINE(1)); ); ); ! @WHILE( MORE ; ENDCALC