Resource-Constrained Scheduling Problem

/***************************************************************/
/*                                                             */
/*          S A S   S A M P L E   L I B R A R Y                */
/*                                                             */
/*    NAME: clpe16                                             */
/*   TITLE: Resource-Constrained Scheduling Problem            */
/*          with Nonstandard Temporal Constraints (clpde16)    */
/* PRODUCT: OR                                                 */
/*  SYSTEM: ALL                                                */
/*    KEYS: OR                                                 */
/*   PROCS: CLP                                                */
/*    DATA:                                                    */
/*                                                             */
/* SUPPORT:                             UPDATE:                */
/*     REF:                                                    */
/*    MISC: Example 16 from the CLP Procedure chapter of the   */
/*          Constraint Programming book.                       */
/*                                                             */
/***************************************************************/

data bridge;
   format _ACTIVITY_ $3. _DESC_ $34. _RESOURCE_ $14.
          _SUCCESSOR_ $3. _LAG_ $3. ;
   input _ACTIVITY_ & _DESC_ & _DURATION_ _RESOURCE_ &
         _SUCCESSOR_ & _LAG_ & _LAGDUR_;
   _QTY_ = 1;
datalines;
a1   excavation (abutment 1)             4  excavator       s1   .     .
a2   excavation (pillar 1)               2  excavator       s2   .     .
a3   excavation (pillar 2)               2  excavator       p1   .     .
a4   excavation (pillar 3)               2  excavator       p2   .     .
a5   excavation (pillar 4)               2  excavator       s5   .     .
a6   excavation (abutment 2)             5  excavator       s6   .     .
ab1  concrete setting time (abutment 1)  1  .               m1   .     .
ab2  concrete setting time (pillar 1)    1  .               m2   .     .
ab3  concrete setting time (pillar 2)    1  .               m3   .     .
ab4  concrete setting time (pillar 3)    1  .               m4   .     .
ab5  concrete setting time (pillar 4)    1  .               m5   .     .
ab6  concrete setting time (abutment 2)  1  .               m6   .     .
b1   concrete foundation (abutment 1)    1  concrete mixer  ab1  .     .
b1   concrete foundation (abutment 1)    1  concrete mixer  s1   ff   -4
b2   concrete foundation (pillar 1)      1  concrete mixer  ab2  .     .
b2   concrete foundation (pillar 1)      1  concrete mixer  s2   ff   -4
b3   concrete foundation (pillar 2)      1  concrete mixer  ab3  .     .
b3   concrete foundation (pillar 2)      1  concrete mixer  s3   ff   -4
b4   concrete foundation (pillar 3)      1  concrete mixer  ab4  .     .
b4   concrete foundation (pillar 3)      1  concrete mixer  s4   ff   -4
b5   concrete foundation (pillar 4)      1  concrete mixer  ab5  .     .
b5   concrete foundation (pillar 4)      1  concrete mixer  s5   ff   -4
b6   concrete foundation (abutment 2)    1  concrete mixer  ab6  .     .
b6   concrete foundation (abutment 2)    1  concrete mixer  s6   ff   -4
l    delivery of the preformed bearers   2  crane           t1   .     .
l    delivery of the preformed bearers   2  crane           t2   .     .
l    delivery of the preformed bearers   2  crane           t3   .     .
l    delivery of the preformed bearers   2  crane           t4   .     .
l    delivery of the preformed bearers   2  crane           t5   .     .
m1   masonry work (abutment 1)          16  bricklaying     t1   .     .
m1   masonry work (abutment 1)          16  bricklaying     ua   fs   -2
m2   masonry work (pillar 1)             8  bricklaying     t1   .     .
m2   masonry work (pillar 1)             8  bricklaying     t2   .     .
m2   masonry work (pillar 1)             8  bricklaying     ua   fs   -2
m3   masonry work (pillar 2)             8  bricklaying     t2   .     .
m3   masonry work (pillar 2)             8  bricklaying     t3   .     .
m3   masonry work (pillar 2)             8  bricklaying     ua   fs   -2
m4   masonry work (pillar 3)             8  bricklaying     t3   .     .
m4   masonry work (pillar 3)             8  bricklaying     t4   .     .
m4   masonry work (pillar 3)             8  bricklaying     ua   fs   -2
m5   masonry work (pillar 4)             8  bricklaying     t4   .     .
m5   masonry work (pillar 4)             8  bricklaying     t5   .     .
m5   masonry work (pillar 4)             8  bricklaying     ua   fs   -2
m6   masonry work (abutment 2)          20  bricklaying     t5   .     .
m6   masonry work (abutment 2)          20  bricklaying     ua   fs   -2
p1   foundation piles 2                 20  pile driver     s3   .     .
p2   foundation piles 3                 13  pile driver     s4   .     .
pa   beginning of project                0  .               a1   .     .
pa   beginning of project                0  .               a2   .     .
pa   beginning of project                0  .               a3   .     .
pa   beginning of project                0  .               a4   .     .
pa   beginning of project                0  .               a5   .     .
pa   beginning of project                0  .               a6   .     .
pa   beginning of project                0  .               l    fse  30
pa   beginning of project                0  .               ue   .     .
pe   end of project                      0  .               .    .     .
s1   formwork (abutment 1)               8  carpentry       b1   .     .
s1   formwork (abutment 1)               8  carpentry       a1   sf   -3
s2   formwork (pillar 1)                 4  carpentry       b2   .     .
s2   formwork (pillar 1)                 4  carpentry       a2   sf   -3
s3   formwork (pillar 2)                 4  carpentry       b3   .     .
s3   formwork (pillar 2)                 4  carpentry       p1   sf   -3
s4   formwork (pillar 3)                 4  carpentry       b4   .     .
s4   formwork (pillar 3)                 4  carpentry       p2   sf   -3
s5   formwork (pillar 4)                 4  carpentry       b5   .     .
s5   formwork (pillar 4)                 4  carpentry       a5   sf   -3
s6   formwork (abutment 2)              10  carpentry       b6   .     .
s6   formwork (abutment 2)              10  carpentry       a6   sf   -3
t1   positioning (preformed bearer 1)   12  crane           v1   .     .
t2   positioning (preformed bearer 2)   12  crane           pe   .     .
t3   positioning (preformed bearer 3)   12  crane           pe   .     .
t4   positioning (preformed bearer 4)   12  crane           pe   .     .
t5   positioning (preformed bearer 5)   12  crane           v2   .     .
ua   removal of the temporary housing   10  .               pe   .     .
ue   erection of temporary housing      10  .               .    .     .
ue   erection of temporary housing      10  .               s1   ss    6
ue   erection of temporary housing      10  .               s2   ss    6
ue   erection of temporary housing      10  .               s3   ss    6
ue   erection of temporary housing      10  .               s4   ss    6
ue   erection of temporary housing      10  .               s5   ss    6
ue   erection of temporary housing      10  .               s6   ss    6
v1   filling 1                          15  caterpillar     pe   .     .
v2   filling 2                          10  caterpillar     pe   .     .
;


