You can see the entire program resched2.cpp
here or view it online in the standard distribution.
#include <ilsched/iloscheduler.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};
///////////////////////////////////////////////////////////////////////////////
//
// TEXTURE CHOICE POINT FOR SCHEDULING AND RESCHEDULING
//
///////////////////////////////////////////////////////////////////////////////
class TextureGoalI : public IlcGoalI {
private:
IlcSchedule _schedule;
IloRandom _randGen;
protected:
IlcResourceTexture chooseResource();
IlcBool choosePrecedence(IlcResourceTexture,
IlcResourceConstraint&, IlcResourceConstraint&);
public:
TextureGoalI(IlcSchedule s, IloRandom r)
: IlcGoalI(s.getSolver()), _schedule(s), _randGen(r) { }
~TextureGoalI() {}
virtual IlcGoal execute();
};
IlcGoal TextureGoalI::execute() {
IlcResourceTexture selectedTexture = chooseResource();
while(0 != selectedTexture.getImpl()) {
IlcResourceConstraint rctA, rctB;
if (!choosePrecedence(selectedTexture, rctA, rctB)) {
selectedTexture.setNoCommitmentsAtCriticalPoint();
selectedTexture = chooseResource();
}
else
return IlcAnd(IlcTrySetSuccessor(rctA, rctB), this);
}
return 0;
}
IlcResourceTexture TextureGoalI::chooseResource() {
IlcResourceTexture selectedTexture = 0;
IlcInt nbNonzero = 0;
// Randomly choose a texture from all of those with non-zero max
// criticality.
for(IlcResourceIterator iter(_schedule); iter.ok(); ++iter) {
if ((*iter).isDiscreteResource()) {
IlcDiscreteResource res = (IlcDiscreteResource) (*iter);
IlcResourceTexture texture = res.getTextureMeasurement();
if (texture.getMaxCriticality() > 0) {
nbNonzero++;
if ((0 == selectedTexture.getImpl()) ||
(_randGen.getFloat() < 1/(IlcFloat) nbNonzero)) {
selectedTexture = texture;
}
}
}
}
return selectedTexture;
}
IlcBool TextureGoalI::choosePrecedence(IlcResourceTexture texture,
IlcResourceConstraint& before,
IlcResourceConstraint& after) {
// Find a pair of unsequenced resource constraints which minimize a
// measure of temporal slack due to:
//
// Laborie, P. and Ghallab, M.
// Planning with Sharable Resource Constraints
// Proceedings of the 14th International Joint Conference on
// Artificial Intelligence, IJCAI-95, 1995
before = 0;
after = 0;
IlcRCTextureArray rcArray = texture.getCriticalityOrderedRCTextures();
IlcInt nbRCs = rcArray.getSize();
IlcFloat minCommitVal = IlcFloatMax;
for(IlcInt i = 0; (i < nbRCs - 1) &&
(rcArray[i].getCriticalContribution() > 0); ++i) {
IlcResourceConstraint ctI = (rcArray[i]).getResourceConstraint();
IlcInt startMinI = ctI.getActivity().getStartMin();
IlcInt startMaxI = ctI.getActivity().getStartMax();
IlcInt endMinI = ctI.getActivity().getEndMin();
IlcInt endMaxI = ctI.getActivity().getEndMax();
for(IlcInt j = i+1; (j < nbRCs) &&
(rcArray[j].getCriticalContribution() > 0); ++j) {
IlcResourceConstraint ctJ = (rcArray[j]).getResourceConstraint();
if (!ctI.isSucceededBy(ctJ) && !ctJ.isSucceededBy(ctI)) {
IlcInt startMinJ = ctJ.getActivity().getStartMin();
IlcInt startMaxJ = ctJ.getActivity().getStartMax();
IlcInt endMinJ = ctJ.getActivity().getEndMin();
IlcInt endMaxJ = ctJ.getActivity().getEndMax();
// calculate I before J
IlcInt denom = (endMaxI - endMinI) + (startMaxJ - startMinJ);
IlcInt numer;
IlcFloat commitVal = 1;
if ((endMinI <= startMaxJ) && (denom > 0)) {
numer = (IlcMin(endMaxI, startMaxJ) - endMinI) +
(startMaxJ - IlcMax(startMinJ, endMinI));
commitVal = 1 - IlcMax(((IlcFloat)numer/(IlcFloat)denom), 0);
if (commitVal < minCommitVal) {
minCommitVal = commitVal;
before = ctI;
after = ctJ;
}
}
// calculate J before I
denom = (endMaxJ - endMinJ) + (startMaxI - startMinI);
if ((endMinJ <= startMaxI) && (denom > 0)) {
numer = (IlcMin(endMaxJ, startMaxI) - endMinJ) +
(startMaxI - IlcMax(startMinI, endMinJ));
commitVal = 1 - IlcMax(((IlcFloat)numer/(IlcFloat)denom), 0);
if (commitVal < minCommitVal) {
minCommitVal = commitVal;
before = ctJ;
after = ctI;
}
}
}
}
}
return ((before.getImpl() != 0) && (after.getImpl() != 0));
}
ILCGOAL1(AssignESTArray,
IlcIntVarArray, stArray) {
IloSolver solver = getSolver();
IlcInt size = stArray.getSize();
for(IlcInt i = 0; i < size; ++i)
stArray[i].setValue(stArray[i].getMin());
return 0;
}
ILOCPGOALWRAPPER2(TextureGoal, solver, IloNumVar, cost, IloRandom, rand) {
IlcScheduler scheduler(solver);
IlcIntVarArray varArray(solver, scheduler.getNumberOfActivities()+1);
IlcInt index = 0;
for(IlcActivityIterator iter(scheduler); iter.ok(); ++iter)
varArray[index++] = (*iter).getStartVariable();
varArray[index] = solver.getIntVar(cost);
return IlcAnd(IlcGoal(new (solver.getHeap())
TextureGoalI(scheduler, rand)),
AssignESTArray(solver, varArray));
}
///////////////////////////////////////////////////////////////////////////////
//
// ORIGINAL MODEL DEFINITION
//
///////////////////////////////////////////////////////////////////////////////
void CreateOriginalModel(const IloEnv env,
IloNum resourceCapacity,
IloInt numberOfJobs,
IloInt numberOfResources,
IloInt* resourceNumbers,
IloNum* durations,
IloNumVar& makespan,
IloModel& model,
IloSchedulerSolution& solution)
{
IloRandom randGen(env, 8975324);
IloInt i, j, k;
IloInt numberOfActivities = numberOfJobs * numberOfResources;
char buffer[128];
model = IloModel(env);
solution = IloSchedulerSolution(env);
// CREATE THE MAKESPAN VARIABLE.
IloNum horizon = 0;
for (i = 0; i < numberOfActivities; i++)
horizon += durations[i];
makespan = IloNumVar(env, 0, horizon, ILOINT);
solution.getSolution().add(makespan);
// CREATE THE RESOURCES.
IloSchedulerEnv schedEnv(env);
schedEnv.getResourceParam().setCapacityEnforcement(IloHigh);
schedEnv.getResourceParam().setPrecedenceEnforcement(IloMediumHigh);
IloTextureParam textureParam = schedEnv.getTextureParam();
textureParam.setCriticalityCalculator(
IloProbabilisticCriticalityCalculator(env));
textureParam.setRCTextureFactory(IloRCTextureESTFactory(env));
textureParam.setRandomGenerator(randGen);
IloDiscreteResource *resources =
new (env) IloDiscreteResource[numberOfResources];
for (j = 0; j < numberOfResources; j++) {
sprintf(buffer, "R%ld", j);
resources[j] = IloDiscreteResource(env,resourceCapacity,buffer);
}
IloInt r;
for(r = 0; r < resourceCapacity; ++r) {
// CREATE THE ACTIVITIES.
k = 0;
for (i = 0; i < numberOfJobs; i++) {
IloActivity previousActivity;
for (j = 0; j < numberOfResources; j++) {
sprintf(buffer, "O%ldJ%ldS%ldR%ld", r, i, j, resourceNumbers[k]);
IloActivity activity(env, durations[k], buffer);
IloDiscreteResource resource = resources[resourceNumbers[k]];
model.add(activity.requires(resource, 1));
if (j != 0)
model.add(activity.startsAfterEnd(previousActivity));
solution.add(activity);
previousActivity = activity;
k++;
}
model.add( previousActivity.endsBefore(makespan) );
}
}
// SET THE OBJECTIVE
model.add(IloMinimize(env, makespan));
}
///////////////////////////////////////////////////////////////////////////////
//
// SOLVE THE MODEL (ORIGINAL OR SUBMODEL)
//
///////////////////////////////////////////////////////////////////////////////
IloBool SolveModel(const IloModel model, IloSchedulerSolution solution,
const IloNumVar optVar,
IloInt failLimit, IloNum& runningTime) {
IloEnv env = model.getEnv();
IloSolver solver(model);
IlcScheduler scheduler(solver);
IloBool solved = IloFalse;
// PROPAGATE TO FIND LOWER BOUND ON OPT-VAR
solver.solve(IloGoalTrue(env));
IlcInt minOpt = solver.getIntVar(optVar).getMin();
solver.out() << "Initial propagation lower-bound optimization value: "
<< minOpt << endl;
IloGoal goal = TextureGoal(env, optVar, IloRandom(env, 45874052));
IloInt nbOfFails = 0;
runningTime = 0;
IloBool foundOpt = IloFalse;
IloInt localFailLimit = 1;
IloNum bestOptVal = optVar.getUB();
IloNum lastBestOptVal = bestOptVal;
IloConstraint optVarConstraint;
while(!foundOpt && (localFailLimit > 0)) {
IloGoal limitedGoal = IloLimitSearch(env, goal,
IloFailLimit(env, localFailLimit));
solver.startNewSearch(limitedGoal);
while(!foundOpt && solver.next()) {
bestOptVal = solver.getIntVar(optVar).getValue();
solver.out() << "Solution with optimization value: "
<< bestOptVal << endl;
solution.store(scheduler);
if (bestOptVal == minOpt) {
solver.out() << "Locally optimal solution" << endl;
foundOpt = IloTrue;
}
solved = IloTrue;
}
runningTime += solver.getTime();
nbOfFails += solver.getNumberOfFails();
localFailLimit *= 2;
IloInt failsLeft = failLimit - nbOfFails;
if (localFailLimit > failsLeft)
localFailLimit = failsLeft;
solver.endSearch();
if (bestOptVal < lastBestOptVal) {
if (optVarConstraint.getImpl() != 0)
model.remove(optVarConstraint);
optVarConstraint = (optVar < bestOptVal);
model.add(optVarConstraint);
lastBestOptVal = bestOptVal;
}
}
solver.out() << "Number of fails : " << nbOfFails
<< "\nRunning time : " << runningTime
<< endl;
solver.end();
if (optVarConstraint.getImpl() != 0)
model.remove(optVarConstraint);
return solved;
}
///////////////////////////////////////////////////////////////////////////////
//
// RESCHEDULING EVENT (ADD A NEW JOB)
//
///////////////////////////////////////////////////////////////////////////////
void AddNewJob(IloModel model,
IloInt newJobNumber,
IloResourceConstraint *rcArray,
IloNumVar makespan,
IloInt numberOfJobs,
IloInt numberOfResources,
IloNum *existingDurations) {
IloEnv env = model.getEnv();
IloRandom randGen(env, 98303001);
// CREATE DURATIONS
IloNum minDur = IloInfinity;
IloNum maxDur = -IloInfinity;
IloInt i;
for(i = 0; i < numberOfJobs * numberOfResources; ++i) {
if (existingDurations[i] < minDur)
minDur = existingDurations[i];
if (existingDurations[i] > maxDur)
maxDur = existingDurations[i];
}
IloNum diff = maxDur - minDur + 1;
IloNumArray durations(env, numberOfResources);
for(i = 0; i < numberOfResources; ++i)
durations[i] = randGen.getInt(IloTrunc(diff)) + minDur;
// CREATE RESOURCE USAGE
IloArray<IloDiscreteResource> resources(env, numberOfResources);
i = 0;
for(IloIterator<IloDiscreteResource> iter(env); iter.ok(); ++iter) {
resources[i] = *iter;
++i;
}
// RANDOMLY PERMUTE RESOURCE REQUIREMENTS
for(i = 0; i < 1000; ++i) {
IloInt index1 = randGen.getInt(numberOfResources);
IloInt index2 = randGen.getInt(numberOfResources);
while (index1 == index2)
index2 = randGen.getInt(numberOfResources);
IloDiscreteResource tmp = resources[index1];
resources[index1] = resources[index2];
resources[index2] = tmp;
}
// CREATE THE ACTIVITIES
IloActivity previousActivity;
char buffer[128];
for(i = 0; i < numberOfResources; ++i) {
sprintf(buffer, "NJ%ldS%ld%s", newJobNumber, i, resources[i].getName());
IloActivity activity(env, durations[i], buffer);
rcArray[i] = activity.requires(resources[i], 1);
model.add(rcArray[i]);
if (i != 0)
model.add(activity.startsAfterEnd(previousActivity));
previousActivity = activity;
}
model.add(previousActivity.endsBefore(makespan));
randGen.end();
}
///////////////////////////////////////////////////////////////////////////////
//
// DEFINE AND SOLVE TEMPORAL MODEL
//
///////////////////////////////////////////////////////////////////////////////
void
CreateTemporalModel(IloSchedulerSolution originalSolution,
IloResourceConstraint *newJob,
IloInt numberOfResources,
IloNumVar makespan,
IloModel& temporalModel,
IloSchedulerSolution& temporalSolution) {
IloEnv env = originalSolution.getEnv();
temporalModel = IloModel(env);
temporalSolution = IloSchedulerSolution(env);
temporalSolution.add(makespan);
// CALCULATE MAXIMUM INCREASE IN MAKESPAN FROM THE NEW JOB
IloNum maxMakespanIncrease = 0;
IloInt i;
for(i = 0; i < numberOfResources; ++i)
maxMakespanIncrease +=
newJob[i].getActivity().getProcessingTimeVariable().getUB();
temporalModel.add(makespan <
originalSolution.getSolution().getValue(makespan) +
maxMakespanIncrease);
// Put all the precedence constraints from the original model into
// the temporal model but not the resource constraints.
for (IloIterator<IloPrecedenceConstraint> pcIter(env);
pcIter.ok();
++pcIter)
{
IloPrecedenceConstraint pc = (*pcIter);
IloActivity nextAct = pc.getFollowingActivity();
IloActivity prevAct = pc.getPrecedingActivity();
temporalModel.add(pc);
if (!temporalSolution.contains(nextAct))
temporalSolution.add(nextAct);
if (!temporalSolution.contains(prevAct))
temporalSolution.add(prevAct);
if (originalSolution.contains(prevAct))
temporalModel.add(prevAct.startsAfter(
originalSolution.getStartMin(prevAct)));
if (originalSolution.contains(nextAct))
temporalModel.add(nextAct.startsAfter(
originalSolution.getStartMin(nextAct)));
}
// search for the last activity, and add
// its time-bound constraint over the makespan.
for (IloIterator<IloTimeBoundConstraint> tbcIter(env);
tbcIter.ok();
++tbcIter) {
IloTimeBoundConstraint tbc = (*tbcIter);
if (tbc.getType() == IloEndsBefore)
temporalModel.add(tbc);
}
}
void CreateAndSolveTemporalModel(IloSchedulerSolution originalSolution,
IloResourceConstraint *newJob,
IloInt numberOfResources,
IloNumVar makespan,
IloSolver& temporalSolver,
IloModel& temporalModel,
IloSchedulerSolution& temporalSolution) {
CreateTemporalModel(originalSolution,
newJob,
numberOfResources,
makespan,
temporalModel,
temporalSolution);
IloEnv env = originalSolution.getEnv();
// SOLVE TEMPORAL MODEL (PROPAGATE)
temporalSolver = IloSolver(temporalModel);
if (!temporalSolver.solve(IloGoalTrue(env)))
throw IloSchedulerException( "No solution for temporal model." );
IlcScheduler temporalScheduler(temporalSolver);
temporalSolution.store(temporalScheduler);
IloInt i;
for(i = 0; i < numberOfResources; ++i) {
IloActivity act = newJob[i].getActivity();
env.out() << "\t" << act.getName() << "["
<< temporalSolution.getStartMin(act)
<< ".." << temporalSolution.getStartMax(act)
<< " -- " << act.getProcessingTimeVariable().getLB()
<< " --> " << temporalSolution.getEndMin(act)
<< ".." << temporalSolution.getEndMax(act)
<< "]" << endl;
}
env.out() << "Maximum new makespan: "
<< temporalSolution.getSolution().getMax(makespan) << endl;
}
///////////////////////////////////////////////////////////////////////////////
//
// UPDATE TEMPORAL MODEL
//
///////////////////////////////////////////////////////////////////////////////
void UpdateTemporalModel(IloModel tModel,
IloSolver tSolver,
IloSchedulerSolution tSolution,
IloSchedulerSolution subSolution) {
// ADD THE START TIMES IN tSolution TO THE TEMPORAL MODEL
for(IloSchedulerSolution::ActivityIterator iter(subSolution);
iter.ok(); ++iter) {
IloActivity act = *iter;
tModel.add(act.startsAt(subSolution.getStartMin(act)));
}
if (!tSolver.solve(IloGoalTrue(tSolver.getEnv())
, IloSynchronizeAndContinue
)
)
throw IloSchedulerException( "No solution for temporal model" );
IlcScheduler scheduler(tSolver);
tSolution.store(scheduler);
}
///////////////////////////////////////////////////////////////////////////////
//
// CREATE THE DEFAULT SOLUTION
//
///////////////////////////////////////////////////////////////////////////////
IloSchedulerSolution CreateDefaultSolution(
IloSchedulerSolution originalSolution,
IloResourceConstraint* newJob,
IloNumVar makespan,
IloInt numberOfResources) {
// The simplest way to add the new job is simply to place it at the
// end of the existing schedule.
IloEnv env = originalSolution.getEnv();
IloNum newActStartMin = 0;
IloSchedulerSolution defaultSolution = originalSolution.makeClone(env);
for(IloInt i = 0; i < numberOfResources; ++i) {
IloResource resource = newJob[i].getResource();
// FIND THE ACTIVITY ON THE RESOURCE WITH THE MAX endMin
for(IloIterator<IloResourceConstraint> iter(env);
iter.ok(); ++iter) {
IloResourceConstraint rct = *iter;
if ((rct.getImpl() != newJob[i].getImpl()) &&
(rct.getResource().getImpl() == resource.getImpl()) &&
(defaultSolution.getEndMin(rct.getActivity()) > newActStartMin))
newActStartMin = defaultSolution.getEndMin(rct.getActivity());
}
IloActivity newAct = newJob[i].getActivity();
defaultSolution.add(newAct);
defaultSolution.setStartMin(newAct, newActStartMin);
newActStartMin += newAct.getProcessingTimeVariable().getLB();
}
IloNum mkspValue = defaultSolution.getSolution().getMin(makespan);
if (mkspValue < newActStartMin) {
defaultSolution.getSolution().setMin(makespan, newActStartMin);
defaultSolution.getSolution().setMax(makespan, newActStartMin);
}
return defaultSolution;
}
///////////////////////////////////////////////////////////////////////////////
//
// SUB-MODEL DEFINITION
//
///////////////////////////////////////////////////////////////////////////////
void
CreateSubmodel(IloSchedulerSolution temporalSolution,
IloModel& subModel,
IloSchedulerSolution& subSolution,
IloNumVar& optCriteria,
IloSchedulerSolution originalSolution,
IloResourceConstraint newRC) {
IloEnv env = temporalSolution.getEnv();
subModel = IloModel(env);
subSolution = IloSchedulerSolution(env);
IloActivity newAct = newRC.getActivity();
IloDiscreteResource reschedRes = newRC.getResource();
// GET THE TIME BOUNDS OF THE TO-BE-ADDED ACTIVITY
IloNum minTime = temporalSolution.getStartMin(newAct);
IloNum maxTime = temporalSolution.getEndMax(newAct);
env.out() << "Resource " << reschedRes.getName()
<< " on interval: [" << minTime << ".." << maxTime << "]"
<< endl;
// ADD THE NEW TIME BOUNDS AND RESOURCE CONSTRAINT
subModel.add(newAct.startsAfter(minTime));
subModel.add(newAct.endsBefore(maxTime));
subModel.add(newRC);
subSolution.add(newAct);
env.out() << "Adding " << newAct.getName() << endl;
IloNumVar maxEnd(env, minTime, maxTime, ILOINT);
subSolution.add(maxEnd);
subModel.add(newAct.endsBefore(maxEnd));
IloSchedulerEnv schedEnv(env);
IloNumToNumStepFunction initialOcc = schedEnv.getInitialOccupationParam();
initialOcc.setValue(initialOcc.getDefinitionIntervalMin(),
initialOcc.getDefinitionIntervalMax(), 0);
IloInt nbActsInSubmodel = 0;
IloNumVarArray hasChanged(env);
// ITERATE OVER THE RESOURCE CONSTRAINTS
for(IloIterator<IloResourceConstraint> iter(env); iter.ok(); ++iter) {
IloDiscreteResource res = (*iter).getResource();
IloActivity act = (*iter).getActivity();
if ((res.getImpl() == reschedRes.getImpl()) &&
(act.getImpl() != newAct.getImpl())) {
IloNum actStart = temporalSolution.getStartMin(act);
IloNum actEnd = temporalSolution.getEndMin(act);
if ((actStart >= minTime) && (actEnd <= maxTime)) {
// ADD TO MODEL THOSE ACTIVITIES HAVE A [MIN-START MIN-END]
// INTERVAL WHOLLY WITHIN THE INTERVAL OF THE NEW ACTIVITY
subModel.add((*iter));
subModel.add(act.startsAfter(actStart));
subModel.add(act.endsBefore(temporalSolution.getEndMax(act)));
subModel.add(act.endsBefore(maxEnd));
subSolution.add(act);
++nbActsInSubmodel;
hasChanged.add(IloNumVar(act.startsAfter(originalSolution.getStartMin(act)+1)));
}
else if ((actEnd > minTime) && (actStart < maxTime)) {
// FOR THE ACTIVITIES PARTIALLY IN THE INTERVAL, ADD THEM AS
// INITIAL OCCUPATION ON THE RESOURCE
actStart = IloMax(actStart, minTime);
actEnd = IloMin(actEnd, maxTime);
initialOcc.addValue(actStart, actEnd, (*iter).getCapacity());
}
}
}
// SET OPTIMIZATION CRITERIA TO BE A COMBINATION OF THE (LOCAL)
// MAKESPAN AND THE CHANGES FROM THE ORIGINAL MODEL
optCriteria = IloNumVar(env, 0, IloIntMax, ILOINT);
subModel.add(optCriteria == 1000 * maxEnd + IloSum(hasChanged));
subModel.add(IloMinimize(env, optCriteria));
}
IloBool CreateAndSolveSubmodel(IloSchedulerSolution temporalSolution,
IloSchedulerSolution& subSolution,
IloSchedulerSolution originalSolution,
IloResourceConstraint newRC,
IloNum& runningTime)
{
IloNumVar optCriteria;
IloModel subModel;
CreateSubmodel(temporalSolution, subModel, subSolution,
optCriteria, originalSolution, newRC);
IloInt failLimit = 1000;
IloBool solved = SolveModel(subModel, subSolution, optCriteria,
failLimit, runningTime);
// UNSET THE INITIAL OCCUPATION (MAY HAVE BEEN MODIFIED IN SUBMODEL)
IloSchedulerEnv schedEnv(temporalSolution.getEnv());
IloNumToNumStepFunction initialOcc = schedEnv.getInitialOccupationParam();
initialOcc.setValue(initialOcc.getDefinitionIntervalMin(),
initialOcc.getDefinitionIntervalMax(), 0);
//subModel.end();
return solved;
}
///////////////////////////////////////////////////////////////////////////////
//
// COUNT CHANGES IN START TIME ASSIGNMENTS
//
///////////////////////////////////////////////////////////////////////////////
IloInt CountChanges(IloSchedulerSolution originalSolution,
IloSchedulerSolution currentSolution) {
IloInt nbChanges = 0;
for(IloSchedulerSolution::ActivityIterator iter(currentSolution);
iter.ok(); ++iter) {
IloActivity act = *iter;
if (originalSolution.contains(act) &&
(originalSolution.getStartMin(act) !=
currentSolution.getStartMin(act)))
++nbChanges;
}
return nbChanges;
}
///////////////////////////////////////////////////////////////////////////////
//
// 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(const IloSchedulerSolution solution,
const IloNumVar makespan)
{
IloEnv env = solution.getEnv();
env.out() << "Solution with makespan ["
<< solution.getSolution().getMin(makespan) << ".."
<< solution.getSolution().getMax(makespan) << "]" << endl;
for (IloSchedulerSolution::ActivityIterator iter(solution);
iter.ok();
++iter)
{
IloActivity activity = *iter;
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() << "]" << endl;
}
}
void PrintSolutionComparison(IloSchedulerSolution currentSolution,
IloSchedulerSolution previousSolution,
IloSchedulerSolution originalSolution,
IloNumVar makespan) {
IloInt incrementalChanges = CountChanges(previousSolution,
currentSolution);
IloInt totalChanges = CountChanges(originalSolution,
currentSolution);
IloNum incrementalPercentIncrease =
(IloNum(currentSolution.getSolution().getValue(makespan)) -
IloNum(previousSolution.getSolution().getValue(makespan))) /
IloNum(previousSolution.getSolution().getValue(makespan)) * 100;
IloNum totalPercentIncrease =
(IloNum(currentSolution.getSolution().getValue(makespan)) -
IloNum(originalSolution.getSolution().getValue(makespan))) /
IloNum(originalSolution.getSolution().getValue(makespan)) * 100;
IloEnv env = currentSolution.getEnv();
env.out() << "\n\nNew job successfully added.\n\tTotal makespan: "
<< currentSolution.getSolution().getValue(makespan)
<< "\n\t% makespan increase (incremental) : "
<< incrementalPercentIncrease << "%"
<< "\n\t% makespan increase (overall) : "
<< totalPercentIncrease << "%"
<< "\n\tNumber of changes (incremental) : "
<< incrementalChanges
<< "\n\tNumber of changes (overall) : "
<< totalChanges << "\n" << endl;
}
void PrintFinalSolutionComparison(IloSchedulerSolution fromScratchSolution,
IloNum fromScratchSolveTime,
IloSchedulerSolution incSolution,
IloNum incSolveTime,
IloSchedulerSolution origSolution,
IloNum origSolveTime,
IloNumVar makespan,
IloNum nbOrigActs) {
// PRINT COMPARISON OF THE SOLVE-FROM-SCRATCH VS INCREMENTAL SOLVES
IloInt incChanges = CountChanges(origSolution,
incSolution);
IloInt scratchChanges = CountChanges(origSolution,
fromScratchSolution);
IloNum incPercentIncrease =
(IloNum(incSolution.getSolution().getValue(makespan)) -
IloNum(origSolution.getSolution().getValue(makespan))) /
IloNum(origSolution.getSolution().getValue(makespan)) * 100;
IloNum scratchPercentIncrease =
(IloNum(fromScratchSolution.getSolution().getValue(makespan)) -
IloNum(origSolution.getSolution().getValue(makespan))) /
IloNum(origSolution.getSolution().getValue(makespan)) * 100;
IloEnv env = origSolution.getEnv();
env.out() << "\nOriginal solve:"
<< "\nMakespan : "
<< origSolution.getSolution().getValue(makespan)
<< "\nRunning time : " << origSolveTime
<< "\n\nIncremental solves:"
<< "\nMakespan : "
<< incSolution.getSolution().getValue(makespan)
<< "\n% Makespan increase : " << incPercentIncrease << "%"
<< "\n% Activities changed : "
<< IloNum(incChanges) / nbOrigActs * 100 << "% ("
<< incChanges << ")"
<< "\nRunning time : " << incSolveTime
<< "\n\nSolve from scratch:"
<< "\nMakespan : "
<< fromScratchSolution.getSolution().getValue(makespan)
<< "\n% Makespan increase : " << scratchPercentIncrease << "%"
<< "\n% Activities changed : "
<< IloNum(scratchChanges) / nbOrigActs * 100 << "% ("
<< scratchChanges << ")"
<< "\nRunning time : " << fromScratchSolveTime
<< "\n" << endl;
}
///////////////////////////////////////////////////////////////////////////////
//
// SOLVE MODEL AFTER ADDING NEW JOB
//
///////////////////////////////////////////////////////////////////////////////
void ScheduleJob(IloModel evolvingModel,
IloSchedulerSolution originalSolution,
IloSchedulerSolution& evolvingSolution,
IloResourceConstraint* newJob,
IloNumVar makespan,
IloInt numberOfResources,
IloNum& incRunningTime) {
IloEnv env = evolvingModel.getEnv();
// CREATE DEFAULT SOLUTION IN CASE WE FAIL TO INCORPORATE NEW JOB
IloSchedulerSolution defaultSolution =
CreateDefaultSolution(evolvingSolution, newJob,
makespan, numberOfResources);
// CREATE AND PROPAGATE TEMPORAL MODEL INCLUDING NEW JOB
IloModel temporalModel;
IloSchedulerSolution temporalSolution;
IloSolver temporalSolver;
CreateAndSolveTemporalModel(evolvingSolution, newJob,
numberOfResources, makespan,
temporalSolver, temporalModel,
temporalSolution);
// SCHEDULE EACH ACTIVITY IN THE NEW JOB
IloInt i;
IloBool solved = IloTrue;
for(i = 0; (i < numberOfResources) && solved; ++i) {
env.out() << "\n**** Submodel: " << i << " ****" << endl;
IloSchedulerSolution subSolution;
IloNum subRunningTime;
solved = CreateAndSolveSubmodel(temporalSolution,
subSolution, evolvingSolution,
newJob[i], subRunningTime);
incRunningTime += subRunningTime;
if (solved) {
// UPDATE TEMPORAL MODEL WITH SUBMODEL SOLUTION
UpdateTemporalModel(temporalModel, temporalSolver,
temporalSolution, subSolution);
IloInt nbChanges = CountChanges(evolvingSolution,
subSolution);
env.out() << "Subproblem solved.\n\tTotal makespan: "
<< temporalSolution.getMin(makespan)
<< "\n\tNumber of changes in sub-solution: "
<< nbChanges << endl;
}
else {
env.out() << "Failure to solve subproblem! "
<< "Reverting to default solution." << endl;
}
subSolution.end();
}
if (solved) {
// ASSIGN THE MAKESPAN TO MIN VALUE
temporalSolution.getSolution().setMax(makespan,
temporalSolution.getSolution().getMin(makespan));
PrintSolutionComparison(temporalSolution, evolvingSolution,
originalSolution, makespan);
evolvingSolution.end();
evolvingSolution = temporalSolution.makeClone(env);
}
else {
// ONE OF THE SUBMODELS COULD NOT BE SOLVED WITHIN THE FAILURE
// LIMITS. FALL BACK TO THE DEFAULT SOLUTION
PrintSolutionComparison(defaultSolution, evolvingSolution,
originalSolution, makespan);
evolvingSolution.end();
evolvingSolution = defaultSolution.makeClone(env);
}
temporalSolution.end();
temporalSolver.end();
defaultSolution.end();
}
///////////////////////////////////////////////////////////////////////////////
//
// PARAMETERS
//
///////////////////////////////////////////////////////////////////////////////
void InitParameters(int argc,
char** argv,
IloNum& resourceCap,
IloInt& numberOfJobs,
IloInt& numberOfResources,
IloInt*& resourceNumbers,
IloNum*& durations)
{
resourceCap = 3;
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;
}
}
if (argc > 2)
resourceCap = atol(argv[2]);
}
///////////////////////////////////////////////////////////////////////////////
//
// MAIN FUNCTION
//
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv)
{
IloNum resourceCapacity;
IloInt numberOfJobs;
IloInt numberOfResources;
IloInt* resourceNumbers;
IloNum* durations;
InitParameters(argc,
argv,
resourceCapacity,
numberOfJobs,
numberOfResources,
resourceNumbers,
durations);
try {
IloEnv env;
IloNumVar makespan;
env.out() << "\n**** Original Model ****" << endl;
IloSchedulerSolution originalSolution;
IloModel originalModel;
// DEFINE ORIGINAL MODEL
CreateOriginalModel(env, resourceCapacity,
numberOfJobs, numberOfResources,
resourceNumbers, durations, makespan,
originalModel, originalSolution);
// SOLVE ORIGINAL MODEL
IloInt failLimit = 5000;
IloNum originalRunningTime;
IloBool solved = SolveModel(originalModel, originalSolution,
makespan,
failLimit, originalRunningTime);
if (!solved)
throw IloSchedulerException( "No solution found to original model." );
PrintSolution(originalSolution, makespan);
IloNum incRunningTime = 0;
IloSchedulerSolution evolvingSolution = originalSolution.makeClone(env);
// ADD NEW JOBS, EVERY TIME A JOB IS ADDED, INCREMENTALLY RE-SOLVE
IloInt nbNewJobs = numberOfJobs;
IloResourceConstraint *newJob =
new (env) IloResourceConstraint[numberOfResources];
for(IloInt j = 0; j < nbNewJobs; ++j) {
env.out() << "\nAdding new job: NJ" << j << endl;
// CREATE NEW JOB
AddNewJob(originalModel, j, newJob, makespan,
numberOfJobs, numberOfResources, durations);
// INCORPORATE NEW JOB INTO SCHEDULE
ScheduleJob(originalModel, originalSolution, evolvingSolution,
newJob, makespan, numberOfResources,
incRunningTime);
}
// SOLVE COMPLETE MODEL (ORIGINAL + NEW JOBS) FROM SCRATCH
env.out() << "\n **** Complete Model ***" << endl;
IloSchedulerSolution totalSolution = evolvingSolution.makeClone(env);
IloNum totalRunningTime;
solved = SolveModel(originalModel, totalSolution,
makespan,
failLimit, totalRunningTime);
if (!solved)
throw IloSchedulerException( "No solution found for overall model." );
// COMPARE ORIGINAL SOLUTION TO INCREMENTAL AND FROM SCRATCH SOLUTIONS
IloNum nbOrigActs = numberOfJobs * numberOfResources * resourceCapacity;
PrintFinalSolutionComparison(totalSolution, totalRunningTime,
evolvingSolution, incRunningTime,
originalSolution, originalRunningTime,
makespan, nbOrigActs);
PrintSolution(totalSolution, makespan);
totalSolution.end();
evolvingSolution.end();
originalSolution.end();
env.end();
} catch (IloSchedulerException& exc) {
cout << exc << endl;
} catch (IloException& exc) {
cout << exc << endl;
}
return 0;
}