IBM ILOG Scheduler User's Manual > Advanced Concepts > More Advanced Problem Modeling with Concert Technology > Complete Program and Output

You can see the entire program cassign.cpp here or online in the standard distribution.

#include <ilsched/iloscheduler.h>
 
ILOSTLBEGIN
 
#if defined(ILO_SDXLOUTPUT)
#include "sdxloutput.h"
#endif
 
IloInt NumberOfActivities = 10;
IloInt NumberOfWorkers = 3;
const char* ActivityNames [] = {"masonry   ",
                                "carpentry ",
                                "plumbing  ",
                                "ceiling   ",
                                "roofing   ",
                                "painting  ",
                                "windows   ",
                                "facade    ",
                                "garden    ",
                                "moving    "};
const char* WorkerNames [] = {"joe",
                              "jack",
                              "jim"};
IloNum ProcessingTimes  [] = {7, 3, 8, 3,  1,  2,  1,  2,  1,  1};
const char* PossibleAssignments [] = {"joe", "jack",     0,
                                      "joe",      0, "jim",
                                      0, "jack",     0,
                                      "joe",      0, "jim",
                                      "joe",      0, "jim",
                                      0, "jack", "jim",
                                      "joe",      0, "jim",
                                      "joe", "jack",     0,
                                      "joe", "jack", "jim",
                                      "joe",      0, "jim"}; 
 
/* THE PROBLEM IS TO MINIMIZE 
         EITHER (1) THE MAXIMAL
         OR     (2) THE AVERAGE
   AMOUNT OF TIME SPENT BY EACH WORKER ON THE CONSTRUCTION SITE GIVEN
   THAT A WORKER CANNOT LEAVE THE SITE BETWEEN TWO ACTIVITIES. */
 
///////////////////////////////////////////////////////////////////////////////
//
// DEFINITION OF THE WORKER CLASS
//
///////////////////////////////////////////////////////////////////////////////
class Worker {
private:
  IloUnaryResource _resource;
  IloActivity      _limitedAttendanceOnSite;
public:
  Worker(IloModel, IloNum durMaxOnSite, const char* name);
  ~Worker();
  IloUnaryResource getResource() const {
    return _resource;
  }
  IloActivity getAttendance() const {
    return _limitedAttendanceOnSite;
  }
  IloNumVar getAttendanceProcessingTime() const {
    return _limitedAttendanceOnSite.getProcessingTimeVariable();
  }
  IloConstraint coverAttendance(IloActivity act);
};
  
Worker::Worker(IloModel model, IloNum durMaxOnSite, const char* name)
  :_resource(model.getEnv()),
   _limitedAttendanceOnSite(model.getEnv(), 
                            IloNumVar(model.getEnv(), 0, durMaxOnSite, IloNumVar::Int))
{
  _resource.setName(name);
  _resource.setCapacityEnforcement(IloMediumHigh);
  _limitedAttendanceOnSite.setName("Attendance");
  _resource.setObject(this);
  /* NEGATIVE STATEMENT: THE FACT THAT THE WORKER IS ON THE SITE FOR
     A LIMITED INTERVAL OF TIME MEANS THAT IT IS TO BE CONSIDERED
     "REQUIRED" BEFORE AND AFTER THIS INTERVAL OF TIME. */
  model.add(_limitedAttendanceOnSite.requires(_resource,
                                              1,
                                              IloBeforeStartAndAfterEnd));
}
 
Worker::~Worker()
{}
 
IloConstraint Worker::coverAttendance(IloActivity act){
  return (act.startsAfterStart(_limitedAttendanceOnSite) &&
          _limitedAttendanceOnSite.endsAfterEnd(act));
}
 
///////////////////////////////////////////////////////////////////////////////
//
// PROBLEM DEFINITION
//
///////////////////////////////////////////////////////////////////////////////
 
