IBM ILOG Scheduler User's Manual > Local Search in Scheduler > Shuffling as Local Search in Scheduler > Complete Program

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

#include <ilsched/iloscheduler.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};
 
IloInt 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};
 
IloInt 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};
 
IloInt 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};
 
///////////////////////////////////////////////////////////////////////////////
//
// PROBLEM DEFINITION
//
///////////////////////////////////////////////////////////////////////////////
 
IloModel
DefineModel(IloEnv& env,
            IloInt numberOfJobs,
            IloInt numberOfResources,        
            IloInt* resourceNumbers,
            IloInt* durations,
            IloNumVar& makespan,
            IloSchedulerSolution& solution)
{
  IloModel model(env);
 
  /* CREATE THE MAKESPAN VARIABLE. */
  IloInt numberOfActivities = numberOfJobs * numberOfResources;
  IloInt horizon = 0;
  IloInt k;
 
  for (k = 0; k < numberOfActivities; k++)
    horizon += durations[k];
 
  makespan = IloNumVar(env, 0, horizon, ILOINT);
  solution = IloSchedulerSolution(env);
 
  /* CREATE THE RESOURCES. */
  IloSchedulerEnv schedEnv(env);
  schedEnv.getResourceParam().setCapacityEnforcement(IloMediumHigh);
  schedEnv.getResourceParam().setPrecedenceEnforcement(IloMediumHigh);
 
  IloTextureParam tParam = schedEnv.getTextureParam();
 
  IloRCTextureFactory rcFactory = IloRCTextureProbabilisticFactory(env);
  tParam.setRCTextureFactory(rcFactory);
 
  IloTextureCriticalityCalculator critCalc =
    IloProbabilisticCriticalityCalculator(env);
  tParam.setCriticalityCalculator(critCalc);
 
  IloRandom randGen(env, 98020981);
  tParam.setRandomGenerator(randGen);
  tParam.setHeuristicBeta(0.75);
 
  IloInt j;
  IloUnaryResource *resources = 
    new (env) IloUnaryResource[numberOfResources];
  for (j = 0; j < numberOfResources; j++)
    resources[j] = IloUnaryResource(env);
 
  /* CREATE THE ACTIVITIES. */
  char buffer[128];
  k = 0;
  IloInt i;
  for (i = 0; i < numberOfJobs; i++) {
    IloActivity previousActivity;
    for (j = 0; j < numberOfResources; j++) {
      IloActivity activity(env, durations[k]);
      sprintf(buffer, "J%ldS%ldR%ld", i, j, resourceNumbers[k]);
      activity.setName(buffer);
      model.add(activity.requires(resources[resourceNumbers[k]]));
      if (j != 0)
        model.add(activity.startsAfterEnd(previousActivity));
      solution.add(activity, IloRestoreNothing);
      previousActivity = activity;
      k++;
    }
    model.add(previousActivity.endsBefore(makespan));
  }
 
  /* RETURN THE MODEL. */
  return model;
}
 
 
///////////////////////////////////////////////////////////////////////////////
//
// PRINT SOLUTION
//
///////////////////////////////////////////////////////////////////////////////
void PrintSolution(IloSchedulerSolution sol){
  IloEnv env = sol.getEnv();
  for(IloSchedulerSolution::ActivityIterator ite(sol); ite.ok(); ++ite){
    IloActivity act = *ite;
    env.out() << act.getName() << "[";
 
    IloNum startMin = sol.getStartMin(act);
    IloNum startMax = sol.getStartMax(act);
    env.out() << startMin;
    if (startMin != startMax)
      env.out() << ".." << startMax;
 
    env.out() << " -- ";
 
    IloNum pMin = sol.getProcessingTimeMin(act);
    IloNum pMax = sol.getProcessingTimeMax(act);
    env.out() << pMin;
    if (pMin != pMax)
      env.out() << ".." << pMax;
 
    env.out() << " --> ";
 
    IloNum endMin = sol.getEndMin(act);
    IloNum endMax = sol.getEndMax(act);
    env.out() << endMin;
    if (endMin != endMax)
      env.out() << ".." << endMax;
 
    env.out() << "]" << endl;
  }
}
 
///////////////////////////////////////////////////////////////////////////////
//
// Critical Path Object: This object computes and returns a set of
// resource constraints that constitute a randomly selected critical
// path in the current solution
//
///////////////////////////////////////////////////////////////////////////////
 
