IBM ILOG Scheduler User's Manual > Local Search in Scheduler > Large Neighborhood Search for the Jobshop Problem with Alternatives > Complete Program

You can see the entire program altlns.cpp here or view it online in the standard distribution.

#include <ilsched/ilolnsgoals.h>
#include <ilsolver/iimls.h>
 
ILOSTLBEGIN
 
IloInt ResourceNumbers06 [] = {2, 0, 1, 3, 5, 4,
                               1, 2, 4, 5, 0, 3,
                               2, 3, 5, 0, 1, 4,
                               1, 0, 2, 3, 4, 5,
                               2, 1, 4, 5, 0, 3,
                               1, 3, 5, 0, 4, 2};
 
IloNum Durations06 [] = { 1,  3,  6,  7,  3,  6,
                          8,  5, 10, 10, 10,  4,
                          5,  4,  8,  9,  1,  7,
                          5,  5,  5,  3,  8,  9,
                          9,  3,  5,  4,  3,  1,
                          3,  3,  9, 10,  4,  1};
 
IloInt ResourceNumbers10 [] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                               0, 2, 4, 9, 3, 1, 6, 5, 7, 8,
                               1, 0, 3, 2, 8, 5, 7, 6, 9, 4,
                               1, 2, 0, 4, 6, 8, 7, 3, 9, 5,
                               2, 0, 1, 5, 3, 4, 8, 7, 9, 6,
                               2, 1, 5, 3, 8, 9, 0, 6, 4, 7,
                               1, 0, 3, 2, 6, 5, 9, 8, 7, 4,
                               2, 0, 1, 5, 4, 6, 8, 9, 7, 3,
                               0, 1, 3, 5, 2, 9, 6, 7, 4, 8,
                               1, 0, 2, 6, 8, 9, 5, 3, 4, 7};
 
IloNum Durations10 [] = {29, 78,  9, 36, 49, 11, 62, 56, 44, 21,
                         43, 90, 75, 11, 69, 28, 46, 46, 72, 30,
                         91, 85, 39, 74, 90, 10, 12, 89, 45, 33,
                         81, 95, 71, 99,  9, 52, 85, 98, 22, 43,
                         14,  6, 22, 61, 26, 69, 21, 49, 72, 53,
                         84,  2, 52, 95, 48, 72, 47, 65,  6, 25,
                         46, 37, 61, 13, 32, 21, 32, 89, 30, 55,
                         31, 86, 46, 74, 32, 88, 19, 48, 36, 79,
                         76, 69, 76, 51, 85, 11, 40, 89, 26, 74,
                         85, 13, 61,  7, 64, 76, 47, 52, 90, 45};
 
IloInt ResourceNumbers20 [] = {0, 1, 2, 3, 4,
                               0, 1, 3, 2, 4,
                               1, 0, 2, 4, 3,
                               1, 0, 4, 2, 3,
                               2, 1, 0, 3, 4,
                               2, 1, 4, 0, 3,
                               1, 0, 2, 3, 4,
                               2, 1, 0, 3, 4,
                               0, 3, 2, 1, 4,
                               1, 2, 0, 3, 4,
                               1, 3, 0, 4, 2,
                               2, 0, 1, 3, 4,
                               0, 2, 1, 3, 4,
                               2, 0, 1, 3, 4,
                               0, 1, 4, 2, 3,
                               1, 0, 3, 4, 2,
                               0, 2, 1, 3, 4,
                               0, 1, 4, 2, 3,
                               1, 2, 0, 3, 4,
                               0, 1, 2, 3, 4};
 
IloNum Durations20 [] = {29,  9, 49, 62, 44,
                         43, 75, 69, 46, 72,
                         91, 39, 90, 12, 45,
                         81, 71,  9, 85, 22,
                         14, 22, 26, 21, 72,
                         84, 52, 48, 47,  6,
                         46, 61, 32, 32, 30,
                         31, 46, 32, 19, 36,
                         76, 76, 85, 40, 26,
                         85, 61, 64, 47, 90,
                         78, 36, 11, 56, 21,
                         90, 11, 28, 46, 30,
                         85, 74, 10, 89, 33,
                         95, 99, 52, 98, 43,
                          6, 61, 69, 49, 53,
                          2, 95, 72, 65, 25,
                         37, 13, 21, 89, 55,
                         86, 74, 88, 48, 79,
                         69, 51, 11, 89, 74,
                         13,  7, 76, 52, 45};
 
 
 
