model GENSCHED ; /* General Scheduler */

/*----------------------------------------------------------------------
 *
 * N		Set of tasks 
 * D[N]		Integer processing time of nth task
 * F[N]		Deadlines for the nth task
 * P[N,N]	(DAG) Precedence matrix determines a parial order on the tasks	
 * R		Set of resources
 * B[R]		Capacity of rth resource		
 * M		Processors for executing tasks
 * A[M,N]	True if nth task can be executed on the mth processor
 *
 */

  set N = (Plastering, Electrics, Plumbing, Painting, Sanding, Fireplace, Gas); 
  set R = (Money, Tea, Lorry);
  set M = (Plasterer, Electrician, Plumber, Decorator, Manager);

  parameter D[N] ; /* Processing Times */
  D[n] := data (n in N: Plastering 1, Electrics 1, Plumbing 1, Painting 3, Sanding 2, Fireplace 1, Gas 1);
  
  parameter F[N] ; /* Task deadlines */
  F[n] := data (n in N: Plastering 14, Electrics 19, Plumbing 12, Painting 19, Sanding 15, Fireplace 15, Gas 15);

  parameter P[N,N] ; /* Task precedence (project network) (nb diagonal entries 0) */
  P[i,j] := table(i in N, j in N:
			Plastering Electrics Plumbing Painting Sanding Fireplace Gas :
	Plastering 	0          0         0        0        0       0         0 
	Electrics 	1          0         0        0        0       0         0
	Plumbing 	0          0         0        0        0       0         0
	Painting 	0          0         0        0        0       0         0
	Sanding 	0          0         0        0        0       0         0 
	Fireplace 	0          0         0        0        0       0         0 
	Gas		0          0         0        0        0       0         0 );

  parameter I[N,N] ; /* Models EQUIVALENCE - required because GLPK constraint qualifiers in development */
  I[i,j] := table(i in N, j in N:
			Plastering Electrics Plumbing Painting Sanding Fireplace Gas :
	Plastering 	1          0         0        0        0       0         0 
	Electrics 	0          1         0        0        0       0         0
	Plumbing 	0          0         1        0        0       0         0
	Painting 	0          0         0        1        0       0         0
	Sanding 	0          0         0        0        1       0         0 
	Fireplace 	0          0         0        0        0       1         0 
	Gas		0          0         0        0        0       0         1 );

  parameter B[R] ; /* Resource Limits (per time interval) */
  B[r] := data (r in R: Money 100000, Tea 48, Lorry 1);

  parameter W[R] ; /* Resource Cost */
  W[r] := data (r in R: Money 1, Tea 0.1, Lorry 100);

  parameter C[R,N] ; /* Job Resource Usage (per time interval) */
  C[i,j] := table(i in R, j in N:
			Plastering Electrics Plumbing Painting Sanding Fireplace Gas :
	Money	 	100        150       80       50       50      350       80 
	Tea	 	1          1         1        1        0       0         1 
	Lorry	 	0          0         0        0        1       1         0 );

  parameter A[M,N] ; /* Job Assignment Compatability */
  A[i,j] := table(i in M, j in N:
			Plastering Electrics Plumbing Painting Sanding Fireplace Gas :
	Plasterer 	1          0         0        0        0       0         0 
	Electrician 	0          1         0        0        0       0         0
	Plumber 	0          0         1        0        0       0         1 
	Decorator 	0          0         0        1        1       0         0
	Manager	 	0          0         0        0        0       1         0 );

  parameter T ; /* Total of all processing times */
  T := sum (n, D[n]) ;

  integer variable X ;			/* End time of schedule */
  variable Y ;				/* Total cost */
  integer variable S[N] ;		/* Starting time of nth job */
  binary  variable Q[M,N] ;		/* Assignment of jobs to machines */
  binary  variable U[N,N] ;		/* Dummy - True when S[i] <= S[k] */
  integer variable V[N,N] ;		/* Dummy - True if ith job active when jth job starts */ 
 
  constraints
       Z,           			/* Total time taken */
       START[N],			/* Earliest start time constraints */
       FINISH[N],			/* Ensure schedule ends after jobs */
       COMPLETE[N],			/* Complete all job constraints */
       COMPAT[M,N],			/* Jobs allocated to suitable machines */
       DUMMY1[N,N],			/*  */
       DUMMY2[N,N],			/*  */
       EXCLUS[M,N,N],			/* Exclusivity constraints */
       PREC1[N,N],			/* Precedence relations 1 */
       PREC2[N,N],			/* Precedence relations 2 */
       RESOURCE[R,N],			/* Resource Limits */
       COST,				/* Calculate cost */
       DEADLINE[N];			/* Deadline Constraints */	
       
  Z := X ; 					/* Total schedule duration */

/* Problem Instance */
  START[n] := S[n] >= 0 ; 
  FINISH[n] := X >= S[n] + D[n] ;
  COMPLETE[n] := sum (m, Q[m,n]) = 1 ;

/* Scheduler - I[i,j] used to eliminate constraints where i != j - use predicates when available */
  COMPAT[m,n] := Q[m,n] <= A[m,n] ;
  DUMMY1[i,k] := S[k] - S[i] <= T * (U[i,k] + I[i,k]) ; 
  DUMMY2[i,k] := U[i,k] + U[k,i] = 1 - I[i,k] ; 
  EXCLUS[m,i,k] := S[i] + D[i] - S[k] <= T * (1 - U[i,k] + 2 - Q[m,i] - Q[m,k]) ; 
  PREC1[i,k] := U[i,k] >= P[i,k] ;
  PREC2[i,k] := P[i,k] * (S[i] + D[i] - S[k]) <= 0 ;
  
/* Resource + Deadline Constraints */
  RESOURCE[r,k] := C[r,k] + sum (i, (V[i,k] + V[k,i]) * C[r,i]) - 2 * V[k,k] * C[r,k] <= B[r] ;
  COST := Y = sum ((m,n,r), C[r,n] * W[r] * Q[m,n]) ; 
  DEADLINE[n] := S[n] + D[n] <= F[n] ;
  
  minimize Z;

end;