class RCSortElement {
private:
  IloResourceConstraint _rc;
  IloNum _startValue;
  IloInt _tieBreaker;
  
public:
  RCSortElement() : _rc(0), _startValue(0), _tieBreaker(0) {}
  RCSortElement(IloResourceConstraint rc, IloInt startValue, IloInt tieBreaker)
    : _rc(rc), _startValue(startValue), _tieBreaker(tieBreaker) {}
 
  IloResourceConstraint getRC() { return _rc; }
  IloNum getStartValue() { return _startValue; }
  IloInt getTieBreaker() { return _tieBreaker; }
 
  void setRC(IloResourceConstraint rc) { _rc = rc; }
  void setStartValue(IloNum st) { _startValue = st; }
  void setTieBreaker(IloInt tie) { _tieBreaker = tie; }
};
 
static
int RCSortElementAscendingSTCompare(const void* first,
				    const void* second)
{
  RCSortElement *a = (RCSortElement *) first;
  RCSortElement *b = (RCSortElement *) second;
  
  if (a->getStartValue() > b->getStartValue())
    return 1;
  else if (a->getStartValue() < b->getStartValue())
    return -1;
  else if (a->getTieBreaker() > b->getTieBreaker())
    return 1;
  else if (a->getTieBreaker() < b->getTieBreaker())
    return -1;
 
  return 0;
}
 
class CriticalPath {
private:
  IloInt _cpSize;
  IloArray<IloResourceConstraint> _cpArray;
 
  IloRandom _randGen;
 
  RCSortElement *getCriticalRCs(IloSchedulerSolution, IloInt&);
  void pickRandomCP(RCSortElement *, IloSchedulerSolution,IloInt);
  void addCPElement(IloResourceConstraint);
 
public:
  CriticalPath(IloEnv env) :
    _cpSize(0), _cpArray(env), _randGen(env, 98998) {}
  ~CriticalPath() {}
 
  IloArray<IloResourceConstraint>& computeCP(IloSchedulerSolution, IloInt&);
};
 
RCSortElement *CriticalPath::getCriticalRCs(IloSchedulerSolution solution,
					    IloInt& nbCriticalRCs) {
  // count the number of RCs with assigned start times
  for(IloSchedulerSolution::ResourceConstraintIterator iter(solution);
      iter.ok(); ++iter) {
    IloResourceConstraint rc = *iter;
    IloActivity act = rc.getActivity();
    if (solution.getStartMin(act) == solution.getStartMax(act)) 
      ++nbCriticalRCs;
  }
 
  // populate array
  RCSortElement *sortArray = new RCSortElement[nbCriticalRCs];
  IloInt index = 0;
  for(IloSchedulerSolution::ResourceConstraintIterator iter2(solution);
      iter2.ok(); ++iter2) {
 
    IloResourceConstraint rc = *iter2;
    IloActivity act = rc.getActivity();
 
    if (solution.getStartMin(act) == solution.getStartMax(act)) {
      sortArray[index].setRC(rc);
      sortArray[index].setStartValue(solution.getStartMin(act));
      sortArray[index].setTieBreaker(index);
      ++index;
    }
  }
 
  // order in ascending start time
  qsort(sortArray, nbCriticalRCs, 
        sizeof(RCSortElement), RCSortElementAscendingSTCompare);
 
  return sortArray;
}
 
void CriticalPath::addCPElement(IloResourceConstraint rc) {
  if (_cpSize < _cpArray.getSize())
    // reuse
    _cpArray[_cpSize] = rc;
  else 
    // add
    _cpArray.add(rc);
 
  _cpSize++;
}
 
