IBM ILOG Scheduler User's Manual > Integrated Applications > A Randomizing Algorithm: the Job-Shop Problem > Developing the Problem-Solving Algorithm > Optimizing the Makespan

Inside the function MinimizeRandom, the search goal SolveProblem is limited with the function IloLimitSearch. More precisely, the number of backtracks is limited to the value of numberOfFailsPerIteration, a parameter of the function MinimizeRandom.

ILCGOAL1(SolveProblemIlc, IloRandom, rand) {
  IloSolver s = getSolver();
  IlcScheduler scheduler = IlcScheduler(s);
  IlcResourceConstraint constraint;
  if (SelectResourceConstraint(constraint, scheduler, rand)) {
    return IlcAnd(IlcTryRankFirst(constraint),
                  this);
  }
  return 0;
}
 
ILOCPGOALWRAPPER1(SolveProblem, solver, IloRandom, rand) {
  return SolveProblemIlc(solver, rand);
}
 
void
MinimizeRandom(IloSolver& solver,
               IloNumVar& makespan,
               IloSchedulerSolution& solution,
               IloInt numberOfIterations,
               IloInt numberOfFailsPerIteration)
{
  IloEnv env = solver.getEnv();
  IlcScheduler sched(solver);
  IloRandom randGen(env);
  IloGoal goal;
 
  /* GENERATE AN INITIAL SOLUTION. */
  goal = IloGoalTrue(solver.getEnv());
  solver.solve(goal);
  IloNum min = solver.getMin(makespan);
  makespan.setLB(min);
 
  goal = SolveProblem(env, randGen);
  solver.solve(goal);
  IloNum max = solver.getMin(makespan);
 
  /* OPTIMIZE. */
  IloInt iteration = 0;
 
  goal = IloLimitSearch(env,
                        SolveProblem(env, randGen),
                        IloFailLimit(env, numberOfFailsPerIteration));
  
  while ((min != max) && (iteration < numberOfIterations)) {
    iteration++;
    IloNum value = IloTrunc((min + iteration * max) / (iteration + 1));
    makespan.setUB(value);
 
    solver.out() << "Trying makespan: " << value << endl;
 
    if (solver.solve(goal)) {
      max = solver.getMin(makespan);
      solver.out() << "Solution with makespan " << max << endl;
      solution.store(sched);
    }
 
    else /* ... */ 
  } 
  /* ... */ 
}
 

Remembering our experience with the dichotomizing binary search (in Chapter 16), at each iteration, we use a weighted sum of the minimal value min and maximal value max of the makespan variable as a new tentative bound for this constrained variable. Here, it would not be a good idea to systematically take the average of these two values as a new tentative makespan. In fact, it could be that no solution exists with a makespan of (min+max)/2, so taking the same tentative makespan at each iteration could lead to ignoring many good solutions to the scheduling problem.

Each iteration may fail either because there is no solution with a given makespan, or because the algorithm could not find such a solution in the given number of backtracks. By testing the number of failures (the value of solver.getNumberOfFails()) at the end of the iteration, we know whether the numberOfFailsPerIteration backtracks have been consumed or not. If not, the algorithm has proven that no solution exists with a makespan of at most the given value. Hence, it is correct to write the following code.

    if (solver.solve(goal)) {
      max = solver.getMin(makespan);
      solver.out() << "Solution with makespan " << max << endl;
      solution.store(sched);
    }
 
    else if (solver.getNumberOfFails() < numberOfFailsPerIteration) {
      /* ACTUAL FAILURE NOT DUE TO THE LIMITED NUMBER OF FAILS. */
      solver.out() << "Failure with makespan " << value << endl;
      min = value + 1;
    }
    else 
      solver.out() << "Failure from fail limit" << endl;
  }
 

Notice that the best solution is stored in an instance of the class IloSchedulerSolution.