///////////////////////////////////////////////////////////////////
//
// PRINTING OF SOLUTIONS
//
///////////////////////////////////////////////////////////////////
 
void PrintRange(IloEnv& env, IloNum min, IloNum max) {
  if (min == max)
    env.out() << (IlcInt)min;
  else
    env.out() << (IlcInt)min << ".." << (IlcInt)max;
}
 
void PrintSolution(IloEnv& env, 
                   const IloSchedulerSolution solution,
                   const IloNumVar makespan)
{
  if (solution.contains(makespan)) {
    env.out() << "Solution with makespan ["
      << solution.getMin(makespan) << ".." 
      << solution.getMax(makespan) << "]" << endl;
  }
 
  for (IloSchedulerSolution::ResourceConstraintIterator
                                           iter(solution);
       iter.ok();
       ++iter) 
  {
    IloResourceConstraint rc = *iter;
    if (!solution.isResourceSelected(rc))
      IloSchedulerException("No resource assigned!");
 
    IloActivity activity = rc.getActivity();
    env.out() << activity.getName() << "[";
    PrintRange(env, 
               solution.getStartMin(activity),  
               solution.getStartMax(activity));
    env.out() << " -- ";
    PrintRange(env, 
               solution.getDurationMin(activity),
               solution.getDurationMax(activity));
    env.out() << " --> ";
    PrintRange(env, 
               solution.getEndMin(activity),
               solution.getEndMax(activity));
    env.out() << "]: " << solution.getSelected(rc).getName()
              << endl;
  }
}
 
////////////////////////////////////////////////////////////////////
//
// FIND A FIRST SOLUTION
//
////////////////////////////////////////////////////////////////////
 
void FindInitialSolution(IloModel model, 
                         IloSchedulerSolution globalSolution,
                         IloSchedulerSolution lsSolution,
                         IloNumVar costVar) {
  IloEnv env = model.getEnv();
  IloSolver solver(model);
  IlcScheduler scheduler(solver);
 
  IloGoal g = IloAssignAlternative(env) && 
    IloRankForward(env) && 
    IloInstantiate(env, costVar);
 
  solver.startNewSearch(g);
 
  if(solver.next()) {
    IloNum best = solver.getValue(costVar);
    solver.out() << "Solution for Cost " <<  best << endl;
    solver.printInformation();
    globalSolution.store(scheduler);
    lsSolution.store(scheduler);
  }
 
  solver.endSearch();
  solver.end();
}
 
////////////////////////////////////////////////////////////////////
//
// DEFINE A NEW NEIGHBORHOOD : RELOCATE JOB
//
////////////////////////////////////////////////////////////////////
 
class RelocateJobNHoodI : public IloSchedulerLargeNHoodI {
protected:
  IloArray<IloActivityArray> _jobs;
 
public:
  RelocateJobNHoodI(IloEnv env,
                    IloArray<IloActivityArray> jobs,
                    const char* name);
  ~RelocateJobNHoodI();
  // virtuals
  virtual IloInt getSize(IloSolver solver) const;
  virtual IloSolution defineSelected(IloSolver solver, IloInt index);
  virtual void start(IloSolver solver, IloSolution solution);
};
 
ILOPREDICATE0(IloRCFalsePredicate,
              IloResourceConstraint, rc) {
  return IloFalse;
}
 
ILOCTXPREDICATE0(IloRCTrueIfNotSelectedPredicate,
                 IloResourceConstraint, rc,
                 IloSchedulerLargeNHood, nhood) {
  if (nhood.isSelected(rc))
    return IloFalse;
  else 
    return IloTrue;
}
 
ILOPREDICATE0(IloActivityFalsePredicate,
                IloActivity, activity) {
  return IloFalse;
}
 