void
AddTemporalConstraints(IloModel model,
                       IloActivityArray task,
                       IloNum origin)
{
  IloActivity masonry   = task[0];
  IloActivity carpentry = task[1];
  IloActivity plumbing  = task[2];
  IloActivity ceiling   = task[3];
  IloActivity roofing   = task[4];
  IloActivity painting  = task[5];
  IloActivity windows   = task[6];
  IloActivity facade    = task[7];
  IloActivity garden    = task[8];
  IloActivity moving    = task[9];
 
  /* POST THE TEMPORAL CONSTRAINTS. */
  masonry.setStartMax(origin);
  model.add(carpentry.startsAfterEnd(masonry));
  model.add(plumbing.startsAfterEnd(masonry));
  model.add(ceiling.startsAfterEnd(masonry));
  model.add(roofing.startsAfterEnd(carpentry));
  model.add(painting.startsAfterEnd(ceiling));
  model.add(windows.startsAfterEnd(roofing));
  model.add(facade.startsAfterEnd(roofing));
  model.add(facade.startsAfterEnd(plumbing));
  model.add(garden.startsAfterEnd(roofing));
  model.add(garden.startsAfterEnd(plumbing));
  model.add(moving.startsAfterEnd(windows));
  model.add(moving.startsAfterEnd(facade));
  model.add(moving.startsAfterEnd(garden));
  model.add(moving.startsAfterEnd(painting));
}
IloModel
DefineProblem(IloEnv env,
              IloInt numberOfActivities,
              IloInt numberOfWorkers,
              const char** activityNames,
              const char** workerNames,
              IloNum* processingTimes,
              const char** possibleAssignments,
              IloAltResSet& workersSet,
              IloNumVar& maxCriterion,
              IloNumVar& sumCriterion)
{
  /* CREATE THE MODEL. */
  IloNum origin = 0;
  IloNum horizon = 18;
 
  IloModel model(env);
  IloSchedulerEnv schedEnv(env);
  schedEnv.setOrigin(origin);
  schedEnv.setHorizon(horizon);
 
  /* CREATE THE WORKERS AND SET THE OPTIMIZATION CRITERIA. */
  IloUnaryResourceArray workers(env, numberOfWorkers);
  IloNumVarArray attendancies(env, numberOfWorkers);
  IloInt i;
  for (i = 0; i < numberOfWorkers; i++) {
    Worker* worker =
      new (env) Worker(model, horizon, workerNames[i]);
    attendancies[i] = worker->getAttendanceProcessingTime();
    workers[i]      = worker->getResource();
    workersSet.add(workers[i]);
  }
 
  sumCriterion = IloNumVar(env, 0, numberOfWorkers * horizon, IloNumVar::Int);
  maxCriterion = IloNumVar(env, 0, horizon, IloNumVar::Int);
  model.add(sumCriterion == IloSum(attendancies));
  model.add(maxCriterion == IloMax(attendancies));
 
  /* CREATE THE ACTIVITIES AND THE ASSIGNMENT VARIABLES. IT IS ASSUMED
     THAT THERE IS AT LEAST ONE POSSIBLE ASSIGNMENT PER ACTIVITY. */
  IloInt k = 0;
  IloActivityArray tasks(env,numberOfActivities);
  for (i = 0; i < numberOfActivities; i++) {
    IloActivity activity(env, processingTimes[i]);
    activity.setName(activityNames[i]);
    tasks[i] = activity;
    // RESOURCE ALLOCATION DEMAND
    IloResourceConstraint alternative = activity.requires(workersSet, 1);
    // REMOVE UNAVAILABLE WORKERS
    for (IloInt j = 0; j < numberOfWorkers; j++) {
      if (!possibleAssignments[k]) {
        alternative.setRejected(workers[j]);
      } else {
        model.add(IloIfThen(env,
                            alternative.select(workers[j]),
                            (((Worker*)
                              workers[j].getObject())->coverAttendance(activity))));
      }
      k++;
    }
    model.add(alternative);
  }
  AddTemporalConstraints(model, tasks, origin);
  /* RETURN THE CREATED MODEL. */
  return model;
}
 
///////////////////////////////////////////////////////////////////////////////
//
// PRINTING OF SOLUTIONS
//
///////////////////////////////////////////////////////////////////////////////
 
void
PrintSolution(IloSolver solver,
              IlcInt maxCriterionValue,
              IlcInt sumCriterionValue)
{
  IlcScheduler scheduler(solver);
  for (IlcAltResSetIterator resIte(scheduler); resIte.ok(); ++resIte) {
    for(IlcAltResConstraintIterator ite(*resIte); ite.ok(); ++ite) {
      if ((*ite).getNumberOfPossible() == 1)
        solver.out() << (*ite).getSelected() << "\t"
                     << (*ite).getActivity() << endl;
    }
  }
  solver.out() << "MAXIMAL ATTENDANCE ON SITE = " << maxCriterionValue << endl;
  solver.out() << "SUM OF ATTENDANCES ON SITE = " << sumCriterionValue << endl; 
}
 