void CriticalPath::pickRandomCP(RCSortElement *sortArray,
				IloSchedulerSolution solution,
				IloInt nbCriticalRCs) {
  _cpSize = 0;
  IloNum endRC = 0;
  IloInt i = -1;
  while(i < nbCriticalRCs - 1) {
    // skip elements not on the same critical path as rc
    IloInt nextIndexStart = i + 1;
    while((nextIndexStart < nbCriticalRCs) &&
          (sortArray[nextIndexStart].getStartValue() < endRC))
      nextIndexStart++;
    
    // gather elements that are successors of rc on the critical
    // path. There may be more than one
 
    IloInt nextIndexEnd = nextIndexStart + 1;
    while((nextIndexEnd < nbCriticalRCs) &&
          (sortArray[nextIndexEnd].getStartValue() == endRC))
      nextIndexEnd++;
      
    if (nextIndexStart < nbCriticalRCs) {
 
      // At this point the elements between sortArray[nextIndexStart]
      // and sortArray[nextIndexEnd-1] inclusive are all successors to
      // rc on some critical path. 
      // We randomly pick one of them.
 
      IloInt randIndex = nextIndexStart;
      IloInt indexDiff = nextIndexEnd - nextIndexStart;
      if (indexDiff > 1)
        randIndex += _randGen.getInt(indexDiff);
            
      IloResourceConstraint next = sortArray[randIndex].getRC();
      addCPElement(next);
 
      i = randIndex;
      endRC = solution.getEndMin(next.getActivity());
    }
    else
      i = nbCriticalRCs;
  }
}
 
IloArray<IloResourceConstraint>& CriticalPath::computeCP(
				       IloSchedulerSolution solution,
				       IloInt& cpSize) {
  IloInt nbCriticalRCs = 0;
  RCSortElement *sortArray = getCriticalRCs(solution, nbCriticalRCs);
  
  // Pick the RCs in one (randomly selected) critical path
  pickRandomCP(sortArray, solution, nbCriticalRCs);
  delete [] sortArray;
 
  cpSize = _cpSize;
  return _cpArray;
}
 
///////////////////////////////////////////////////////////////////////////////
//
// SHUFFLE NEIGHBORHOOD
//
///////////////////////////////////////////////////////////////////////////////
class ShuffleNHoodI : public IloNHoodI {
private:
  IloNumVar _costVar;
  IloNum _prob;
 
  CriticalPath _cp;
 
  IloSchedulerSolution _delta;
  IloRandom _randGen;
 
public:
  ShuffleNHoodI(IloEnv env, IloNumVar cost, 
		IloNum probability, const char *name = 0)
    : IloNHoodI (env, name),
      _costVar(cost), _prob(probability),
      _cp(env), _delta(0), _randGen(env, 98998) {}
  ~ShuffleNHoodI() {}
 
  void        start       (IloSolver solver, IloSolution soln);
 
  IloSolution define      (IloSolver solver, IloInt) {
    assert(_delta.getImpl() != 0);
    assert(_delta.getSolution().getImpl() != 0);
    return _delta.getSolution().makeClone(solver.getEnv());
  }
 
  IloInt      getSize     (IloSolver) const { return 1; }
};
 
void ShuffleNHoodI::start(IloSolver solver, IloSolution soln) {
  IloSchedulerSolution solution(soln);
 
  IloEnv env = solver.getEnv();
 
  IloInt cpSize = 0;
  IloArray<IloResourceConstraint> cpArray = _cp.computeCP(solution, cpSize);
  
  // Create the move
  if (_delta.getImpl() != 0)
    _delta.end();
  _delta = solution.makeClone(env);
 
  // For each edge in the critical path (between resource constraints
  // on the same resource) keep it in the solution with probability 
  // (1 - _prob)
  for(IloInt i = 0; i < cpSize - 1; ++i) {
    IloResourceConstraint before = cpArray[i];
    IloResourceConstraint after = cpArray[i + 1];
 
    if (solution.hasNextRC(before) &&
	(after.getImpl() == solution.getNextRC(before).getImpl()) &&
	(_randGen.getFloat() > _prob)) {
      // Remove resource constraint from delta. This has the effect of
      // ensuring that this edge will be in the next solution
      _delta.remove(before);
    }
  }
 
  // For everything remaining in the solution, get rid of the nexts.
  // This has the effect of leaving the next of each rc undefined and
  // so this defines the search space for the rescheduling goal.
  for(IloSchedulerSolution::ResourceConstraintIterator iter3(_delta);
      iter3.ok(); ++iter3) {
    IloResourceConstraint rc = *iter3;
    if (_delta.hasNextRC(rc))
      _delta.unsetNext(rc, _delta.getNextRC(rc));      
  }
 
  // Reset the lower bound of the costVar
 
  _delta.setMin(_costVar, 0);
}
 
 
IloNHood ShuffleNHood(IloEnv env, IloNumVar cost,
		      IloNum prob, const char *name = 0) {
  return new (env) ShuffleNHoodI(env, cost, prob, name);
}
 
 
///////////////////////////////////////////////////////////////////////////////
//
// PROBLEM SOLVING
//
///////////////////////////////////////////////////////////////////////////////
 