%macro resources;
   proc sql;
      create table bridge2 as
         select a.*, b._duration_ as bdur from
            bridge a left join bridge b
            on a._SUCCESSOR_ = b._ACTIVITY_;
      quit;

   proc sort data=bridge2 nodup;
      by _ACTIVITY_ _RESOURCE_ _SUCCESSOR_ _LAG_ _DURATION_ _LAGDUR_ _QTY_ bdur;
   run;
   proc sql noprint;
      select count(distinct(_RESOURCE_)) into :rescnt from bridge
         where _RESOURCE_ is not null;
      select distinct(_RESOURCE_) into :res_1-:res_%eval(&rescnt.)
         from bridge where _RESOURCE_ is not null;
      create table resources as
         select distinct _ACTIVITY_, _RESOURCE_, _DURATION_ from bridge;
   quit;
   %do i = 1 %to &rescnt.;
      proc sql noprint;
         select count(*)
            into :actcnt_%eval(&i.)
            from resources where _RESOURCE_ eq "&&res_&i..";
         select _ACTIVITY_
            into :act_%eval(&i.)_1-:act_%eval(&i.)_%eval(&&actcnt_&i..)
            from resources where _RESOURCE_ eq "&&res_&i..";
         select _DURATION_
            into :dur_%eval(&i.)_1-:dur_%eval(&i.)_%eval(&&actcnt_&i..)
            from resources where _RESOURCE_ eq "&&res_&i..";
      quit;
   %end;
   %let makespan = 104; /* should be 104 with cumulative included */
   data _NULL_;
      set bridge2 end=lastobs;
      retain start 1;
      if start then do;
         start = 0;
         call execute('data condata; format _TYPE_ $3.;');
      end;
      if missing(_SUCCESSOR_) then do;
      end;
      else if _LAG_ eq '' then do;
         call execute('_TYPE_ = "GE";');
         call execute('_RHS_ = ' || _DURATION_ || ';');
         call execute(_SUCCESSOR_ || ' = 1;');
         call execute(_ACTIVITY_ || ' = -1;');
      end;
      else if _LAG_ eq 'ff' then do;
         call execute('_TYPE_ = "GE";');
         call execute('_RHS_ = ' || (_LAGDUR_ + _DURATION_ - bdur) || ';');
         call execute(_SUCCESSOR_ || ' = 1;');
         call execute(_ACTIVITY_ || ' = -1;');
      end;
      else if _LAG_ eq 'fs' then do;
         call execute('_TYPE_ = "GE";');
         call execute('_RHS_ = ' || (_LAGDUR_ + _DURATION_) || ';');
         call execute(_SUCCESSOR_ || ' = 1;');
         call execute(_ACTIVITY_ || ' = -1;');
      end;
      else if _LAG_ eq 'fse' then do;
         call execute('_TYPE_ = "EQ";');
         call execute('_RHS_ = ' || (_LAGDUR_ + _DURATION_) || ';');
         call execute(_SUCCESSOR_ || ' = 1;');
         call execute(_ACTIVITY_ || ' = -1;');
      end;
      else if _LAG_ eq 'sf' then do;
         call execute('_TYPE_ = "GE";');
         call execute('_RHS_ = ' || (_LAGDUR_ - bdur) || ';');
         call execute(_SUCCESSOR_ || ' = 1;');
         call execute(_ACTIVITY_ || ' = -1;');
      end;
      else if _LAG_ eq 'ss' then do;
         call execute('_TYPE_ = "GE";');
         call execute('_RHS_ = ' || _LAGDUR_ || ';');
         call execute(_SUCCESSOR_ || ' = 1;');
         call execute(_ACTIVITY_ || ' = -1;');
      end;
      if (_SUCCESSOR_ ne '') then do;
         call execute('output;');
         call execute(_SUCCESSOR_ || '=.; ');
         call execute(_ACTIVITY_ || '=.; _TYPE_ = ""; _RHS_ = .;');
      end;
      if lastobs then do;
         call execute('pe=1;_TYPE_="MIN";_RHS_=.;output;');
         call execute('run;');
      end;
   run;