RelocateJobNHoodI::RelocateJobNHoodI(IloEnv env,
                                     IloArray<IloActivityArray> jobs,
                                     const char* name)
  : IloSchedulerLargeNHoodI(env, name),
    _jobs(env)
{
  // set default restore policy
  IloPredicate<IloResourceConstraint> rcFalsePredicate = 
    IloRCFalsePredicate(env);
  setRestoreRCNextPredicate(rcFalsePredicate);
  setRestoreRCPrevPredicate(rcFalsePredicate);
  setRestoreRCSetupPredicate(rcFalsePredicate);
  setRestoreRCTeardownPredicate(rcFalsePredicate);
  IloPredicate<IloResourceConstraint> rcTrueIfNotSelectedPredicate = 
    IloRCTrueIfNotSelectedPredicate(env);
  setRestoreRCDirectSuccessorPredicate(rcTrueIfNotSelectedPredicate);
  setRestoreRCDirectPredecessorPredicate(rcTrueIfNotSelectedPredicate);
  setRestoreRCCapacityPredicate(rcTrueIfNotSelectedPredicate);
  setRestoreRCSelectedPredicate(rcTrueIfNotSelectedPredicate);
 
  IloPredicate<IloActivity> activityFalsePredicate =
    IloActivityFalsePredicate(env);
  setRestoreActivityStartPredicate(activityFalsePredicate);
  setRestoreActivityEndPredicate(activityFalsePredicate);
  setRestoreActivityDurationPredicate(activityFalsePredicate);
  setRestoreActivityProcessingTimePredicate(activityFalsePredicate);
  setRestoreActivityExternalPredicate(activityFalsePredicate);
  
  for (IloInt j=0; j<jobs.getSize(); ++j) {
    IloInt nbActivities = jobs[j].getSize();
    IloActivityArray job(env);
    for (IloInt a=0; a<nbActivities; ++a) {
      IloActivity activity = jobs[j][a];
      job.add(activity);
    }
    _jobs.add(job);
  }
}
 
RelocateJobNHoodI::~RelocateJobNHoodI()
{
  _jobs.end();
}
 
void 
RelocateJobNHoodI::start(IloSolver solver, IloSolution solution) 
{
  IloSchedulerLargeNHoodI::start(solver, solution);
}
 
IloInt 
RelocateJobNHoodI::getSize(IloSolver solver) const
{
  IloInt size = _jobs.getSize();
  
  return size;
}
 
IloSolution
RelocateJobNHoodI::defineSelected(IloSolver solver, IloInt index)
{
  IloEnv env = solver.getEnv();
  
  IloActivityArray job;
  job = _jobs[index];
  
  IloSchedulerSolution selected(env);
  
  IloSchedulerSolution currentSolution = getCurrentSolution();
  
  // add in selected all activities of selected job and their
  // resource constraints
  IloInt nbActivities = job.getSize();
  for (IloInt a=0; a<nbActivities; a++) {
    IloActivity activity = job[a];
    selected.add(activity);
    IloSchedulerSolution::ResourceConstraintIterator 
      rcIter(currentSolution, activity);
    for (; rcIter.ok(); ++rcIter) {
      IloResourceConstraint rc = *rcIter;
      selected.add(rc);
    }
  }
  
  return selected;
}
 
IloSchedulerLargeNHood 
RelocateJobNHood(IloEnv env, 
                 IloArray<IloActivityArray> jobs,
                 const char* name = 0)
{
  return new (env) RelocateJobNHoodI(env, 
                                     jobs,
                                     name);
}
 
 
IloSchedulerSolution CreateLSSolution(IloEnv env, 
                                      IloSchedulerSolution globalSolution) {
 
 
  /* CREATE LOCAL SEARCH SOLUTION */
  IloSchedulerSolution lsSolution = globalSolution.makeClone(env);
 
  for (IloIterator <IloResourceConstraint> iter(env); iter.ok(); ++iter)
    lsSolution.setRestorable(*iter, 
                             IloRestoreRCNext | IloRestoreRCSelected);
 
  for (IloIterator <IloResource> resIter(env); resIter.ok(); ++resIter)
    lsSolution.add(*resIter);
 
  return lsSolution;
}
 
////////////////////////////////////////////////////////////////////
//
// SOLVE THE MODEL USING A GREEDY DESCENT SEARCH
//
////////////////////////////////////////////////////////////////////
 