///////////////////////////////////////////////////////////////////////////////
//
// MAIN FUNCTION
//
///////////////////////////////////////////////////////////////////////////////
 
void
InitParameters(int argc, char** argv, IloBool& minimizeMax)
{
  if ((argc > 1) && (!strcmp(argv[1], "-minimizeSum")))
    minimizeMax = IloFalse;
}
 
int main(int argc, char** argv)
{
  try {
    IloBool minimizeMax = IlcTrue;
    InitParameters(argc, argv, minimizeMax);
    IloEnv env;
    IloNumVar maxCriterion, sumCriterion;
    IloAltResSet workersSet(env);
    IloModel model = DefineProblem(env,
                                   NumberOfActivities,
                                   NumberOfWorkers,
                                   ActivityNames,
                                   WorkerNames,
                                   ProcessingTimes,
                                   PossibleAssignments,
                                   workersSet,
                                   maxCriterion,
                                   sumCriterion);
    
    IloNumVar criterion = ((minimizeMax) ? maxCriterion : sumCriterion);  
    model.add(IloMinimize(env, criterion));
    
    IloSolver solver(model);
    IloGoal goal = IloAssignAlternative(env) &&
      IloSetTimesForward(env,
                         criterion,
                         IloSelFirstActMinEndMin);
    
    if (solver.solve(goal)) {
      IlcInt maxCrit = solver.getIntVar(maxCriterion).getMin();
      IlcInt sumCrit = solver.getIntVar(sumCriterion).getMin();
      PrintSolution(solver, maxCrit, sumCrit);
#if defined(ILO_SDXLOUTPUT)
      IloSDXLOutput output(env);
      ofstream outFile("cassign.xml");
      output.write(IlcScheduler(solver), outFile);
      outFile.close();
#endif
    } else
      solver.out() << "No solution!" << endl;
    solver.printInformation();
    env.end();
  } catch (IloException& exc) {
    cout << exc << endl;
  }
  
  return 0;
}
 
///////////////////////////////////////////////////////////////////////////////
//
// RESULTS
//
///////////////////////////////////////////////////////////////////////////////
 
/* cassign -minimizeMax
   jim [1]        moving     [17 -- 1 --> 18]
   jim [1]        garden     [15 -- 1 --> 16]
   jack [1]       facade     [15 -- 2 --> 17]
   jim [1]        windows    [12 -- 1 --> 13]
   jim [1]        painting   [13 -- 2 --> 15]
   jim [1]        roofing    [11 -- 1 --> 12]
   joe [1]        ceiling    [7 -- 3 --> 10]
   jack [1]       plumbing   [7 -- 8 --> 15]
   jim [1]        carpentry  [8 -- 3 --> 11]
   joe [1]        masonry    [0 -- 7 --> 7]
   MAXIMAL ATTENDANCE ON SITE = 10
   SUM OF ATTENDANCES ON SITE = 30
*/
 
/* cassign -minimizeSum
   joe [1]        moving     [17 -- 1 --> 18]
   joe [1]        garden     [15 -- 1 --> 16]
   jack [1]       facade     [15 -- 2 --> 17]
   joe [1]        windows    [16 -- 1 --> 17]
   jim [1]        painting   [11 -- 2 --> 13]
   joe [1]        roofing    [14 -- 1 --> 15]
   joe [1]        ceiling    [8 -- 3 --> 11]
   jack [1]       plumbing   [7 -- 8 --> 15]
   joe [1]        carpentry  [11 -- 3 --> 14]
   jack [1]       masonry    [0 -- 7 --> 7]
   MAXIMAL ATTENDANCE ON SITE = 17
   SUM OF ATTENDANCES ON SITE = 29
*/
 
 

As you can see from the results, if we want to minimize the time that any one worker spends on site, we'll minimize maximal attendance on site, and get results such as the first one, where all workers spend 10 days.

In contrast, if we minimize the total number of days spent on site for all workers, we'll get a solution like the second one, where jim spends only 5 days, jack 17, and joe stays 7 days on site.