void FindInitialSolution(IloModel model, 
			 IloSchedulerSolution globalSolution,
			 IloSchedulerSolution lsSolution,
			 IloNumVar costVar) {
  IloEnv env = model.getEnv();
  IloSolver solver(model);
  IlcScheduler scheduler(solver);
 
  IloGoal g = IloRankForward(env) && IloInstantiate(env, costVar);
  solver.startNewSearch(g);
 
  if(solver.next()) {
    IlcInt best = (IlcInt)solver.getValue(costVar);
    solver.out() << "Solution for Cost " <<  best << endl;
    solver.printInformation();
    globalSolution.store(scheduler);
    lsSolution.store(scheduler);
  }
 
  solver.endSearch();
  solver.end();
}
 
void
SolveModel(IloModel model,
	   IloNumVar makespan,
	   IloSchedulerSolution& globalSolution)
{
  IloEnv env = model.getEnv();
  IloObjective obj = IloMinimize(env, makespan);
  globalSolution.getSolution().add(makespan);
 
  /* CREATE LOCAL SEARCH SOLUTION */
  IloSchedulerSolution lsSolution = globalSolution.makeClone(env);
 
  for (IloIterator <IloResourceConstraint> iter(env); iter.ok(); ++iter) 
    lsSolution.add(*iter, IloRestoreRCNext);
 
  lsSolution.getSolution().add(obj);
 
  /* GENERATE AN INITIAL SOLUTION. */
  FindInitialSolution(model, globalSolution, lsSolution, makespan);
  IloNum best = globalSolution.getMax(makespan);
  env.out() << "Initial solution:" << endl;
  PrintSolution(globalSolution);
 
  /* LOCAL SEARCH */
  IloSolver lsSolver(model);
  IlcScheduler lsScheduler(lsSolver);
 
  IloNHood nhood = ShuffleNHood(env, makespan, 0.2);
  IloGoal reschedGoal = IloLimitSearch(env,
				       IloTextureSuccessorGoal(env) && 
				       IloInstantiate(env, makespan),
				       IloFailLimit(env, 100));
  IloGoal move = IloSingleMove(env, 
			       lsSolution,
			       nhood,
			       reschedGoal);
 
  IloInt maxIter = 100;
  for(IloInt i = 0; i < maxIter; ++i) {
    lsSolver.out() << "Move: " << i << ":\t";
    if (!lsSolver.solve(move))
      lsSolver.out() << "no solution" << endl;     
    else {
      IloNum cost = lsSolution.getSolution().getObjectiveValue();
      lsSolver.out() << "solution at cost: " << cost;
      if (cost < best) {
	globalSolution.store(lsScheduler);
	best = cost;
	lsSolver.out() << " **";
      }
      lsSolver.out() << endl;
    }
  }
 
  env.out() << "Final solution Cost: " << best << endl;
 
  PrintSolution(globalSolution);
  lsSolver.end();
}
 
///////////////////////////////////////////////////////////////////////////////
//
// MAIN FUNCTION
//
///////////////////////////////////////////////////////////////////////////////
 
void
InitParameters(int argc,
               char** argv,
               IloInt& numberOfJobs,
               IloInt& numberOfResources,                    
               IloInt*& resourceNumbers,
               IloInt*& durations)
{
  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;
    }
  }
}
 
int main(int argc, char** argv)
{
  try {
    IloEnv env;
 
    IloInt numberOfJobs = 6;
    IloInt numberOfResources = 6;
    IloInt* resourceNumbers = ResourceNumbers06;
    IloInt* durations = Durations06;
    InitParameters(argc,
                   argv,
                   numberOfJobs,
                   numberOfResources,
                   resourceNumbers,
                   durations);
    IloNumVar makespan;
    IloSchedulerSolution solution;
    IloModel model = DefineModel(env,
                                 numberOfJobs,
                                 numberOfResources,
                                 resourceNumbers,
                                 durations,
                                 makespan,
                                 solution);
 
    SolveModel(model,
	       makespan,
	       solution);
    env.end();
  }
  catch (IloException& exc) {
    cout << exc << endl;
  }
 
  return 0;
}