Overview | Group | Tree | Graph | Index | Concepts |
This function creates and returns a goal that assigns a start
time to all activities managed by schedule
. If the
argument criterion
is given, then the assignments are
made to minimize criterion
. The activity selector
aSel
selects the next activity. By default, that is,
if no activity selector is given as an argument, the activity
selector used is defined as follows:
IloSelector<IlcActivity,IlcSchedule> aSel = IlcActivityInScheduleSelector(s); sel.setPredicate(!IlcActivityStartVarBoundPredicate(s) && !IlcActivityPostponedPredicate(s)); sel.setComparator(IlcLexicalComposition(IlcCompareMin(IlcActivityStartMinEvaluator(s)), IlcCompareMin(IlcActivityEndMaxEvaluator(s))));
The goal is designed to efficiently schedule activities in a chronological order. It considers only solutions that can be produced as follows: in each step, choose an unscheduled activity A of minimal earliest start time and schedule it as early as possible, as allowed by the previously scheduled activities (which have smaller start times than A).
Internally, the function uses a schedule-or-postpone method that works as follows:
Before assigning the earliest start time to an activity with a candidate start time st, it is determined whether an already postponed activity actP exists that can be or should be scheduled before st, that is, whether actP.getEndMin() <= st or actP.getStartMax() <= st. If this is the case, a fail is generated based on the reasoning that if we have “normal” precedence and resource constraints, the earliest start time of actP will never be removed and thus actP will remain postponed and no solution will be found in this branch of the search tree.
This reasoning can result in an incomplete search (that is, solutions may
be missed) in some situations. To take a specific example, suppose we have
an activity B
that can start as much as 50 units after the
start of an activity A
. This can be expressed by a precedence
constraint with negative delay:
A.startsAfterStart(B, -50)
Assume that B
cannot be scheduled at a time earlier than 100
because it requires a resource that has a maximal capacity of 0 in the
interval [0,100). A
, therefore, cannot be scheduled before time
50 even if there are no earlier activities. IlcSetTimes
will
not find a solution here, because it will attempt to assign A
to a start time of 0. When this fails, it will postpone A
but
then realize that there are no other activities that can be schedule before
A
and therefore it concludes that there are no solutions. Note
that in this very simple case, it is likely that constraint propagation will
discover that 50 is the actual earliest start time of A
and so
IlcSetTimes
will successfully find a solution. However, if this
situation is part of a larger, more complex constraint interaction, it
cannot be guaranteed that constraint propagation will discover the globally
consistent earliest start time. In such a situation, then, a solution will
be missed.
Another situation where missed solutions can arise is when the processing
time of an activity depends on its start or end time. For example, we have
three activities, A
, B
, and C
, all of
which require the same unary capacity resource. Activities B
and C
have processing times of 10. Activity A
has
a processing time of 1 if its start time is in the set {1, 2, 3, 4} and a
processing time of 10 otherwise. The scheduling horizon is 25 time units and
B
and C
can only start at or after time unit
1.
The Scheduler Concert Technology code below and the output
demonstrate that such a situation leads to a missed solution when
IlcSetTimes
(extracted from the IloSetTimesForward
goal) is used.
IloEnv env; IloModel model(env); IloSchedulerEnv schedEnv(env); schedEnv.setOrigin(0); schedEnv.setHorizon(25); IloNumVar ptA(env, 1, 10, ILOINT); IloActivity a(env, ptA); a.setName("A"); IloActivity b(env, 10, "B"); IloActivity c(env, 10, "C"); model.add(b.startsAfter(1)); model.add(c.startsAfter(1)); // processing time is 1 iff start time is in set {1, 2, 3, 4} // otherwise processing time is 10 IloNumVar stA = a.getStartVariable(); model.add((stA < 1) || (stA > 4) || (ptA == 1)); model.add(((stA >= 1) && (stA <= 4)) || (ptA == 10)); IloUnaryResource r(env); model.add(a.requires(r)); model.add(b.requires(r)); model.add(c.requires(r)); IloSolver solver(model); IloBool solved = solver.solve(IloSetTimesForward(env)); solver.out() << "First solve: " << solved << endl; model.add(a.startsAt(1)); solved = solver.solve(IloSetTimesForward(env)); solver.out() << "Solve after adding a.startsAt(1): " << solved << endl; Output: First solve: 0 Solve after adding a.startsAt(1): 1
What happens in this example is that the IlcSetTimes
goals
tries to assign activity A
to start time 0. This leads to a
dead-end because this implies that all three activities have a processing
time of 10 which cannot be accommodated on a unary resource within the 25
time-unit scheduling horizon. Activity A
is then postponed and
the goal attempts to assign B
to time 1. This fails, as all
three activities again would have to have processing times of 10. Finally,
the goal tries to schedule activity C
at time 1, with the same
result as when it tried activity B
. Therefore, the goal
concludes, there are no solutions.
Clearly, however, there are solutions, as demonstrated when we assign the
start time of A
to be 1. The processing time of A
is therefore 1 and the other two activities can then follow within the
scheduling horizon.
In general, IlcSetTimes
may miss solutions if there are
precedence constraints with negative delay, if the processing time of an
activity depends on its start or end time, or if reservoirs are used.
In particular, care should be taken in using precedence constraints with a negative delay, activities where the processing time depends on the start or end time of the activity, and reservoirs.
See IloSelector
in the IBM ILOG Solver Reference Manual for more information.
See Also:
IlcSchedule, IlcScheduleOrPostpone, IlcSetTimesBackward