void SolveModel(IloModel model, 
                IloNumVar makespan,
                IloArray<IloActivityArray> jobs,
                IloSchedulerSolution& globalSolution) {
 
  IloEnv env = model.getEnv();
 
  /* CREATE LOCAL SEARCH SOLUTION */
  IloSchedulerSolution lsSolution = CreateLSSolution(env, globalSolution);
  IloObjective obj = IloMinimize(env, makespan);
  lsSolution.getSolution().add(obj);
 
  globalSolution.getSolution().add(makespan);
 
  /* GENERATE AN INITIAL SOLUTION. */
  FindInitialSolution(model, globalSolution, lsSolution, makespan);
  IloNum best = globalSolution.getMax(makespan);
  env.out() << "Initial solution:" << endl;
  PrintSolution(env, globalSolution, makespan);
 
  /* SET PARAMETERS FOR LOCAL SEARCH */
  IloSolver lsSolver(model);
  IlcScheduler lsScheduler(lsSolver);
 
  /* GLIDING TIME WINDOW SEARCH */
 
  IloGoal subGoal = 
    IloAssignAlternative(env) && 
    IloRankForward(env) && 
    IloInstantiate(env, makespan);
 
  IloInt failLimit = 2000;
 
  if (failLimit < IloIntMax) {
    subGoal = IloLimitSearch(env, subGoal, IloFailLimit(env, failLimit));
  }
  
  subGoal = IloSelectSearch(env, subGoal, IloMinimizeVar(env, makespan, 1.0));
 
  IloSchedulerLargeNHood relocateActivities = IloRelocateActivityNHood(env);
 
  IloSchedulerLargeNHood timeWindowNHood = 
    IloTimeWindowNHood(env, 20, 10);
 
  IloSchedulerLargeNHood relocateJobNHood = RelocateJobNHood(env, jobs);
 
  IloNHood nhood = 
    IloContinue(env, relocateActivities)
    +
    IloContinue(env, relocateJobNHood)
    +
    IloContinue(env, timeWindowNHood)
    ;
    
  
  IloGoal greedyMove = IloSingleMove(env, 
                                     lsSolution, 
                                     nhood,
                                     IloImprove(env), 
                                     IloFirstSolution(env),
                                     subGoal);
  IloInt movesDone = 0;
  while(lsSolver.solve(greedyMove)) {
    IloNum cost = lsSolution.getSolution().getObjectiveValue();
    lsSolver.out() << "Move: " << movesDone << ":\t";
    ++movesDone;
    lsSolver.out() << "solution at cost: " << cost << " ** HC" << endl;
    best = cost;
    globalSolution.store(lsScheduler);
  }
 
  env.out() << "Final solution Cost: " << best << endl;
  
  PrintSolution(env, globalSolution, makespan);
  
  lsSolver.end();
}
 
////////////////////////////////////////////////////////////////////
//
// DEFINE THE MODEL WITH ALTERNATIVE RESOURCES
//
////////////////////////////////////////////////////////////////////
 
