! Job Load Leveling  (LoadLevel).
  Given a
    Set of jobs, each with an
     earliest begin time,
     latest finish time,
     processing time, and
     load,  
   Decide 
     when each job should be started
     so that the total load in process at
     any instant is minimized.

   The loads could be amount of electricity used by
 various appliances, e.g., dishwasher, hot water heater, iron.
 We want to find when to run each appliance so maximum electricity
 used at any instant is minimized;

! Keywords: Load leveling, Sequencing, Scheduling, Due dates, Ready time,
            Disjunctive formulation;
SETS:
  job: r, p, d, v, s, load;
  jxj(job,job) | &1 #LT# &2: y1, y2, y3, y4, 
                       ds1, ds2, ds3, ds4;
ENDSETS
DATA: ! 1 2 3 4 5 6 7 8 9 10 11 ! The processing times; p = 1 3 18 5 24 4 8 13 12 2 6; ! Ready times; r = 5 12 0 20 0 2 2 3 5 6 20; ! Due date of each job; d = 10 16 22 32 25 8 11 29 25 14 29; ! Load associated with each job; v = 5 4 2 1 6 1 2 3 7 4 3; ENDDATA ! Variables: s(j) = start time for job j, load(j) = load in process at instant just after job j starts, for i < j: ( 4 overlap configurations for i and j) y1(i,j) = 1 if i finishes before j starts, ds1(i,j) = s(j) - s(i), 0 otherwise, y2(i,j) = 1 if i starts before j starts but finishes after j starts, ds2(i,j) = s(j) - s(i), 0 otherwise, y3(i,j) = 1 if i starts after j starts but not before j finishes, ds3(i,j) = s(i) - s(j), 0 otherwise, y4(i,j) = 1 is i starts after j finishes, ds4(i,j) = s(i) - s(j), 0 otherwise; ! Minimize the maximum load at any instant; MIN = mxload; ! Cannot start job i too early or too late; @FOR( job(i): s(i) >= r(i); s(i) + p(i) <= d(i); ); ! The sequencing constraints. Define the disjunctions/scenarios of the 4 possible overlap types for i and j; @FOR( jxj( i,j) : ! Case 1, i finishes before j starts; ds1(i,j) >= p(i)*y1(i,j); ds1(i,j) <= (d(j)-p(j)-r(i))*y1(i,j); ! Case 2, i starts before j starts, but finishes after j starts; ds2(i,j) <= p(i)*y2(i,j); ! Case 3, i starts after j starts but before j finishes; ds3(i,j) <= p(j)*y3(i,j); ! Case 4, i starts after j finishes; ds4(i,j) >= p(j)*y4(i,j); ds4(i,j) <= (d(i)-p(i)-r(j))*y4(i,j); ! Tie the cases together; ! Jobs i and j must be performed in one of the 4 configurations; y1(i,j) + y2(i,j) + y3(i,j) + y4(i,j) = 1; ! Difference in start times of i and j; ds1(i,j) + ds2(i,j) - ds3(i,j) - ds4(i,j) = s(j) - s(i) ; ! Must be binary, 0 or 1; @BIN(y1(i,j)); @BIN(y2(i,j)); @BIN(y3(i,j)); @BIN(y4(i,j)); ); ! Compute load at various instants. We need look only at job start times. The load just after start of job k is v(k) + load of jobs i or j starting before k but not yet finished when k starts; @FOR( job(k): load(k) = v(k) + @SUM( jxj(i,k): y2(i,k)*v(i)) + ! i < k; @SUM( jxj(k,j): y3(k,j)*v(j)); ! j > k; mxload >= load; ! Force the maximum; );