%mend resources;
%resources;



proc clp condata=condata varselect=wdeg out=out usecondatavars=1;
   obj lb=0 ub=2000;
   cumulative (start=( m1-m6 ) dur=( 16 8 8 8 8 20 ))
              (start=( s1-s6 ) dur=( 8 4 4 4 4 10 ))
              (start=( v1 v2 ) dur=( 15 10 ))
              (start=( b1-b6 ) dur=( 1 1 1 1 1 1))
              (start=( l t1-t5 ) dur=( 2 12 12 12 12 12 ))
              (start=( a1-a6 ) dur=( 4 2 2 2 2 5))
              (start=( p1 p2 ) dur=( 20 13 ));
run;


proc print data=out noobs;
run;

proc transpose data=out out=startdata
   name=_ACTIVITY_  prefix=START;
run;

proc sql noprint;
   create table schedule as
      select a._ACTIVITY_ label="Activity", a._DESC_,
         a._RESOURCE_ as Resource,
         a._SUCCESSOR_, a._LAG_, a._QTY_, a._DURATION_,
         a._LAGDUR_, (b.START1 + a._DURATION_) as finish,
         b.start1 as start from bridge a left join
         startdata b on a._ACTIVITY_ = b._ACTIVITY_
         order by a._resource_, a._activity_, a._successor_;
quit;

title 'Zoned by Resource';
proc gantt data=schedule;
   id _ACTIVITY_ ;
   chart  /  es=start ef=finish scale=2
   zone=Resource onezoneval
   height=2 pcompress
   ref=104 reflabel
   nolegend nojobnum
   /*  activity = _ACTIVITY_ successor= _SUCCESSOR_ */;
run;