IloModel
DefineModel(IloEnv& env,
            IloInt numberOfJobs,
            IloInt numberOfResources,        
            IloInt* resourceNumbers,
            IloNum* durations,
            IloRandom randomGenerator,
            IloSchedulerSolution solution,
            IloArray<IloActivityArray>& jobs,
            IloNumVar& makespan)
{
  IloModel model(env);
 
  /* CREATE THE MAKESPAN VARIABLE. */
  IloInt numberOfActivities = numberOfJobs * numberOfResources;
  IloNum horizon = 0;
  IloInt k;
 
  for (k = 0; k < numberOfActivities; k++)
    horizon += durations[k];
 
  makespan = IloNumVar(env, 0, IloIntMax/2, ILOINT);
 
  /* CREATE THE RESOURCES. */
  IloSchedulerEnv schedEnv(env);
  IloResourceParam resParam = schedEnv.getResourceParam();
  resParam.setCapacityEnforcement(IloMediumLow);
 
  char buffer[128];
  IloInt j;
  IloUnaryResource *resources = 
    new (env) IloUnaryResource[numberOfResources];
  for (j = 0; j < numberOfResources; j++) {
    sprintf(buffer, "R%ld", j);
    resources[j] = IloUnaryResource(env, buffer);
  }
 
  /* CREATE THE ALTERNATIVE RESOURCE SETS */
  env.out() << "Creating resource sets" << endl;
  IloInt *altResourceNumbers = new (env) IloInt[numberOfResources];
  IloAltResSet *altResSets = 
    new (env) IloAltResSet[numberOfResources];
  for (j = 0; j < numberOfResources; j++) {
    altResSets[j] = IloAltResSet(env);
    altResSets[j].add(resources[j]);
 
    // RANDOMLY PICK ANOTHER RESOURCE TO BE IN THE SET
    assert(numberOfResources > 1);
    IloInt index = randomGenerator.getInt(numberOfResources);
    while(index == j)
      index = randomGenerator.getInt(numberOfResources);
 
    altResSets[j].add(resources[index]);
    altResourceNumbers[j] = index;
    env.out() << "Set #" << j << ":\t" << resources[j].getName()
              << " " << resources[index].getName() << endl;
  }
 
  /* CREATE THE ALTERNATIVE DURATIONS */
  IloNum *altDurations = new (env) IloNum[numberOfActivities];
  for(k = 0; k < numberOfActivities; k++) {
    IloNum multiplier = 1.0 + (randomGenerator.getFloat() / 2.0);
    altDurations[k] = IloCeil(multiplier * durations[k]);
  }
 
  /* CREATE THE ACTIVITIES. */
  env.out() << "Setting alternative processing times" << endl;
  k = 0;
  IloInt i;
  jobs = IloArray<IloActivityArray>(env);
  for (i = 0; i < numberOfJobs; i++) {
    IloActivityArray job(env);
    jobs.add(job);
    IloActivity previousActivity;
    for (j = 0; j < numberOfResources; j++) {
 
      IloNum ptMin = IloMin(durations[k], altDurations[k]);
      IloNum ptMax = IloMax(durations[k], altDurations[k]);
      IloNumVar ptVar(env, ptMin, ptMax, ILOINT);
 
      IloActivity activity(env, ptVar);
      job.add(activity);
 
      sprintf(buffer, "J%ldS%ldR%ld", i, j, resourceNumbers[k]);
      activity.setName(buffer);
      solution.add(activity, IloRestoreNothing);
 
      IloResourceConstraint rc =
        activity.requires(altResSets[resourceNumbers[k]]);
      
      // SET THE DIFFERENT DURATIONS DEPENDING ON 
      // RESOURCE SELECTION 
      rc.setProcessingTimeMax(resources[resourceNumbers[k]],
                              durations[k]);
      rc.setProcessingTimeMin(resources[resourceNumbers[k]],
                              durations[k]);
 
      rc.setProcessingTimeMax(
             resources[altResourceNumbers[resourceNumbers[k]]],
             altDurations[k]);
      rc.setProcessingTimeMin(
             resources[altResourceNumbers[resourceNumbers[k]]],
             altDurations[k]);
 
      model.add(rc);
 
      solution.add(rc, IloRestoreNothing);
      env.out() << activity.getName() 
                << ":\tProcessing Time(" 
                << resources[resourceNumbers[k]].getName()
                << "): " << durations[k]
                << "\n\tProcessing Time(" 
                << resources[altResourceNumbers[
                                    resourceNumbers[k]]].getName()
                << "): " << altDurations[k]
                << endl;
 
 
      if (j != 0)
        model.add(activity.startsAfterEnd(previousActivity));
      previousActivity = activity;
      k++;
    }
    model.add(previousActivity.endsBefore(makespan));
  }
 
  /* RETURN THE MODEL. */
  return model;
}
 
 
/////////////////////////////////////////////////////////////////
//
// INITIALIZE THE PROGRAM ARGUMENTS
//
/////////////////////////////////////////////////////////////////
void
InitParameters(int argc,
               char** argv,
               IloInt& numberOfJobs,
               IloInt& numberOfResources,
               IloInt*& resourceNumbers,
               IloNum*& durations)
{
  numberOfJobs = 6;
  numberOfResources = 6;
  resourceNumbers = ResourceNumbers06;
  durations = Durations06;
 
  if (argc > 1) {
    IloInt number = atol(argv[1]);
    if (number == 10) {
      numberOfJobs = 10;
      numberOfResources = 10;
      resourceNumbers = ResourceNumbers10;
      durations = Durations10;
    }
    else if (number == 20) {
      numberOfJobs = 20;
      numberOfResources = 5;
      resourceNumbers = ResourceNumbers20;
      durations = Durations20;
    }
  }
}
 
/////////////////////////////////////////////////////////////////
//
// MAIN FUNCTION
//
/////////////////////////////////////////////////////////////////
 
int main(int argc, char** argv)
{
  IloInt numberOfJobs;
  IloInt numberOfResources;
  IloInt* resourceNumbers;
  IloNum* durations;
 
  InitParameters(argc,
                 argv,
                 numberOfJobs,
                 numberOfResources,
                 resourceNumbers,
                 durations);
 
  try {
 
    IloEnv env;
    IloNumVar makespan;
    IloArray<IloActivityArray> jobs;
    IloRandom randGen(env, 8975324);
    IloSchedulerSolution solution(env);
    IloModel model = DefineModel(env,
                                 numberOfJobs,
                                 numberOfResources,
                                 resourceNumbers,
                                 durations,
                                 randGen,
                                 solution,
                                 jobs,
                                 makespan);
 
    SolveModel(model, makespan, jobs, solution);
    env.end();
  }
  catch (IloSchedulerException& exc) {
    cout << exc << endl;
  }
  catch (IloException& exc) {
    cout << exc << endl;
  }
 
  return 0;
}