@Chapter
    @Title { Ejection Chains }
    @Tag { eject }
@Begin
Ejection chains are sequences of repairs that generalize the augmenting
paths from bipartite matching.  They are credited to Glover
@Cite { $glover1996 }, who named them and applied them to the travelling
salesman problem.  Similar ideas, often not clearly expressed, appear
in a few earlier papers.
@PP
We begin by introducing ejection chains and @I { ejectors }, which are
KHE objects that supply a general framework for ejection chain solving.
File @C { khe_se_ejector.c }, which is unlikely to change much in the
future, implements this material.  Later in the chapter, we present
the more volatile code that KHE offers to make ejectors handle
specific kinds of defects.
# In timetabling,
# they have been applied to nurse rostering @Cite { $dowsland1998 },
# teacher assignment @Cite { $kingston2008resource }, and time repair
# @Cite { $kim1997 }.  Reference @Cite { $kim1997 } is very cryptic,
# unfortunately.
@BeginSections

@Section
    @Title { Introduction }
    @Tag { eject.intro }
@Begin
@LP
This section present ejection chains.  Some of its statements will
need to be qualified later, but they are close enough to the truth
to serve for purposes of introduction.
@PP
A @I defect is a specific point in a solution where something is
not perfect and incurs a non-zero cost.  Concretely, it is a
monitor (possibly a group monitor) with non-zero cost.
# More precisely, monitors have @I { lower bounds }, which are costs
# below which the monitors provably cannot go.  A defect is a monitor
# whose cost exceeds its lower bound.
@PP
An ejection chain algorithm iterates over the defects of a solution.
For each defect it tries a set of alternative @I { repairs }.  A repair
could be a simple move or swap, or something arbitrarily complex.  It
is targeted specifically at the defect, and usually removes it,
but it may introduce new defects.  If no new defects of significant
cost appear, that is success.  If just one significant new defect
appears, the algorithm calls itself recursively to try to remove
that defect, building up a chain of coordinated repairs.  If several
significant new defects appear, or the recursive call fails to remove
the new defect, the algorithm undoes the repair and continues with
alternative repairs.
# It can also try to remove all the new defects.
# , although that is not often useful.
@PP
Corresponding to the well-known function for finding an augmenting
path in a bipartite graph, starting from a given unassigned node,
is this function, formulated by the author, for `augmenting'
(improving) a solution, starting from a given defect:
@ID @C {
bool Augment(Solution s, Cost c, Defect d);
}
(KHE's interface is somewhat different to this.)  @C { Augment }
has precondition
@ID @C { cost(s) >= c  &&  cost(s) - cost(d) < c }
If @C { Augment } can change @C { s } to reduce its cost to less than
@C { c }, it does so and returns @C { true }; if not, it leaves @C { s }
unchanged and returns @C { false }.  Importantly, the precondition
implies that removing @C { d } without adding new defects would
succeed.  Here is an abstract implementation of @C { Augment }:
# (In practice the precondition also takes account of @C { d }'s
# lower bound.)
@ID -1px @Break @C {
bool Augment(Solution s, Cost c, Defect d)
{
  repair_set = RepairsOf(d);
  for( each repair r in repair_set )
  {
    new_defect_set = Apply(s, r);
    if( cost(s) < c )
      return true;
    for( each e in new_defect_set )
      if( cost(s) - cost(e) < c && Augment(s, c, e) )
        return true;
    UnApply(s, r);
  }
  return false;
}
}
It begins by finding a set of alternative ways that @C { d } could be
repaired.  For example, an unassigned task could be repaired by
assigning any suitable resource to it; an overloaded resource could be
repaired by reassigning or unassigning any task that contributes to
the overload; and so on.  In practice, these repairs don't have to be
placed into a set---they just need to be iterated over.  For each
repair @C { r }, @C { Augment } applies @C { r } and receives the set
of new defects introduced by @C { r }, looks for success in two ways,
then if neither of those works out it unapplies the repair and continues
by trying the next repair, returning @C { false } when all repairs have
been tried without success.
@PP
Success could come in two ways.  Either a repair reduces
@C { cost(s) } to below @C { c }, or some new defect @C { e } has
cost large enough to ensure that removing it alone would constitute
success, and a recursive call targeted at @C { e } succeeds.  Notice
that @C { cost(s) } may grow without limit as the chain deepens,
while there is a defect @C { e } whose removal would reduce the
solution's cost to below @C { c }.
@PP
The key observation that justifies the whole approach is this:
the new defects targeted by the recursive calls are not known to
have resisted attack before.  It might be possible to repair one
of them without introducing any new defects of significant cost.
@PP
The algorithm stops at the first successful chain.  An option
for finding the best successful chain rather than the first has
been withdrawn, because of problems in combining it with
ejection beams (Section {@NumberOf eject.variants.beams}).  It is no
loss:  it produced nothing remarkable, and ran slowly.
Another option, for limiting the disruption caused by the
repairs, has also been withdrawn.  It too was not very useful.
It can be approximated by limiting the maximum chain length,
as described next.
@PP
The tree searched by @C { Augment } as presented may easily
grow to exponential size, which is not the intention.  KHE
offers two methods of limiting its size, both of which seem
useful:  limit the maximum chain length to a fixed constant,
perhaps 3 or 4; and (following the lead of augmenting paths in
bipartite matching) refuse to revisit parts of the solution
already visited during the current repair.  They may be used
separately or together.  Section {@NumberOf eject.schedules}
has the details.
@PP
Given a solution and a list of its defects, the main loop cycles
through the list repeatedly, calling @C { Augment } on each
defect in turn, with @C { c } set to @C { cost(s) }.  When the
main loop exits, every defect has been tried at least once without
success since the most recent success, so no further successful
augments are possible, unless there is a random element within
@C { Augment }.  Under reasonable assumptions, this very clear-cut
stopping criterion ensures that the whole algorithm runs in
polynomial time, for the same reason that hill-climbing does.
@PP
Defects come in a variety of types, basically one for each monitor
type.  For example, a missing task assignment (an assign resource
monitor defect) is different from a resource overload (a limit busy
times or cluster busy times monitor defect).  Each type of defect
needs its own specialized set of repairs.  This idea seems to have
been first clearly articulated by the present author, who named the
result @I { polymorphic ejection chains }:  the first step in each
augment is a dynamic dispatch on the defect type (not shown above).
# The repairs are usually applied directly, rather than indirectly
# via objects built to represent them.
@PP
Careful work is needed to maximize the effectiveness of ejection
chains.  Grouping together monitors that measure the same thing
is important, because it reduces the number of defects and increases
their cost, increasing the chance that a chain will be continued.
Individual repair operations should actually remove the defects that
they are called to repair (the framework does not check this), and should
do whatever seems most likely to avoid introducing new defects.  We'll
consider these points in detail in Section {@NumberOf eject.practice}.
@End @Section

@Section
    @Title { Ejector construction }
    @Tag { eject.ejectors }
@Begin
@LP
KHE offers @I { ejector } objects which provide a framework for
ejection chain algorithms, reducing the implementation burden to
writing just the augment functions.
# The framework uses visit
# numbers (Section {@NumberOf "solutions.top.visit.numbers"}), in
# the conventional way.
# @PP
# To support statistics gathering (Section {@NumberOf ejection.statistics}),
# a single ejector object may be re-used many times, even on different
# instances (although not in parallel---an ejector object is highly
# mutable and cannot be shared by two or more threads).  This makes
# it important to distinguish between those parts of the ejector which
# are constant throughout its lifetime, and those parts which vary from
# solve to solve.  This section is concerned with the constant parts.
# @PP
An ejector object, stored in a given arena @C { a }, is constructed by
a sequence of calls beginning with
@ID @C {
KHE_EJECTOR KheEjectorMakeBegin(HA_ARENA a);
}
# KHE_EJECTOR KheEjectorMakeBegin(char *schedule, HA_ARENA a);
followed by calls which load augment functions, as explained below,
and ending with
@ID @C {
void KheEjectorMakeEnd(KHE_EJECTOR ej);
}
The ejector is then ready to do some solving
(Section {@NumberOf eject.solving}).  The arena attribute is returned by
@ID @C {
HA_ARENA KheEjectorArena(KHE_EJECTOR ej);
}
# char *KheEjectorSchedules(KHE_EJECTOR ej);
There is no function to delete an ejector; it is deleted when its
arena is deleted.
@PP
A mentioned earlier, in between calling @C { KheEjectorMakeBegin }
and @C { KheEjectorMakeEnd }, the augment functions need to be
loaded.  They are written by the user, as described in
Section {@NumberOf eject.practice}, and passed to the ejector by calls to
@ID @C {
void KheEjectorAddAugment(KHE_EJECTOR ej, KHE_MONITOR_TAG tag,
  KHE_EJECTOR_AUGMENT_FN augment_fn, int augment_type);
void KheEjectorAddGroupAugment(KHE_EJECTOR ej, int sub_tag,
  KHE_EJECTOR_AUGMENT_FN augment_fn, int augment_type);
}
The first says that defects which are non-group monitors with tag
@C { tag } should be handled by @C { augment_fn }; the second says
that defects which are group monitors with sub-tag @C { sub_tag }
should be handled by @C { augment_fn }.  Here @C { sub_tag } must
be between 0 and 29 inclusive.  Any values not set are handled
by doing nothing, as though an unsuccessful attempt was made to
repair them.  Ejectors handle the polymorphic dispatch by defect
type.  The @C { augment_type } parameter is used by statistics
gathering (Section {@NumberOf eject.statistics}), and may be
0 if statistics are not wanted.
@End @Section

@Section
    @Title { Ejector schedules }
    @Tag { eject.schedules }
@Begin
@LP
# The first method is to limit the maximum chain length to a fixed
# constant, perhaps 3 or 4.  The maximum length is passed as an
# extra parameter to @C { Augment }, and reduced on each
# recursive call, with value 0 preventing further recursion.
# Not only is this attractive in itself, it also supports
# @I { iterative deepening }, in which @C { Augment } is called
# several times on the same defect, with the length parameter
# increased each time.  Another idea is to use a small length on
# the first iteration of the main loop (see below), and increase
# it on later iterations.
# @PP
# The second method is used by augmenting paths
# in bipartite matching.  Just before each call on
# @C { Augment } from the main loop, the entire solution is
# marked unvisited (by incrementing a global visit number,
# not by traversing the entire solution).  When a repair
# changes some part of the solution, that part is marked
# visited.  Repairs that change parts of the solution that
# are already marked visited are tabu.  This limits the
# size of the tree to the size of the solution.
# @PP
Before we come to solving, we first need to explain ejector schedules.
@PP
An @I { ejector schedule }, or just @I { schedule }, is a string
which informs the ejector which method it should use to prevent
calls on @C { Augment } from a taking exponential time, as discussed
earlier.  The string consists of one or more @I { major schedules }
separated by semicolons:
@ID @C {
<major_schedule>;<major_schedule>; ... ;<major_schedule>
}
Each major schedule consists of one or more @I { minor schedules }
separated by commas:
@ID @C {
<minor_schedule>,<minor_schedule>, ... ,<minor_schedule>
}
Each minor schedule consists of a positive integer called its
@I { maximum length }, followed by either @C { "+" } or @C { "-" },
representing a Boolean value called its @I { may revisit }
attribute.  The maximum length may have the special value @C { "u" },
meaning unlimited.  For example, schedule string
@ID @C {
"1+;u-"
}
contains two major schedules.  The first, @C { "1+" }, has
one minor schedule, with 1 for maximum length and @C { true }
for may revisit; the second, @C { "u-" }, also has one
minor schedule, this time with unlimited for maximum length
and @C { false } for may revisit.
@PP
The entire main loop of the algorithm, which repeatedly tries to
augment out of each defect of the solution until no further
improvements can be found, is repeated once for each major schedule
in order.  Within each iteration of the main loop, the augment for
one defect is tried once for each minor schedule of the current
major schedule, until an augment succeeds in reducing the cost
of the solution or all minor schedules have been tried.
@PP
The maximum length determines the maximum number of repairs allowed
on one chain.  Value 0 allows no repairs at all and is forbidden.
Value 1 allows augment calls from the main loop, but prevents them
from making recursive calls, producing a kind of hill climbing.
Value 2 allows the calls made from the main loop to make recursive
calls, but prevents those calls from recursing.  And so on.
@PP
When the may revisit attribute is @C { false }, each part of the
solution may be changed by at most one of the recursive calls made
while repairing a defect; when it is @C { true }, each part may be
changed by any number of them, although only once along any one chain.
@PP
It is not good to set the maximum length to a large value and may
revisit to @C { true } in the same minor schedule, because the
algorithm will then usually take exponential time.  But setting
the maximum length to a small constant, or setting may revisit to
@C { false }, or both, guarantees polynomial time.  Another
interesting idea is @I { iterative deepening }, in which several
maximum lengths are tried.  For example,
@ID @C {
"1+;2+;3+;u-"
}
tries maximum length 1, then 2, then 3, and finishes with unlimited
length.
@End @Section

@Section
    @Title { Ejector solving }
    @Tag { eject.solving }
@Begin
@LP
Once an ejector has been set up, the ejection chain algorithm
may be run by calling
@ID @C {
bool KheEjectorSolve(KHE_EJECTOR ej, KHE_GROUP_MONITOR start_gm,
  KHE_GROUP_MONITOR continue_gm, char *schedule, KHE_AUGMENT_OPTIONS ao,
  KHE_OPTIONS options);
}
This runs the main loop of the ejection chain algorithm once for each
major schedule, returning @C { true } if it reduces the cost of the
solution monitored by @C { start_gm } and @C { continue_gm }.
@PP
The schedule (explained in the previous section) is @C { schedule }
if that is non-@C { NULL }, otherwise it is the value of the
@C { es_schedule } option from @C { options } if any, otherwise
it is @C { "1+,u-" }.
@PP
The main loop repairs only the defective child monitors of
@C { start_gm }, and the recursive calls repair only the
defective child monitors of @C { continue_gm }.  These two
group monitors could be equal, and either or both could be an
upcast solution.  Although it is not required, in practice
every child monitor of @C { start_gm } would also be a child monitor
of @C { continue_gm }.
@PP
Parameter @C { ao } contains options for the detailed control of augment
functions.  The ejector object does not know what these are; it just passes
@C { ao } back to the user's augment functions, which use it to control
their detailed behaviour (Section {@NumberOf eject.practice.augment}).
@PP
Just as an ejector is constructed by a sequence of calls enclosed
in @C { KheEjectorMakeBegin } and @C { KheEjectorMakeEnd },
so a solve is carried out by a sequence of calls beginning with
@ID @C {
void KheEjectorSolveBegin(KHE_EJECTOR ej, KHE_GROUP_MONITOR start_gm,
  KHE_GROUP_MONITOR continue_gm, char *schedule, KHE_AUGMENT_OPTIONS ao,
  KHE_OPTIONS options);
}
and ending with
@ID @C {
bool KheEjectorSolveEnd(KHE_EJECTOR ej);
}
@C { KheEjectorSolveEnd } does the actual solving.  Function
@C { KheEjectorSolve } above just calls @C { KheEjectorSolveBegin }
and @C { KheEjectorSolveEnd } with nothing in between.
@PP
The only functions callable between @C { KheEjectorSolveBegin }
and @C { KheEjectorSolveEnd } (more precisely, the only ones that
change anything) are
@ID @C {
void KheEjectorAddMonitorCostLimit(KHE_EJECTOR ej,
  KHE_MONITOR m, KHE_COST cost_limit);
void KheEjectorAddMonitorCostLimitReducing(KHE_EJECTOR ej,
  KHE_MONITOR m);
}
The call @C { KheEjectorAddMonitorCostLimit(ej, m, cost_limit) } says that
for a chain to end successfully, not only must the solution cost
be less than the initial cost, but @C { KheMonitorCost(m) } must
be no larger than @C { cost_limit }.
@C { KheEjectorAddMonitorCostLimitReducing(ej, m) } is the same
except that the cost limit is initialized to @C { KheMonitorCost(m) },
and if a successful chain is found and applied which reduces
@C { KheMonitorCost(m) } to below its current limit, that limit is
reduced to the new @C { KheMonitorCost(m) } for subsequent chains.
@PP
To visit these @I { limit monitors }, call
@ID @C {
int KheEjectorMonitorCostLimitCount(KHE_EJECTOR ej);
void KheEjectorMonitorCostLimit(KHE_EJECTOR ej, int i,
  KHE_MONITOR *m, KHE_COST *cost_limit, bool *reducing);
}
The returned values are the monitor, its current cost limit, and
whether that limit may be reduced.  Any number of limit monitors
may be added, but large numbers will not be efficient.
# Instead, a group monitor should be made out of
# them and its cost limited.  Although it is not quite the same
# thing, in practice it is just as good.  The same monitor cost
# limits apply to all schedules.
@PP
Each time the ejector enters the main loop, it copies @C { start_gm }'s
list of defects and sorts the copy by decreasing cost.  Ties are
broken differently depending on the value of the solution's
diversifier.  If the @C { es_limit_defects } option is set to
some integer @M { k } (not @C { "unlimited" }), defects are
dropped from the end of the sorted list to ensure that there
are no more than @M { k } of them.
@PP
Consider a defect @M { d } that the main loop of the ejection chain
solver is just about to attempt to repair.  Suppose that the most
recent change either to the solution or to the major schedule occurred
before the most recent previous attempt to repair @M { d }.  Then, if
the repair is deterministic, the current attempt to repair @M { d }
is certain to fail like the previous attempt did.  Accordingly, it
is skipped.  The implementation of this optimization uses visit
numbers stored in monitors.
@PP
In practice, repairs are not deterministic, since, for diversity,
KHE's augment functions vary the starting points of their traversals
of lists of repairs between calls.  However, the author carried out an
experiment on a large instance (NL-KP-03), in which this optimization
was turned off but a check was made to see whether there were any
cases where repairs which it would have caused to be skipped were
successful.  Over 8 diversified solves there were 15 cases.
@PP
The following functions may be called while @C { KheEjectorSolve }
is running (that is, from within augment functions):
@ID @C {
KHE_GROUP_MONITOR KheEjectorStartGroupMonitor(KHE_EJECTOR ej);
KHE_GROUP_MONITOR KheEjectorContinueGroupMonitor(KHE_EJECTOR ej);
KHE_OPTIONS KheEjectorOptions(KHE_EJECTOR ej);
KHE_SOLN KheEjectorSoln(KHE_EJECTOR ej);
KHE_COST KheEjectorTargetCost(KHE_EJECTOR ej);
bool KheEjectorCurrMayRevisit(KHE_EJECTOR ej);
int KheEjectorCurrLength(KHE_EJECTOR ej);
int KheEjectorCurrAugmentCount(KHE_EJECTOR ej);
bool KheEjectorCurrDebug(KHE_EJECTOR ej);
int KheEjectorCurrDebugIndent(KHE_EJECTOR ej);
}
# KHE_FRAME KheEjectorFrame(KHE_EJECTOR ej);
# KHE_EVENT_TIMETABLE_MONITOR KheEjectorEventTimetableMonitor(KHE_EJECTOR ej);
# KHE_EJECTOR_MAJOR_SCHEDULE KheEjectorCurrMajorSchedule(KHE_EJECTOR ej);
# KHE_EJECTOR_MINOR_SCHEDULE KheEjectorCurrMinorSchedule(KHE_EJECTOR ej);
# int KheEjectorCurrDisruption(KHE_EJECTOR ej);
# KHE_TIME_PA RTITION KheEjectorTimePartition(KHE_EJECTOR ej);
{0.95 1.0} @Scale @C { KheEjectorStartGroupMonitor },
{0.95 1.0} @Scale @C { KheEjectorContinueGroupMonitor },
and
{0.95 1.0} @Scale @C { KheEjectorOptions } are @C { start_gm },
@C { continue_gm }, and @C { options } from @C { KheEjectorSolve }.
# @C { KheEjectorFrame } is the value of
# @C { KheFrameOption(options, "gs_common_frame", ins) },
# for which see Section {@NumberOf extras.frames}.
# @C { KheEjectorEventTimetableMonitor } is the value of the
# @C { gs_event_timetable_monitor } option
# (Section {@NumberOf general_solvers.general}), or @C { NULL }
# if absent.  When it is present, augment functions may use it
# to find the events running at a given time.
@PP
@C { KheEjectorSoln } is @C { start_gm }'s and @C { continue_gm }'s
solution.  @C { KheEjectorTargetCost }
is the cost that the chain needs to improve on (@C { c }
in the abstract code above):  the cost of the solution when
@C { Augment } was most recently called from the main loop.
# , except when ejection trees are in use, as explained in
# Section {@NumberOf ejection.trees}.
# @C { KheEjectorCurrMajorSchedule } is the current major schedule.
# @C { KheEjectorCurrMinorSchedule } is the current minor schedule, and
@C { KheEjectorCurrMayRevisit } is the
@C { may_revisit } attribute of the current minor schedule.
@PP
@C { KheEjectorCurrLength } is 1 when the augment function was called
from the main loop, 2 when it was called from an augment function called
from the main loop, etc.
@PP
@C { KheEjectorCurrAugmentCount } is the number of augments since this
solve began.  @C { KheEjectorCurrDebug } returns @C { true } when @C { ej }
is currently debugging, because it is trying to repair a main loop
defect in the monitor stored in the @C { es_debug_monitor } option of
@C { options }.  It seems to work well for each repair to generate
a one-line description of itself when @C { KheEjectorCurrDebug } is
@C { true }.  @C { KheEjectorCurrDebugIndent } is the current amount
by which debug prints should be indented; this is twice the current
length.
@PP
For completeness we just mention two other functions:
@ID @C {
void KheEjectorRepairBegin(KHE_EJECTOR ej);
bool KheEjectorRepairEnd(KHE_EJECTOR ej, int repair_type, bool success);
}
Each repair must be bracketed in calls to these.  They will be discussed
in Section {@NumberOf eject.practice.augment}.
#@PP
#A function which returns the solution that an ejector is for has been
#deliberately omitted, because an ejector may be applied to more than
#one solution during its lifetime (but never more than one in parallel).
#While solving, the expression
#@ID @C {
#KheMonitorSoln((KHE_MONITOR) KheEjectorStartGroupMonitor(ej))
#}
#returns the solution that @C { ej } is being applied to.
@PP
@C { KheEjectorSolveBegin } is affected by @C { options }.  It
extracts the following options from its @C { options } argument
and uses them to control the ejector during solving:
@TaggedList

# @DTI { @F es_sch edules }
# {
# The value here is a string describing the schedules to apply
# to an ejector.  The default value is @C { "1+,u-" }.  For the
# meaning of this, consult Section {@NumberOf ejection.ejectors}.
# }

@DTI { @F es_schedule }
{
A string giving the schedule (Section {@NumberOf eject.schedules}), only
consulted when the @C { schedule } parameter of @C { KheEjectorSolveBegin }
is @C { NULL }.  The default value is @C { "1+,u-" }.
}

@DTI { @F es_max_augments }
{
An integer upper limit on the number of augments (including recursive
calls) tried on each attempt to repair one main loop defect.  The
default value is @C { 120 }, which may be better than a larger number:
when there is a time limit, it reduces time wasted on lost causes.
}

@DTI { @F es_max_repairs }
{
An integer upper limit on the number of repairs tried on each attempt
to repair a main loop defect.  The default value is @C { INT_MAX }.
}

@DTI { @F es_limit_defects }
{
An option whose value is either @C { "unlimited" } or an integer.
This integer is a limit on the number of defects handled by the
main loop of the ejector.  Each time the main list of defects
is copied and sorted, if its size exceeds this limit, defects are
dropped from the end until it doesn't.  When the option is not
set, or its value is @C { "unlimited" }, no limit is applied.
}

@DTI { @F es_fresh_visits }
{
A Boolean option which, when @C { true }, instructs @C { ej } to
make fresh visits (Section {@NumberOf eject.variants.articulated}).
}

@DTI { @F es_max_beam }
{
An integer option which gives the maximum number of monitors that
may appear in a beam (Section {@NumberOf eject.variants.beams}).  The
default value is 1, producing ejection chains rather than beams.
}

@DTI { @F gs_debug_monitor_id }
{
A string option, which may be used by other solvers, which instructs
the ejector object to generate debug output when repairing the monitor
with this Id.
}

@DTI { @F es_whynot_monitor_id }
{
A string option which instructs the ejector object to end its
run with an augment of the monitor with this Id with debugging
on (unless its cost is 0).  This will show why this monitor was
not able to be repaired.  Sometimes this augment succeeds, and
then the improvement is incorporated into the solution returned.
}

@EndList
@C { KheEjectorMakeBegin } and @C { KheEjectorMakeEnd } are not
affected by @C { options }.
@End @Section

@Section
    @Title { Variants of the ejection chains idea }
    @Tag { eject.variants }
@Begin
@LP
This section presents some variants of the basic ejection chains idea.
@BeginSubSections

@SubSection
    @Title { Defect promotion }
    @Tag { eject.variants.promotion }
@Begin
@LP
@I { This feature has been withdrawn to streamline the implementation.
It has had some successes but not enough, in the author's judgment,
to justify a place in the ejector framework. }
# it cannot be crucial, because it does
# nothing when @C { start_gm } and @C { continue_gm } are the same. }
@PP
Successful chains begin by repairing a defect which is one of
@C { start_gm }'s children, and they continue by repairing defects
which are children of @C { continue_gm }.  The intention is
that @C { start_gm } should monitor some region of the solution
that has only just been assigned, so that there has been no
chance yet to repair its defects, while @C { continue_gm }
monitors the entire solution so far, or the part of it that
is relevant to repairing the defects of @C { start_gm }.
These two regions may be the same, which is why @C { start_gm }
and @C { continue_gm } may be the same group monitor; but
when they are different, the difference is important, as
the following argument shows.
@PP
Suppose only @C { start_gm } is used.  Then the ejector sets
out to repair the right defects, but is unable to follow chains
of repairs into parts of the solution that have been assigned
previously.  Or suppose only @C { continue_gm } is used.  If
the children of @C { continue_gm } are a superset of the children
of @C { start_gm }, as is always the case in practice, this
does allow a full search, but at the cost of trying again to
repair many defects for which a previous repair attempt failed
(those in @C { continue_gm } which are not also in @C { start_gm }).
This can waste a lot of running time.
@PP
At this point, however, an unexpected issue enters.  Suppose
a successful chain is found which causes some child @C { d } of
@C { continue_gm } to become defective, but which nevertheless
terminates without repairing @C { d } because it improves the
overall solution cost.  Here is a new defect, a child of
@C { continue_gm } not known to have been repaired previously,
and thus worthy of being targeted for repair; but if it is not
also a child of @C { start_gm }, it won't be.
@PP
@I { Defect promotion } addresses this issue.  When an ejection
chain is declared successful, the ejector examines the defects
created by that chain's last repair that are children of
@C { continue_gm }.  It makes any of these that are not children
of @C { start_gm } into children of @C { start_gm }:  they get
dynamically added to the set of defects targeted by the current
solve.  Of course, when @C { start_gm } and @C { continue_gm }
are the same, it does nothing.
# These come from the trace object in the usual way.
@PP
Defect promotion is optional, controlled by option
@C { es_no_promote_defects }, whose default value is @C { false }.
On one run it reduced the final solution cost from 0.04571 to 0.03743,
while increasing running time from 286.84 seconds to 490.21 seconds---a
substantial amount, but nothing like what would have occurred if
@C { start_gm } had been replaced by @C { continue_gm }.
@End @SubSection

@SubSection
    @Title { Fresh visit numbers for sub-defects }
    @Tag { eject.variants.articulated }
@Begin
@LP
It is common for a monitor to monitor several points in the
solution.  For example, a prefer times monitor monitors several
meets, all those derived from one point of application of the
corresponding prefer times constraint (one event).  Arguably,
having one monitor for each meet would make more sense; but
there is a problem with this, at least when the cost function
is not @C { Linear }, because then there is no well-defined value
of the cost of such a monitor.  A cost is only defined after all
the deviations of the @I { sub-defects } at all the monitored
points are added up.
@PP
The usual way to repair a defective monitor which monitors several
points is to visit each point, determine whether that point is a
sub-defect, and try some repairs if so.  When the repair is of a
main loop defect (when the current length is 1), it makes sense
for the augment function to give a fresh visit number to each
sub-defect, so that the repair at each sub-defect is free to
search the whole solution, as in this template:
@ID @C {
for( i = 0;  i < KheMonitorPointCount(m);  i++ )
{
  p = KheMonitorPoint(m, i);
  if( KheMonitorPointIsDefective(p) )
  {
    if( KheEjectorCurrLength(ej) == 1 )
      KheSolnNewGlobalVisit(soln);
    if( KheMonitorPointTryRepairs(p) )
      return;
  }
}
}
Calling @C { KheSolnNewGlobalVisit } opens up the whole solution for
visiting.  This is what would happen if the monitor was broken into
smaller monitors, one for each point.  It is important, however, not
to call @C { KheSolnNewGlobalVisit } at deeper levels, since that
amounts to allowing revisiting, so it leads to exponential time
searches.
@PP
Fresh visit numbers are @I not assigned in this way within the
augment functions supplied with KHE.  Instead, a more radical
version of the idea is offered by the @C { es_fresh_visits }
option.  When set to @C { true }, it causes
@ID @C {
if( KheEjectorCurrLength(ej) == 1 )
  KheSolnNewGlobalVisit(soln);
}
to be executed within each call to @C { KheEjectorRepairBegin },
opening up the entire solution, not just to each sub-defect at
length 1, but to each repair of each sub-defect at length 1.
@End @SubSection

#@SubSection
#    @Title { Recent failure suppression }
#    @Tag { eject.variants.suppression }
#@Begin
#@LP
#When the repair of a defect fails, trying to repair it again soon
#is likely to be a waste of time.  To discourage it, the following
#plan is followed when the @C { suppress_recent } option is
#@C { true }.  When it is @C { false }, the plan is still followed
#except that monitor visit numbers are never changed, reducing this
#description to the usual one.
#@PP
#A @I { phase } begins by copying @C { start_gm }'s defects
#and sorting them.  It then proceeds to try to repair each
#defect in order, stopping when it reaches the end of the
#list, unless it ends earlier as explained below.  The
#ejection chain algorithm repeatedly executes one phase,
#stopping after the first phase which repairs nothing.
#@PP
#At the start of the phase, the value of the global visit number is
#recorded; call it the @I { phase visit number }.  After each monitor
#@C { m } is repaired, if the repair failed then @C { m }'s visit
#number is set to the phase visit number.  So monitors that failed
#to repair recently have higher visit numbers.
#@PP
#When sorting at the beginning of the phase, the sort key is
#first increasing visit number, second decreasing cost, and
#third (to break ties consistently) monitor index number.
#This puts defects whose repairs failed recently near the
#back of the list.
#@PP
#When traversing the list repairing defects, if there has been
#at least one success during this phase and the next defect has a
#larger visit number than the success, the phase ends early.  This
#does not disrupt the final phase (the one which repairs nothing
#and terminates the solve), but it does avoid repairing recently
#failed defects while progress is being made elsewhere.
#@End @SubSection

@SubSection
    @Title { Ejection trees }
    @Tag { eject.variants.trees }
@Begin
@LP
@I {
The variant described here has been withdrawn.  Ejection beams
(Section {@NumberOf eject.variants.beams}) perform a similar
function more simply.
}
@PP
An @I { ejection tree } is like an ejection chain except that
at each level below the first, instead of repairing one newly
introduced defect, it tries to repair several (or all) of the
newly introduced defects, producing a tree of repairs rather
than a chain.
@PP
Ejection trees are not likely to be useful often.  It is true that
the run time of an ejection tree is limited as usual by the size of
the solution, but its chance of success is lower than usual, because
it must repair several defects at the lower level to succeed at the
higher level.  If repairing the first defect produces two new
defects, repairing each of those produces two more, and so on, then
the result is a huge number of defects that must all be repaired
successfully.  And to make a repair which introduces a defect and
then repair that defect using an ejection tree is to spend a lot
of time on a defect that can be removed much more easily by undoing
the initial repair.
@PP
However, when the original solution has a very awkward defect, the
best option may be a complex repair which usually introduces several
new defects.  For example, the best way to repair a cluster busy
times overload defect may be to unassign every meet on one of the
problem days.  In that case, it makes sense to use an ejection tree
at that level alone:  that is, to try a repair that introduces
several defects, then try to repair them by finding an ejection
chain for each.
@PP
The @C { max_sub_chains } parameter of @C { KheEjectorRepairEndLong }
(@I { withdrawn }) allows for ejection trees, by specifying the maximum
number of defects introduced by that repair that are to be repaired.
Different repairs may have different values of @C { max_sub_chains }.
For example, the complex cluster busy times repair could be tried only
when @C { KheEjectorCurrLength(ej) } is 1, with @C { max_sub_chains }
set to @C { INT_MAX }.  All other repairs could be given value
1 for @C { max_sub_chains }, producing ordinary chains elsewhere.
@PP
A set of defects now has to be repaired, not necessarily just one.
One option would have been to change the interface of @C { Augment }
to pass this set to the user.  This was not done, because it would
be a major change from the targeted repairs used by ejection chains.
Instead, just as the framework handles the dynamic dispatch by defect
type, so it also accepts a whole set of defects for repair and passes
them one by one to conventional @C { Augment } calls.
@PP
The remainder of this section explains the implementation of
ejection trees (and indeed ejection chains) by presenting a
more detailed description of the @C { Augment } function than
the one given at the start of this chapter.
@PP
To begin with, it was stated earlier that the main loop tries an
augment for every defective child of @C { start_gm }.  In fact,
main loop augments are tried only for defects @C { d } such that
@ID @C {
Potential(d) = KheMonitorCost(d) - KheMonitorLowerBound(d)
}
is positive.  Clearly, when @C { Potential(d) == 0 } there is
no chance of improvement.
@PP
We also need to consider monitor cost limits, which require that the
solution not change so as to cause the cost of some given monitors
to exceed given limits (Section {@NumberOf eject.solving}).  To
handle them, the interface of @C { Augment } is changed to
@ID @C {
bool Augment(Solution s, Cost c, Limits x, Defect d);
}
where @C { x } is a set of monitor cost limits.  @C { Augment }
returns @C { true } if the value of @C { s } afterwards is such
that @C { s }'s cost is less than @C { c } and the limits @C { x }
are all satisfied.  This condition is evaluated by
@ID @C {
bool Success(Solution s, Cost c, Limits x)
{
  return cost(s) < c && LimitsAllSatisfied(s, x);
}
}
The precondition of @C { Augment(s, c, x, d) } is changed to
@ID @C {
!Success(s, c, x) && cost(s) - GPotential(d) < c
}
That is, the solution must not have already reached the target
cost and limits, and the @I { generalized potential } of @C { d },
@C { GPotential(d) }, is large enough to suggest that repairing
@C { d } might get it there.  Its postcondition is
@C { Success(s, c, x) } if @C { true } is returned, and `@C { s }
is unchanged' otherwise.
@PP
For a main loop defect, @C { cost(s) } is @C { c },
@C { GPotential(d) } is @C { Potential(d) }, and
@C { Augment } may be called exactly when it @I is called---when
@C {  Potential(d) > 0 }.  For defects at lower levels,
@C { GPotential(d) } is the amount that the cost of
@C { d } increased when the repair that produced @C { d }
occurred, as returned by @C { KheTraceMonitorCostIncrease }
(Section {@NumberOf monitoring.group.tracing}).  Often, the
cost beforehand will be @C { KheMonitorLowerBound(d) }, so
that this increase will just be @C { Potential(d) } as before;
but if @C { d } was already defective beforehand it will
be smaller, making @C { d } less likely to be augmented.  The
point is that we can only realistically hope to remove the
new cost added when the previous repair was made, not the
pre-existing cost.  (KHE used @C { Potential(d) } here for
many years; the switch to @C { GPotential(d) }
produced better cost on average, and better running time;
a few individual instances had marginally worse cost.)
@PP
The new defects chosen for repair must be @I { open defects }:
defects whose generalized potential is positive, according to
@C { KheTraceMonitorCostIncrease }.  The @C { max_sub_chains } open
defects of largest generalized potential, or all open defects if fewer
than @C { max_sub_chains } open defects are reported by the trace,
are selected.  In the code below, this selection is made by line
@ID @C {
{d1, ..., dn} = SelectOpenDefects(new_defect_set, MaxSubChains(r));
}
# where @C { 0 <= n <= MaxSubChains(r) }
This is implemented by a call to @C { KheTraceReduceByCostIncrease }
(Section {@NumberOf monitoring.group.tracing}).
@PP
Here is the more detailed implementation of @C { Augment }:
@ID -1px @Break {0.96 1.0} @Scale @C {
bool Augment(Solution s, Cost c, Limits x, Defect d)
{
  repair_set = RepairsOf(d);
  for( each repair r in repair_set )
  {
    new_defect_set = Apply(s, r);
    if( Success(s, c, x) )
      return true;
    if( NotAtLengthLimit() )
    {
      {d1, ..., dn} = SelectOpenDefects(new_defect_set, MaxSubChains(r));
      for( i = 1;  i <= n;  i++ )
      {
	sub_c = c + GPotential(d(i+1)) + ... + GPotential(dn);
	sub_x = (i < n ? {} : x);  /* empty limit set except at end */
	if( Success(s, sub_c, sub_x) )
	  continue;
	if( cost(s) - GPotential(di) >= sub_c )
	  break;
	if( !Augment(s, sub_c, sub_x, di) )
	  break;
	if( Success(s, c, x) )
	  return true;
      }
    }
    reset s to its state just before Apply(s, r);
  }
  return false;
}
}
As before, all of this except the loop that iterates over and applies
repairs is hidden in calls to @C { KheEjectorRepairBegin } and
@C { KheEjectorRepairEnd }.  It is easy to verify that this
satisfies the revised postcondition.  The reset near the end is
carried out by a call to @C { KheMarkUndo }.
@PP
After the usual test for success immediately after the repair,
if the length limit has not been reached the new code selects
@C { n } open defects for repair, then calls @C { Augment }
recursively on each in turn.  The complicating factor is the
choice of a target cost and set of limits for each recursive
call, denoted @C { sub_c } and @C { sub_x } above.  Using the
original @C { c } and @C { x }, as is done with ejection chains,
would wrongly place the entire burden of improving the solution
onto the first recursive call.
@PP
When repairing @C { d1 }, the right cost target to shoot for is
@ID @C {
sub_c = c + GPotential(d2) + ... + GPotential(dn);
}
The best that can be hoped for from repairing @C { d2 } is
@C { GPotential(d2) }, the best that can be hoped for from
repairing @C { d3 } is @C { GPotential(d3) }, etc.  So if the
first recursive @C { Augment } cannot reduce @C { cost(s) } below
the given value of @C { sub_c }, there is little hope that after all
the recursive augments it will be reduced below @C { c }.  The
same idea is applied for each of the @C { di }.
@PP
When a recursive call to @C { Augment } changes the solution, some
@C { GPotential(di) } values may change.  So this code re-evaluates
@C { sub_c } from scratch on each iteration of the inner loop,
rather than attempting to save time by adjusting the previous
value of @C { sub_c }.
@PP
The choice of @C { sub_x } causes limits to be ignored except when
carrying out the last augment.  This is in accord with the intention
of monitor cost limits, which is to only check them at the end.  It
would be a mistake to check them earlier.  For example, the repair of
the cluster busy times defects described above is likely to violate
a monitor cost limit when it deassigns meets.  These do need to be
reassigned by the end, but they will not all be reassigned earlier.
@PP
After defining @C { sub_c } and @C { sub_x } but before the call
to @C { Augment }, the code executes
@ID @C {
if( Success(s, sub_c, sub_x) )
  continue;
if( cost(s) - GPotential(di) >= sub_c )
  break;
}
These lines ensure that the precondition of the recursive
@C { Augment } call holds at the time it is made.  If
@C { Success(s, sub_c, sub_x) } holds, then the aim of that call has
already been achieved, so the algorithm moves on to the next one.  It
does not matter that it skips the @C { Success(s, c, x) } test further
on, because there has been such a test since the last time the solution
changed.  If @C { cost(s) - GPotential(di) >= sub_c } holds, then the
algorithm has no real hope of beating @C { sub_c } by repairing @C { di },
and so no real hope of success at all, so it abandons the current repair.
@PP
@C { Success(s, c, x) } implies @C { Success(s, sub_c, sub_x) }
throughout @C { Augment }, because @C {  sub_c >= c } and @C { sub_x }
is a subset of @C { x }.  This cannot be used to simplify @C { Augment }, 
but it does have one or two interesting consequences.  For example, it
applies transitively down through all active calls to @C { Augment }, so
while @C { Success(s, sub_c, sub_x) } is @C { false } at any level of
recursion, the original aim of the ejection tree cannot be satisfied.
@PP
When repairing @C { dn }, @C { sub_c == c } and @C { sub_x == x }.
This gives confidence that @C { Augment } could succeed, and shows
that it reduces to the original @C { Augment } when
@C { MaxSubChains(r) == 1 }, except for the different expression of
how one open defect is selected.
@PP
The method described here finds the first chain that repairs
@C { d1 }, fixes it, and moves on to @C { d2 }.  Representing
the higher path by a solid arrow, the chains (successful or not) that
repair @C { d1 } by dashed arrows, and the chains (successful or
not) that repair @C { d2 } by dotted arrows, the picture is
@CD @Diag
{
AA:: @Box margin { 0i } outlinestyle { noline } 1c @Wide 0.5c @High {} &
BB:: @Box margin { 0i } outlinestyle { noline } 1c @Wide 0.5c @High {}
//
CC:: @Box margin { 0i } outlinestyle { noline } 1c @Wide 1c @High {} &
DD:: @Box margin { 0i } outlinestyle { noline } 1c @Wide 1c @High {}
//
@Arrow from { AA@NE } to { AA@SE }
@Line from { CC@N } to { DD@N }
@Arrow pathstyle { dashed } from { CC@N } to { CC@SW ** 0.25 ++ CC@SE ** 0.75 }
@Arrow pathstyle { dashed } from { CC@N } to { CC@SW ** 0.75 ++ CC@SE ** 0.25 }
@Arrow pathstyle { dotted } from { DD@N } to { DD@SW ** 0.25 ++ DD@SE ** 0.75 }
@Arrow pathstyle { dotted } from { DD@N } to { DD@SW ** 0.75 ++ DD@SE ** 0.25 }
}
Another possibility is to find the first chain that repairs @C { d1 },
then try to find chains for @C { d2 }, but if that fails, to continue
searching for other chains for @C { d1 }:
@CD @Diag
{
AA:: @Box margin { 0i } outlinestyle { noline } 1c @Wide 0.5c @High {} &
BB:: @Box margin { 0i } outlinestyle { noline } 1c @Wide 0.5c @High {}
//
CC:: @Box margin { 0i } outlinestyle { noline } 1c @Wide 1c @High {} &
DD:: @Box margin { 0i } outlinestyle { noline } 1c @Wide 1c @High {}
//
EE:: @Box margin { 0i } outlinestyle { noline } 1c @Wide 1c @High {} &
FF:: @Box margin { 0i } outlinestyle { noline } 1c @Wide 1c @High {}
//
@Arrow from { AA@NE } to { AA@SE }
@Arrow pathstyle { dashed } from { CC@NE } to { CC@S}
@Arrow pathstyle { dashed } from { DD@NW } to { DD@S}
@Arrow pathstyle { dotted } from { EE@N } to { EE@SW ** 0.25 ++ EE@SE ** 0.75 }
@Arrow pathstyle { dotted } from { EE@N } to { EE@SW ** 0.75 ++ EE@SE ** 0.25 }
@Arrow pathstyle { dotted } from { FF@N } to { FF@SW ** 0.25 ++ FF@SE ** 0.75 }
@Arrow pathstyle { dotted } from { FF@N } to { FF@SW ** 0.75 ++ FF@SE ** 0.25 }
}
This approach is implementable within the current framework, but it has
not been tried.  Ejection beams (Section {@NumberOf eject.variants.beams})
do something of this kind.
@End @SubSection

@SubSection
    @Title { Sorting repairs }
    @Tag { eject.variants.sortrepairs }
@Begin
@LP
@I {
The variant described here never performed very well, and it has now
been withdrawn.
}
@PP
Each repair is usually followed immediately by recursive calls which
extend the chain, where applicable.  Setting the @C { save_and_sort }
parameter of @C { KheEjectorRepairEndLong } (@I { withdrawn }) to
@C { true } invokes a different arrangement.  Paths representing the
repairs are saved in the ejector without recursion.  After the last
repair they are sorted into increasing order of the cost of the
solutions they produce, and each is tried in turn, just as though
they had occurred in that sorted order @Cite { $haan2007 }.
@PP
In practice, @C { save_and_sort } would be given the same value
for every repair of a given defect.  However, it is legal to use a
mixture of values.  Those given value @C { true } will be saved,
those given value @C { false } will be recursed on immediately in
the usual way.  If any of those lead to success, that chain is
accepted and any saved repairs are forgotten.
@PP
Only repairs with some hope of success are saved:  those for which
@ID @C {
Success(s, c, x) || (NotAtLengthLimit() &&
  cost(s) - (GPotential(d1) + ... + GPotential(dn)) < c)
}
holds after the repair, in the terminology of
Section {@NumberOf eject.variants.trees}.
@PP
The author's experience with @C { save_and_sort } has been
disappointing.  Chains can end successfully anywhere in the search
tree, and low solution cost at an intermediate point is not a
good predictor of a successful end.  Every saved repair is executed
once before sorting to establish the solution cost after it, then
undone.  If the repair is tried later, it is executed again
(by a path redo).  The significant benefit needed to justify
this extra work does not seem to be there.
@End @SubSection

@SubSection
    @Title { Ejection beams }
    @Tag { eject.variants.beams }
@Begin
@LP
@I { Ejection beams } are yet another variant of the basic ejection
chains idea.  They are similar to the now-withdrawn ejection trees
in that they aim to allow the algorithm to carry on when more than
one defect is introduced by a repair.
# ; but they are arguably easier to deal with.
@PP
An @I { ejection beam }, or just @I { beam }, is a non-empty set of
monitors @C { m }, each with its @I { cost }, denoted @C { cost(m) },
and its @I { target }, denoted @C { targ(m) }.  The value of
@C { cost(m) } is the cost of @C { m } in the current solution, as
usual.  The value of @C { targ(m) } will be specified shortly; it is
a value that the algorithm aspires to reduce @C { cost(m) } to.
@PP
A beam is a true set:  no monitor may appear in it twice.  Its
cardinality (its number of monitors) is at least 1 and at most
some small integer @C { K } (a parameter of the algorithm) whose
value must be at least 1.  @C { K } might be 2 or 3, for example.
@PP
Instead of passing a single defective monitor, the
basic call passes an entire beam @C { B }:
@ID @C {
bool Augment(Solution s, Cost c, Beam B)
}
As usual, if @C { Augment } can find a way to reduce the cost of @C { s }
to less than @C { c }, it does so and returns @C { true }, otherwise
it changes nothing and returns @C { false }.  But now it has a set of
monitors, all those in @C { B }, to repair.  When @C { Augment } is
called, each of these monitors @C { m } is unvisited and satisfies
@ID @C {
cost(m) > targ(m) >= 0
}
Furthermore, along with the usual @C { cost(s) >= c }, the condition
@ID @C {
Open(s, c, B) = cost(s) - sum[m in B](cost(m) - targ(m)) < c
}
holds initially.  So one way for @C { Augment } to succeed would be
to reduce the cost of every monitor in @C { B } down to its target
without introducing any new defects.
@PP
Like the ejection chain algorithm, the ejection beam algorithm has a
main loop that tries to repair each top-level defect @C { m } whose
cost exceeds its lower bound.  To do this it first marks all monitors
unvisited, then it calls @C { Augment }, setting @C { s } to the
current solution, @C { c } to the cost of the current solution,
and @C { B } to a beam consisting of a single element, @C { m },
setting @C { targ(m) } to @C { m }'s lower bound.  This clearly
satisfies the initial conditions of @C { Augment }.
@PP
Here is the implementation of @C { Augment }.  We've added a
@C { Limits } parameter which handles the limits imposed by
@C { KheEjectorAddMonitorCostLimit } (Section {@NumberOf eject.solving}):
@ID -1px @Break {0.96 1.0} @Scale @C {
bool Augment(Solution s, Cost c, Limits x, Beam B)
{
  choose an arbitrary element d of B;
  MonitorSetVisited(d);
  repair_set = RepairsOf(d);
  for( each repair r in repair_set )
  {
    new_defect_set = Apply(s, r);
    if( Success(s, c, x) )
      return true;
    if( LimitsNotReached() && BeamMerge(s, c, B, new_defect_set, K, &B2) )
    {
      if( Augment(s, c, x, B2) )
	return true;
    }
    reset s to its state just before Apply(s, r);
  }
  return false;
}
}
Here @C { Success } compares the solution cost with @C { c } and
checks the limits:
@ID @C {
bool Success(Solution s, Cost c, Limits x)
{
  return cost(s) < c && LimitsAllSatisfied(s, x);
}
}
while @C { LimitsNotReached } returns @C { true } if the various
limits, on number of augments and so on, have not yet been reached.
@PP
Apart from a few minor details, this version of @C { Augment } is
clearly the original one with a set of monitors @C { B } instead
of a single defect @C { d }.  It demands nothing additional
from the user except a single value for @C { K } at the start.
@PP
The key new step is @C { BeamMerge }.  It returns a new non-empty beam
@C { B2 } which is the set union of @C { B } and @C { new_defect_set },
sorted into decreasing @C { cost(m) - targ(m) } order.  Monitors are
omitted when they are visited, and they are dropped from the end of
the sorted list until just before dropping another would make
@C { Open(s, c, B2) } @C { false }.  If the number of monitors
remaining is less than 1 or more than @C { K }, @C { MakeBeam }
returns @C { false }.  So if the recursive call to @C { Augment }
is made, @C { B2 } is a beam of legal size which satisfies the
precondition of @C { Augment }.
@PP
The author was surprised to discover that beams of size 0 could
emerge from @C { BeamMerge }.  They occur when the cost has been
reduced to a new best but limit monitors prevent the current
solution from being declared a success.
@PP
A new monitor @C { m } entering @C { B2 } has its @C { targ(m) }
value set to its cost before @C { r } was applied.  Clearly,
@C { cost(m) > targ(m) >= 0 }, because @C { targ(m) } is
@C { cost(m) } before @C { r } was applied, and it was @C { r }
that caused @C { m } to appear in @C { new_defect_set }, meaning
that @C { m }'s cost increased.  The algorithm aspires to
remove the new defects introduced by @C { r }, which in the
case of @C { m } means returning its cost to what it was before
@C { r }, so this is a suitable value for @C { targ(m) }.
@PP
A monitor @C { m } could lie in both @C { B } and @C { new_defect_set },
if it was a defect before applying @C { r }, and @C { r } made it
worse.  That is fine; it will appear only once in @C { B2 }, with
its original @C { targ(m) }.
@PP
Ejectors always run the ejection beam code, but the default value of
@C { K } is 1, producing ejection chains.  @C { K } is determined by
the { @F es_max_beam } option (Section {@NumberOf eject.solving}), so
setting this to a value larger than 1 is the way to get ejection beams.
@End @SubSection

@EndSubSections
@End @Section

@Section
    @Title { Gathering statistics }
    @Tag { eject.statistics }
@Begin
@LP
Ejectors gather statistics about their performance.  This takes a negligible
amount of time, as the author has verified by comparing run times with
preprocessor flag @C { KHE_EJECTOR_WITH_STATS } in the ejector source
file set to 0 (no statistics) and 1 (all statistics).  On two typical
instances, the increase in overall run time caused by gathering
statistics was less than 0.1 seconds.
@BeginSubSections

@SubSection
    @Title { Options for choosing ejectors }
    @Tag { eject.statistics.options }
@Begin
@LP
Each ejector holds its own statistics, independently of other
ejectors.  Some statistics accumulate across the entire lifetime
of an ejector; they are never reset.  This makes it possible, for
example, to measure the performance of time repair ejection chains
and resource repair ejection chains over an entire set of instances,
by carrying out all time repairs in all instances using one
ejector and all resource repairs in all instances using another.
@PP
To facilitate this, options objects usually contain two ejectors,
under names @C { "es_ejector1" } and @C { "es_ejector2" }, as explained
in Section {@NumberOf eject.practice.top}; they could contain more.  
# @PP
# The next question is what schedules to give to these ejectors.
# A set of schedules is an option, so the @C { options } object
# has option @C { es_schedules } for it, whose value is a string.
# Its default value is @C { "1+,u-" }, for the meaning of which see
# Section {@NumberOf eject.ejectors}.
# @PP
# Setting the schedule string does not set any ejector schedules,
# it merely sets one option of @C { options }, to a fresh copy of
# the string it is given.  User code must set the actual schedules.
# # by passing a schedule string to function @C { KheEjectorMakeBegin }
# # (Section {@NumberOf eject.ejectors}).
@End @SubSection

@SubSection
    @Title { Statistics for analysing Kempe meet moves }
    @Tag { eject.statistics.kempe }
@Begin
@LP
The ejector itself does not maintain statistics for analysing
Kempe meet moves.  These are stored in @C { kempe_stats }
objects, one of which is conveniently available from option
@C { ts_kempe_stats } (Section {@NumberOf time_solvers.kempe}).  This
object is passed to the calls to @C { KempeMeetMove } made by the
augment functions described in this chapter.  Only Kempe meet moves
which are complete repairs on their own are passed this object, not
Kempe meet moves combined with other operations (meet splits and
merges, for example).  So by the end of an ejction chain run,
statistics about these Kempe meet moves will have been accumulated
in the @C { ts_kempe_stats } option of the @C { options } object
passed to the ejection chain repairs.
@End @SubSection

@SubSection
    @Title { Statistics describing a single solve }
    @Tag { eject.statistics.chains }
@Begin
@LP
The statistics presented in this section make sense only for one call
to @C { KheEjectorSolveEnd }.  So they are available only until the
next call to @C { KheEjectorSolveEnd }, when they are reset.
@PP
An @I { improvement } is an ejection chain or tree, rooted in a defect
examined by the main loop, which is applied to the solution and reduces
its cost.  Each time an improvement is applied, four facts about it
are recorded.  The number of improvements applied is returned by
@ID @C {
int KheEjectorImprovementCount(KHE_EJECTOR ej);
}
and the four facts about the @C { i }th improvement (counting from
0 as usual) are returned by
@ID @C {
int KheEjectorImprovementNumberOfRepairs(KHE_EJECTOR ej, int i);
float KheEjectorImprovementTime(KHE_EJECTOR ej, int i);
KHE_COST KheEjectorImprovementCost(KHE_EJECTOR ej, int i);
int KheEjectorImprovementDefects(KHE_EJECTOR ej, int i);
}
These return the number of repairs in the @C { i }th improvement
(this tends to increase with @C { i }), the time from the moment
when @C { KheEjectorSolveEnd } was called to the moment after the
improvement was applied, the solution cost afterwards, and the
number of defects of @C { start_gm } afterwards.  Times are measured in
seconds, to a precision much better than one second.  There are also
@ID @C {
KHE_COST KheEjectorInitCost(KHE_EJECTOR ej);
int KheEjectorInitDefects(KHE_EJECTOR ej);
}
which return the cost and number of defects when @C { KheEjectorSolve }
began.
@End @SubSection

@SubSection
    @Title { Statistics describing multiple solves }
    @Tag { eject.statistics.augment_and_repair }
@Begin
@LP
The statistics presented in this section make sense across
multiple calls to @C { KheEjectorSolveEnd }.  They are initialized
when the ejector is created and never reset.
@PP
It is interesting to see how many repairs make up one improvement.
Each time an improvement occurs on any solve during the lifetime
of the ejector, one entry in a histogram of numbers of repairs is
incremented.  This histogram can be accessed at any time by calling
@ID @C {
int KheEjectorImprovementRepairHistoMax(KHE_EJECTOR ej);
int KheEjectorImprovementRepairHistoFrequency(KHE_EJECTOR ej,
  int repair_count);
}
@C { KheEjectorImprovementRepairHistoMax } returns the maximum,
over all improvements @M { x }, of the number of repairs that
make up @M { x }, or 0 if there have been no improvements.
@C { KheEjectorImprovementRepairHistoFrequency } returns the number
of improvements with the given number of repairs.  Also, functions
@ID @C {
int KheEjectorImprovementRepairHistoTotal(KHE_EJECTOR ej);
float KheEjectorImprovementRepairHistoAverage(KHE_EJECTOR ej);
}
use this same basic information to find the total number of
improvements, and the average number of repairs per improvement
when there is at least one improvement.
@PP
Another histogram, again with one element for each improvement,
records the number of calls to @C { Augment } since the most
recent one in the main loop:
@ID @C {
int KheEjectorImprovementAugmentHistoMax(KHE_EJECTOR ej);
int KheEjectorImprovementAugmentHistoFrequency(KHE_EJECTOR ej,
  int augment_count);
int KheEjectorImprovementAugmentHistoTotal(KHE_EJECTOR ej);
float KheEjectorImprovementAugmentHistoAverage(KHE_EJECTOR ej);
}
This is helpful, for example, in deciding whether it would be
useful to terminate a search after some number of augments has
failed to find an improvement.  A method of doing this is built
into ejectors, but not offered as an official option at the moment.
@PP
Another interesting question is how successful the various augment
functions and repairs are.  There are methodological issues here,
however.  For example, if one kind of repair is tried before another,
it has more opportunities to both succeed and fail than the other.
If there are several alternatives to choose from, the best test
would be to compare the results of several complete runs, one for
each alternative.  No statistical support is needed for that.  But
even after the best alternatives are chosen, there remains the
question of whether each component is pulling its weight.  The
statistics to be described now attempt to answer this question.
@PP
An @I { augment type } is a small integer representing one kind
of augment function.  A @I { repair type } is a small integer
representing one kind of repair.  Functions @C { KheEjectorAddAugment }
and @C { KheEjectorAddGroupAugment } assign an augment type to each
augment function, and thus to each call on an augment function.
Each repair is followed by a call to @C { KheEjectorRepairEnd }
(Section {@NumberOf eject.practice.augment}), and its
@C { repair_type } parameter assigns a repair type to that repair.
Based on this information, the ejector records the following statistics:
@NumberedList

@LI @OneRow {
For each distinct @C { augment_type }, the number of repairs
made by calls on augment functions with that augment type;
}

@LI @OneRow {
For each distinct @C { (augment_type, repair_type) } pair, the
number of repairs of that repair type made by calls on augment
functions with that augment type;
}

@LI @OneRow {
For each distinct @C { augment_type }, the number of successful
repairs made by calls on augment functions with that augment type;
}

@LI @OneRow {
For each distinct @C { (augment_type, repair_type) } pair, the
number of successful repairs of that repair type made by calls
on augment functions with that augment type.
}

@EndList
Only repairs with a @C { true } value for the @C { success }
parameter of @C { KheEjectorRepairEndLong } are counted.
# When the @C { save_and_sort } option is in use, not all saved
# repairs are counted, only those redone after sorting.
For the
purposes of statistics gathering, a repair is considered
successful if it causes its enclosing @C { Augment } function to
return @C { true }, whether this happens immediately, or after
recursion, or after saving and sorting.  The statistics may be
retrieved at any time by calling
@ID @C {
int KheEjectorTotalRepairs(KHE_EJECTOR ej, int augment_type);
int KheEjectorTotalRepairsByType(KHE_EJECTOR ej, int augment_type,
  int repair_type);
int KheEjectorSuccessfulRepairs(KHE_EJECTOR ej, int augment_type);
int KheEjectorSuccessfulRepairsByType(KHE_EJECTOR ej, int augment_type,
  int repair_type);
}
where @C { augment_type } and @C { repair_type } are arbitrary
non-negative integers.  Based on these numbers, a reasonable
analysis of the effectiveness of the augment functions and their
repairs can be made.  For example, the effectiveness of an augment
function can be measured by the ratio of the third number to the
first.  Adding up the result of @C { KheEjectorTotalRepairsByType }
over all values of @C { repair_type } produces the result of
@C { KheEjectorTotalRepairs }, and adding up the result of
@C { KheEjectorSuccessfulRepairsByType } over all values of
@C { repair_type } produces the result of @C { KheEjectorSuccessfulRepairs }.
@End @SubSection

@SubSection
    @Title { Organizing augment and repair types }
    @Tag { eject.statistics.labels }
@Begin
@LP
@C { KheEjectorAddAugment } and @C { KheEjectorAddGroupAugment }
accept any @C { augment_type } values.  The user should define
these values using an enumerated type.  The following function
may be called any number of times during the ejector's setup phase,
to tell it what values to expect:
@ID @C {
void KheEjectorAddAugmentType(KHE_EJECTOR ej, int augment_type,
  char *augment_label);
}
This tells @C { ej } to expect calls to @C { KheEjectorAddAugment }
and @C { KheEjectorAddGroupAugment } with the given value of
@C { augment_type }, and associates a label with that augment type.
Labels must be non-@C { NULL }; copies are stored, not originals.  No
checks are made that the values passed via @C { KheEjectorAddAugment }
and @C { KheEjectorAddGroupAugment } match those declared by
@C { KheEjectorAddAugmentType }.  But if they do, then making tables
of statistics is simplified by calling the following functions afterwards.
@PP
To visit all the augment types declared by calls to
@C { KheEjectorAddAugmentType }, call
@ID @C {
int KheEjectorAugmentTypeCount(KHE_EJECTOR ej);
int KheEjectorAugmentType(KHE_EJECTOR ej, int i);
}
To retrieve the label corresponding to an augment type, call
@ID @C {
char *KheEjectorAugmentTypeLabel(KHE_EJECTOR ej, int augment_type);
}
In this way, suitable values for passing to @C { KheEjectorTotalRepairs }
and the other statistics functions above can be generated, along with
labels to identify the statistics.
@PP
The same functionality is offered for repair types.
@C { KheEjectorRepairBegin } may be passed any values for @C { repair_type },
but the user knows which values will be passed, and the following function
may be called any number of times during the ejector's setup phase to
tell it this:
@ID @C {
void KheEjectorAddRepairType(KHE_EJECTOR ej, int repair_type,
  char *repair_label);
}
@C { KheEjectorAddRepairType } declares that @C { ej } can expect calls
to @C { KheEjectorRepairBegin } with the given value of @C { repair_type },
and associates a label with that repair type.  Labels must be
non-@C { NULL }; copies are stored, not originals.  No checks are made
that the values passed via @C { KheEjectorRepairBegin } match those
declared by @C { KheEjectorAddRepairType }.  But if they do, then
making tables of statistics is simplified by calling the following
functions afterwards.
@PP
To visit all the repair types declared by calls to
@C { KheEjectorAddRepairType }, call
@ID @C {
int KheEjectorRepairTypeCount(KHE_EJECTOR ej);
int KheEjectorRepairType(KHE_EJECTOR ej, int i);
}
To retrieve the label corresponding to a repair type, call
@ID @C {
char *KheEjectorRepairTypeLabel(KHE_EJECTOR ej, int repair_type);
}
There is no way to declare which combinations of augment type and
repair type to expect.  The author handles this by ignoring cases
where @C { KheEjectorTotalRepairsByType } returns 0.
@End @SubSection

@EndSubSections
@End @Section

# @Section
#     @Title { Ejector options }
#     @Tag { eject.options }
# @Begin
# @LP
# @BeginSubSections
# 
# @SubSection
#     @Title { Obtaining ejector objects }
#     @Tag { eject.repair.ejectors }
# @Begin
# @LP
# The first issue is where the ejector objects come from.  They
# need to be loaded with the right augment functions, and they
# need to be available after the solve, to give access to their
# statistics.  KHE offers a function for creating an ejector
# object with its augment functions loaded:
# @ID {0.98 1.0} @Scale @C {
# KHE_EJECTOR KheEjectionChainEjectorMake(KHE_OPTIONS options, HA_ARENA a);
# }
# This returns a new ejector object, ready to solve with, stored in
# arena @C { a }.  Its schedules come from the @C { es_schedules }
# option of @C { options }, and its augment functions are the ones
# defined by KHE.  However, the three ejection chain solvers obtain
# their ejector objects indirectly, by calling
# @ID {0.98 1.0} @Scale @C {
# KHE_EJECTOR KheEjectionChainEjectorOption(KHE_OPTIONS options, char *key);
# }
# This retrieves the object from @C { options } with the given key,
# casts it to @C { KHE_EJECTOR }, and returns it; or if there is no
# such object, it calls @C { KheEjectionChainEjectorMake(options, a) }
# to create one, adds it to @C { options } under the given key, and
# returns it.  The ejector's lifetime is the lifetime of @C { options },
# because @C { a } is @C { KheOptionsArena(options) }.
# @PP
# For @C { KheEjectionChainNodeRepairTimes } and
# @C { KheEjectionChainLayerRepairTimes } the key is @C { "es_ejector1" };
# for @C { KheEjectionChainRepairResources } it is @C { "es_ejector2" }.
# This allows for collecting two sets of statistics, for time
# assignment and resource assignment.
# @End @SubSection
# 
# @EndSubSections
# @End @Section

@Section
    @Title { Using ejection chains in practice }
    @Tag { eject.practice }
@Begin
@LP
In the remaining sections of this chapter, we focus on the practical
side of getting ejectors to repair solutions.  The code presented in
these sections, and found in files @C { khe_se_focus.c }
and @C { khe_se_solvers.c }, is more volatile than the code in
@C { khe_se_ejector.c } defining ejector objects---more subject
to change as new ideas come along.
@BeginSubSections

@SubSection
    @Title { Top-level ejection chains functions }
    @Tag { eject.practice.top }
@Begin
@LP
KHE offers several top-level functions for repairing solutions using
ejection chains:
@ID @C {
bool KheEjectionChainNodeRepairTimes(KHE_NODE parent_node,
  KHE_OPTIONS options, char *schedule);
bool KheEjectionChainLayerRepairTimes(KHE_LAYER layer,
  KHE_OPTIONS options, char *schedule);
bool KheEjectionChainRepairResources(KHE_SOLN soln,
  KHE_RESOURCE_TYPE rt, KHE_OPTIONS options, char *schedule);
bool KheEjectionChainRepairInitialResourceAssignment(
  KHE_GROUP_MONITOR limit_resources_gm, KHE_OPTIONS options,
  char *schedule);
}
These are ready for the end user to call.
@C { KheEjectionChainNodeRepairTimes } repairs the assignments of
the meets of the descendants of the child nodes of @C { parent_node },
and @C { KheEjectionChainLayerRepairTimes } repairs the assignments of
the meets of the descendants of the child nodes of @C { layer }.  This
is for repairing the time assignments of a layer immediately after
they are made, avoiding earlier layers where repairs
have already been tried and are unlikely to succeed.
@C { KheEjectionChainRepairResources } repairs the assignments of the
tasks of @C { soln } whose resource type is @C { rt }.  And
@C { KheEjectionChainRepairInitialResourceAssignment } also repairs
task assignments, but only of tasks monitored by @C { limit_resources_gm }.
@PP
These top-level functions are all much the same.  They use the same
augment functions, for example.  They differ in how some options are
set, and in how @C { start_gm } is defined.
@PP
All three functions make assignments as well as change them, so may
be used to construct solutions as well as repair them.  However,
there are better ways to construct solutions.
@PP
Here is function @C { KheEjectionChainRepairResources }:
@ID @C {
bool KheEjectionChainRepairResources(KHE_SOLN soln,
  KHE_RESOURCE_TYPE rt, KHE_OPTIONS options, char *schedule)
{
  KHE_EJECTOR ej;  KHE_GROUP_MONITOR kempe_gm, start_gm, limit_gm;
  bool res;  KHE_AUGMENT_OPTIONS ao;

  /* get an ejector */
  ej = KheEjectionChainEjectorOption(options, "es_ejector2");

  /* set the control options */
  KheOptionsSetBool(options, "es_repair_times", false);
  KheOptionsSetObject(options, "es_limit_node", NULL);
  KheOptionsSetBool(options, "es_repair_resources", true);

  /* build the required group monitors and solve */
  ao = KheAugmentOptionsMake(ej, soln, rt, options,
    KheEjectorArena(ej));
  KheGroupCorrelatedMonitors(soln, options);
  kempe_gm = KheKempeDemandGroupMonitorMake(soln);
  start_gm = KheStartGroupMonitorMake(soln, rt);
  KheEjectorSolveBegin(ej, start_gm, (KHE_GROUP_MONITOR) soln,
    schedule, ao, options);
  res = KheEjectorSolveEnd(ej);

  /* clean up and return */
  KheGroupMonitorDelete(kempe_gm);
  KheGroupMonitorDelete(start_gm);
  KheUnGroupCorrelatedMonitors(soln);
  return res;
}
}
We've simplified it slightly to emphasize the main points.
We'll go through it now line by line.
@PP
@C { KheEjectionChainEjectorOption } retrieves an ejector object from
@C { options }, as described just below.  This avoids wasting time
by creating ejector objects over and over again, but its main purpose
is to allow statistics over the whole run to be accumulated in just
a few ejector objects---currently @C { "es_ejector1" } for time repair,
and @C { "es_ejector2" } for resource repair.
@PP
Here is @C { KheEjectionChainEjectorOption } and another function
that it may call:
@ID @C {
KHE_EJECTOR KheEjectionChainEjectorMake(KHE_OPTIONS options,
  HA_ARENA a);
KHE_EJECTOR KheEjectionChainEjectorOption(KHE_OPTIONS options,
  char *key);
}
@C { KheEjectionChainEjectorMake } makes an ejector object.  It
starts with a call to @C { KheEjectorMakeBegin }, ends by calling
@C { KheEjectorMakeEnd }, and between them makes many calls to
@C { KheEjectorAddAugmentType }, @C { KheEjectorAddRepairType },
@C { KheEjectorAddAugment }, and @C { KheEjectorAddGroupAugment }, to
load the augment functions given in Sections {@NumberOf eject.time}
and {@NumberOf eject.resource}, together with augment types and
repair types that allow detailed statistics to be gathered.
@PP
@C { KheEjectionChainEjectorOption } retrieves an ejector object from
@C { options }, stored under the given @C { key }.  If there is no such
object, it calls @C { KheEjectionChainEjectorMake } to create one,
stores it under @C { key }, and returns it.
@PP
Next, options @C { "es_repair_times" }, @C { "es_limit_node" }, and
@C { "es_repair_resources" } are set to values appropriate to resource
repair.  These values will be consulted later by augment functions and
will cause them to limit their repairs to resource repairs.
@PP
@C { KheAugmentOptionsMake } constructs an object of type
@C { KHE_AUGMENT_OPTIONS } by filling in its fields based
on values it retrieves from @C { options }.  It is basically
an optimization:  it allows augment functions to access the
options they need much more quickly that via a cumbersome
retrieval from an @C { options } object.  We'll look at
this more closely in Section {@NumberOf eject.practice.options}.
@PP
As discussed in Section {@NumberOf general_solvers.grouping.correlation},
it is important for the effective use of ejection chains to
group correlated monitors.  This is done by the call to
@C { KheGroupCorrelatedMonitors }, and undone at the end by
@C { KheUnGroupCorrelatedMonitors }
(Section {@NumberOf general_solvers.grouping.functions}).
There is no need here to group monitors concerned with time assignment,
but it is simpler to just group everything.
@PP
Two other group monitors, of the kind described as @I { focus groupings }
in Section {@NumberOf general_solvers.grouping.focus}, are needed by
@C { KheEjectionChainRepairResources }.  The first is @C { kempe_gm },
needed to support repairs that include Kempe moves; the second is
@C { start_gm }, needed to limit the main loop of the ejector to repairs
of event resource and resource defects of the given resource type.
We'll have more to say about limiting the scope of repairs later on.
@PP
It is reasonable to worry about the time it takes to group monitors,
so the author ran just the group monitor setup and removal parts of
@C { KheEjectionChainNodeRepairTimes } 10000 times on a typical
instance (BGHS98) and measured the time taken.  This was 31.35
seconds, or about 0.003 seconds per setup"/"remove.  This is not
significant if it is done infrequently.
@PP
Next, @C { KheEjectorSolveBegin } and @C { KheEjectorSolveEnd }
carry out the actual solve.  We've omitted some code here which
optionally installs a limit monitor.  Then, following our general
policy concerning group monitors
(Section {@NumberOf general_solvers.grouping.intro}),
the group monitors created by this function are deleted.  Finally,
the earlier call to @C { KheGroupCorrelatedMonitors } is undone,
and @C { true } is returned if the solution was improved.
@End @SubSection

@SubSection
    @Title { Focus groupings for ejection chains }
    @Tag { eject.practice.focus }
@Begin
@LP
Top-level ejection chain functions need focus groupings for their
start group monitors (but not their continue group monitors, since
they use the solution object for that), and for their limit
monitors.  This section describes the public functions, defined
in @C { khe_se_focus.c }, which return these group monitors.
@PP
@C { KheEjectionChainNodeRepairTimes } uses the group monitor returned by
@ID @C {
KHE_GROUP_MONITOR KheNodeTimeRepairStartGroupMonitorMake(KHE_NODE node);
}
as its start group monitor.  The result has sub-tag
@C { KHE_SUBTAG_NODE_TIME_REPAIR }.  Its children are monitors, or
correlation groupings of monitors where these are already present, of
two kinds.  First are all assign time, prefer times, spread events,
order events, and ordinary demand monitors that monitor the meets
of @C { node } and its descendants, plus any meets whose assignments
are fixed, directly or indirectly, to them.  Second are all resource
monitors.  Only preassigned resources are assigned during time repair,
but those assignments may cause resource defects which can only be
repaired by changing time assignments, just because the resources
involved are preassigned.
@PP
@C { KheEjectionChainLayerRepairTimes } chooses one of the group monitors
returned by
@ID @C {
KHE_GROUP_MONITOR KheLayerTimeRepairStartGroupMonitorMake(
  KHE_LAYER layer);
KHE_GROUP_MONITOR KheLayerTimeRepairLongStartGroupMonitorMake(
  KHE_LAYER layer);
}
as its start group monitor, depending on option
@C { es_layer_repair_long }.  The result has
sub-tag @C { KHE_SUBTAG_LAYER_TIME_REPAIR }, with the same children
as before, only limited to those that monitor the meets and resources
of @C { layer }, or (if @C { es_layer_repair_long } is @C { true})
of layers whose index number is less than or equal to @C { layer }'s.
@PP
@C { KheEjectionChainRepairResources } uses the group monitor returned
by
@ID @C {
KHE_GROUP_MONITOR KheStartGroupMonitorMake(KHE_SOLN soln,
  KHE_RESOURCE_TYPE rt);
}
for its start group monitor.  The result has sub-tag
@C { KHE_SUBTAG_RESOURCE_REPAIR }, and its children are the following
monitors (or correlation groupings of those monitors, where those already
exist):  the assign resource, prefer resources, and avoid split
assignments monitors, and the resource monitors that monitor the
tasks and resources of type @C { rt }, or all of them if @C { rt }
is @C { NULL }.
# If the tasking is for a
# particular resource type, only monitors of entities of that type
# are included.
@PP
To allow an ejection chain to unassign meets temporarily but prevent
it from leaving meets unassigned in the end, a limit monitor is
imposed which rejects chains that allow the total cost of assign
time defects to increase.  This monitor is created by calling
@ID @C {
KHE_GROUP_MONITOR KheGroupEventMonitors(KHE_SOLN soln,
  KHE_MONITOR_TAG tag, KHE_SUBTAG_STANDARD_TYPE sub_tag);
}
passing @C { KHE_ASSIGN_TIME_MONITOR_TAG } and
@C { KHE_SUBTAG_ASSIGN_TIME } as tag parameters.
@PP
To prevent the number of unmatched demand tixels from increasing,
when that is requested by the @C { resource_invariant } option,
the group monitor returned by function @ID @C {
KHE_GROUP_MONITOR KheAllDemandGroupMonitorMake(KHE_SOLN soln);
}
is used as a limit monitor.  Its sub-tag is @C { KHE_SUBTAG_ALL_DEMAND },
and its children are all ordinary and workload demand monitors.
Correlation groupings are irrelevant to limit monitors, so these last
two functions take no account of them.
@End @SubSection

@SubSection
    @Title { Augment options }
    @Tag { eject.practice.options }
@Begin
@LP
Ejection chain code consults many options.  Rather than clumsy
retrievals of these options from an @C { options } object each
time they are needed, the ejection chain code retrieves each
option just once, and stores its value in an object of type
@C { KHE_AUGMENT_OPTIONS }.  This object is passed to the ejector
as an argument of function @C { KheEjectorSolveBegin }, which
passes it back to each augment function, making the options
immediately available to them.
@PP
Type @C { KHE_AUGMENT_OPTIONS } is defined in @C { khe_solvers.h } by
@ID @C {
typedef struct khe_augment_options_rec *KHE_AUGMENT_OPTIONS;
}
and made concrete at the start of @C { khe_se_solvers.c }:
@ID @C {
struct khe_augment_options_rec {

  /* time repair options */
  bool				vizier_node;
  bool				layer_repair_long;
  bool				nodes_before_meets;
  KHE_OPTIONS_KEMPE		use_kempe_moves;
  bool				use_fuzzy_moves;
  bool				no_ejecting_moves;

  /* resource repair options */
  bool				widening_off;
  bool				reversing_off;
  bool				balancing_off;
  int				swap_widening_max;
  int				move_widening_max;
  int				balancing_max;
  bool 				full_widening_on;
  bool				optimal_on;
  int				optimal_width;

  /* options that are set by functions, not by the user */
  bool				repair_times;
  bool				use_split_moves;
  KHE_KEMPE_STATS		kempe_stats;
  KHE_NODE			limit_node;
  bool				repair_resources;
  KHE_SOLN			soln;
  KHE_FRAME			frame;
  KHE_EVENT_TIMETABLE_MONITOR	etm;
  KHE_MTASK_FINDER		mtask_finder;
};
}
The reader can see now why we describe @C { khe_se_solvers.c } as
volatile:  these options control various promising ideas that may come
and go.  The options will come and go with the ideas.
@PP
A few of these options are more permanent than the others and deserve
special notice.
Option @C { repair_times } is @C { true } when augment functions are
permitted to change the assignments of meets, and @C { repair_resources }
is @C { true } when they are permitted to change the assignments of
tasks.  At least one of these must be @C { true }, since otherwise
the augment functions can change nothing.  It is also acceptable for
both to be @C { true }, although that can lead to slow runs.
@PP
Option @C { soln } is the solution under repair; @C { frame } is the
common frame; @C { etm } is an event timetable monitor, from which an
augment function can retrieve the tasks running at a given time; and
@C { mtask_finder } is an mtask finder object, as in
Section {@NumberOf resource_structural.mtask_finding}.
# It is handy to have direct access to these objects, and not have to
# retrieve them from a @C { KHE_OPTIONS } object each time they are needed.
@PP
A @C { KHE_AUGMENT_OPTIONS } object is created by a call to
@C { KheAugmentOptionsMake }.  We won't present its implementation;
it simply creates an object and fills its fields with values
retrieved from the @C { options } object.
# In this way these
# values become immediately accessible; they don't have to be retrieved
# from a @C { KHE_OPTIONS } object each time they are needed.
@PP
Here now is the full list of options that control the top-level
ejection chain functions.  These options are quite separate from
the options used to control ejector objects, which are listed
elsewhere (Section {@NumberOf eject.solving}).
@PP
First come options that do not need to be stored in the
@C { KHE_AUGMENT_OPTIONS } object, because they affect only
the main functions, not the augment functions:
@TaggedList

@DTI { @F rs_ejector_off }
{
A Boolean option which, when @C { true }, causes
@C { KheEjectionChainRepairResources } to do nothing.
}

@DTI { @F { es_ejector1 }, @F { es_ejector2 } }
{
These two options hold ejector objects.  @C { KheEjectionChainNodeRepairTimes }
and @C { KheEjectionChainLayerRepairTimes } use the ejector object stored
under key @F { es_ejector1 }, while @C { KheEjectionChainRepairResources }
uses the ejector object stored under key @F { es_ejector2 }.  There is no
need for the user to set these options, since they will be set the first
time they are needed; but retrieving them at the end of the solve can be
useful, since they will then contain statistics on the performance of the
ejection chain algorithm.
}

# @DTI { @F es_group_limit_resources_off }
# {
# A Boolean option which, when @C { true }, instructs
# @C { KheGroupCorrelatedMonitors }
# (Section {@NumberOf general_solvers.grouping.functions})
# to not group limit resources monitors for the same
# events and resource types.  Grouping these monitors is not exact,
# which is why this option is offered to turn it off, but it may be
# helpful anyway, to reduce the density of these constraints.
# }

@EndList
Next come those options retrieved from @C { options } by
@C { KheAugmentOptionsMake } and stored in the @C { KHE_AUGMENT_OPTIONS }
object.  First, those concerned with time assignment:
@TaggedList

@DTI { @F es_vizier_node }
{
A Boolean option which, when @C { true }, instructs
@C { KheEjectionChainNodeRepairTimes } and
@C { KheEjectionChainLayerRepairTimes } to insert a vizier node
(Section {@NumberOf time_structural.nodes.vizier}) temporarily
while they run.
}

@DTI { @F es_layer_repair_long }
{
A Boolean option which, when @C { true }, instructs
@C { KheEjectionChainLayerRepairTimes } to target every
layer up to and including the current layer when repairing the
current layer.  Otherwise only the current layer is targeted.
}

@DTI { @F es_nodes_before_meets }
{
A Boolean option which, when @C { true }, instructs augment
functions that try both node swaps and meet moves to try the
node swaps first.
}

@DTI { @F es_kempe_moves }
{
This option determines whether augment functions that move
meets use Kempe moves in addition to ejecting and basic
ones (Section {@NumberOf time_solvers.kempe}).  Its possible
values are @F { true }, meaning to use them, @C { false },
meaning to not use them, and @F { large_layers } (the default),
meaning to use them when moving the meets of nodes that lie in
layers of large duration relative to the cycle duration,
reasoning that swaps are usually needed when such meets are moved.
}

@DTI { @F es_fuzzy_moves }
{
A Boolean option which, when @C { true }, instructs augment
functions that move meets to try fuzzy meet moves
(Section {@NumberOf time_solvers.repair.meet_set}) in addition
to the other kinds of meet moves.  If they do, to conserve
running time they only do so when repairing a defect of the
current best solution, not when repairing a defect introduced
by a previous repair.  At present the @C { width }, @C { depth },
and @C { max_meets } arguments passed to @C { KheFuzzyMeetMove }
are fixed constants.
}

@DTI { @F es_no_ejecting_moves }
{
A Boolean option which, when @C { true }, instructs augment
functions that assign and move meets to not use ejecting moves,
only basic ones (Section {@NumberOf time_solvers.kempe}).
}

# @DTI { @F es_no_limit_busy_sequences }
# {
# A Boolean option which, when @C { true }, instructs augment
# functions that repair limit busy times defects to move only
# single assignments, not sequences of assignments.
# }

# @DTI { @F es_max_augments }
# {
# An upper limit on the number of augments tried on each attempt
# to repair a main loop defect.  The default value is @C { 120 },
# which may be more effective than a larger number, especially
# when there is a time limit, because it helps the search to
# not waste time on lost causes.
# }

# @DTI { @F es_max_repairs }
# {
# An upper limit on the number of repairs tried on each attempt
# to repair a main loop defect.  The default value is @C { INT_MAX }.
# }

# @DTI { @F es_no_promote_defects }
# {
# (@I { withdrawn }) A Boolean option which, when @C { true }, instructs
# an ejector not to promote defects (Section {@NumberOf ejection.promotion}).
# }

# @DTI { @F es_fresh_visits }
# {
# A Boolean option which, when @C { true }, instructs an ejector to
# make fresh visits (Section {@NumberOf ejection.articulated}).
# }

# @DTI { @F es_max_beam }
# {
# An integer option which gives the maximum number of monitors that
# may appear in a beam (Section {@NumberOf ejection.beams}).  The
# default value is 1, producing ejection chains rather than beams.
# }

# @DTI { @F es_limit_defects }
# {
# An option whose value is either @C { "unlimited" } or an integer.
# This integer is a limit on the number of defects handled by the
# main loop of the ejector.  Each time the main list of defects
# is copied and sorted, if its size exceeds this limit, defects are
# dropped from the end until it doesn't.  When the option is not
# set, or its value is @C { "unlimited" }, no limit is applied.
# }

@EndList
Next come options concerned with resource assignment:
@TaggedList

@DTI { {@F es_widening_off}, {@F es_reversing_off}, {@F es_balancing_off}  }
{
Boolean options which, when @C { true }, cause widening, reversing,
and balancing to be omitted from task move repairs.
@DP
@I { Note:  Task move repairs treat tasks @C { t } for which
@C { KheTaskNeedsAssignmentHasCost(t) } is @C { false } as free
time.  Inserting this note in the right place is still to do.
}
}

@DTI { {@F es_move_widening_max} }
{
When widening is in effect (when @F es_widening_off is @C { false }),
this integer option determines the maximum number of frame time groups
that widening covers when moves are being tried, in addition to
whatever frame time groups the mtask or mtask set being moved already
covers.  The default value is 4.  Value 0 turns move widening off.
}

@DTI { {@F es_swap_widening_max} }
{
When widening is in effect (when @F es_widening_off is @C { false }),
this integer option determines the maximum number of frame time groups
that widening covers when swaps are being tried, in addition to
whatever frame time groups the mtask or mtask set being swapped already
covers.  The default value is 16.  Value 0 turns swap widening off.
}

@DTI { {@F es_balancing_max} }
{
When balancing is in effect (when @F es_balancing_off is @C { false }),
this integer option determines the maximum number of repairs that
move a task set in the other direction to the main move.  The
default value is 12.
}

@DTI { {@F es_full_widening_on} }
{
A boolean option which, when @C { true }, includes full widening
in task move repairs.  This swaps timetables from the task being
moved back to the start or forward to the end of the cycle.  It
is effective, but its slowness typically increases cost on solves
limited by time.
}

@DTI { {@F es_optimal_on} }
{
A boolean option which, when @C { true }, causes optimal widened
task set moves to be used.  In that case, @F es_optimal_width
just below is consulted, as is {@F es_balancing_off} above, but
the other options from {@F es_widening_off} down to here are
ignored.
}

@DTI { {@F es_optimal_width} }
{
When @F es_optimal_on is @C { true }, this integer option determines
how many extra days to include in the optimal move.  The default value,
6, adds 6 extra days.  When these are added to the one day typically
involved in the original move, this covers a full week.
}

@EndList
The following options are set within
@C { KheEjectionChainRepairResources } and similar functions,
making it futile for the user to set them:
@TaggedList

@DTI { @F es_repair_times }
{
A Boolean option which, when @C { true }, lets augment functions
change meet assignments.  @C { KheEjectionChainNodeRepairTimes } and
@C { KheEjectionChainLayerRepairTimes } set it to @C { true }, while
@C { KheEjectionChainRepairResources } sets it to @C { false }.
}

@DTI { @F es_split_moves }
{
A Boolean option which, when @C { true }, instructs augment
functions that move meets to try split meet moves in addition
to other kinds of meet moves.  @C { KheGeneralSolve2024 } sets
this option to @C { true } when the instance contains soft split
events or distribute split events constraints, reasoning that
they may not have been satisfied by structural solvers.
}

@DTI { @F es_limit_node }
{
This option holds a node object.  When it is non-@C { NULL }, it causes
augment functions that assign and move meets to limit their repairs to
the descendants of that node.  This option is set by all three functions.
}

@DTI { @F es_repair_resources }
{
A Boolean option which, when @C { true }, lets augment functions
change task assignments.  @C { KheEjectionChainNodeRepairTimes } and
@C { KheEjectionChainLayerRepairTimes } set it to @C { false }, while
@C { KheEjectionChainRepairResources } sets it to @C { true }.
}

@EndList
As mentioned earlier, one can expect this list to change as good
ideas come and go.
@End @SubSection

@SubSection
    @Title { How to write an augment function }
    @Tag { eject.practice.augment }
@Begin
@LP
An augment function has type
@ID @C {
bool (*KHE_EJECTOR_AUGMENT_FN)(KHE_EJECTOR ej,
  KHE_AUGMENT_OPTIONS ao, KHE_MONITOR d);
}
The parameters are the ejector @C { ej } passed to @C { KheEjectorSolve },
a set of options @C { ao } which may be consulted to influence the
detailed behaviour of the augment function, and one defect @C { d }
that the augment function is supposed to repair.  When ejection beams
are in use the ejector still only expects the user's augment functions
to repair a single defect.  Thus one can switch from chains to beams
and back again without changing any augment functions.
@PP
It is a precondition that @C { d }'s cost exceeds its lower bound, and
that reducing its cost to its lower bound would be a step towards a
successful augment.  The return value says whether the augment
successfully reduced the solution cost or not, and must be @C { true }
if some call to @C { KheEjectorRepairEnd } (see below) returns
@C { true }, or @C { false } otherwise.  The ejector relies on
this value and it must be right.
@PP
Augment functions often look like this, although not necessarily exactly:
@ID {0.98 1.0} @Scale @C {
bool ExampleAugment(KHE_EJECTOR ej, KHE_AUGMENT_OPTIONS ao, KHE_MONITOR d)
{
  KHE_ENTITY e;  bool success;  REPAIR r;
  e = SomeSolnEntityRelatedTo(d);
  if( !KheEntityVisited(e) )
  {
    KheEntityVisit(e);
    for( each r in RepairsOf(e) )
    {
      KheEjectorRepairBegin(ej);
      success = Apply(r);
      if( KheEjectorRepairEnd(ej, 0, success) )
	return true;
    }
    if( KheEjectorCurrMayRevisit(ej) )
      KheEntityUnVisit(e);
  }
  return false;
}
}
Function @C { SomeSolnEntityRelatedTo } uses @C { d } to identify
some entity (node, meet, task, etc.) that will be changed by the
repairs, but that should only be changed if it has not already
been visited (tested by calling @C { KheMeetVisited } etc. from
Section {@NumberOf "solutions.top.visit.numbers"}).  After the
visit, the boilerplate code
@ID @C {
if( KheEjectorCurrMayRevisit(ej) )
  KheEntityUnVisit(e);
}
marks the entity unvisited if revisiting is allowed.
@PP
Each application of one repair must be bracketed by calls to
@C { KheEjectorRepairBegin } and @C { KheEjectorRepairEnd },
as shown.  We'll return to these two functions shortly.
@PP
The ejector object checks the time limit.  Augment functions do not
need to, and should not, since that could cause option
@F { es_whynot_monitor_id } (Section {@NumberOf eject.solving})
to stop too soon.
@PP
Since writing the above, it has occurred to the author that the
visited entity related to @C { d } could be @C { d } itself.  The
code above that relates to visiting @C { e } is now applied to
@C { d } behind the scenes in the ejector object.  This has the
usual effect of prohibiting exponential searches, and it means
that the writer of an augment function can safely drop all
visiting code, producing this:
@ID {0.98 1.0} @Scale @C {
bool ExampleAugment(KHE_EJECTOR ej, KHE_AUGMENT_OPTIONS ao, KHE_MONITOR d)
{
  bool success;  REPAIR r;
  for( each r in RepairsOf(d) )
  {
    KheEjectorRepairBegin(ej);
    success = Apply(r);
    if( KheEjectorRepairEnd(ej, 0, success) )
      return true;
  }
  return false;
}
}
The augment function is also conceptually simpler this way:  it
searches a graph whose nodes are defects and whose edges are repairs,
without revisiting any node.  Of course, the search is different,
and the question of whether it is better or worse must be decided
empirically.  The author has successfully removed all visiting code
from his nurse rostering augment functions:  running times are often
larger, but cost is nearly always lower, often significantly lower.
@PP
Function @C { RepairsOf } builds a set of alternative repairs @C { r }
of @C { e } or @C { d }, and @C { Apply(r) } stands for the code that
applies repair @C { r }.  In practice, repairs just need to be iterated
over and applied; an explicit set is not needed.  Nor is
there any need for the augment function to follow any particular
structure; anything that generates a sequence of pairs of calls
to @C { KheEjectorRepairBegin } and @C { KheEjectorRepairEnd }
is acceptable.
@PP
For example, some expensive repairs are only worth trying when
repairing a defect that is really present in the solution, not
introduced by a previous repair.  This can be effected by
@ID @C {
if( KheEjectorCurrLength(ej) == 1 )
{
  KheEjectorRepairBegin(ej);
  ... apply expensive repair ...
  if( KheEjectorRepairEnd(ej, 0, success) )
    return true;
}
}
This works because, as mentioned earlier,
@C { KheEjectorCurrLength(ej) } returns 1 when the augment function
was called from the main loop, 2 when the augment function was called
by an augment function called from the main loop, and so on.
@PP
Functions @C { KheEjectorRepairBegin } and @C { KheEjectorRepairEnd }
are supplied by KHE:
@ID @C {
void KheEjectorRepairBegin(KHE_EJECTOR ej);
bool KheEjectorRepairEnd(KHE_EJECTOR ej, int repair_type, bool success);
}
Calls to them must occur in unnested matching pairs.  A call to
@C { KheEjectorRepairBegin } informs @C { ej } that a repair is
about to begin, and the matching call to @C { KheEjectorRepairEnd }
informs it that that repair has just ended.  The repair is undone and
redone as required behind the scenes by @C { KheEjectorRepairEnd },
using marks and paths, so undoing is not the user's concern.
@PP
The @C { repair_type } parameter of @C { KheEjectorRepairEnd } is
used to gather statistics about the solve
(Section {@NumberOf eject.statistics}).  It may be 0 if statistics
are not wanted.
@PP
The @C { success } parameter tells the ejector whether the caller
thinks the current repair was successful (that is, ran to completion).
If it is @C { false }, the ejector undoes the partially completed
repair and forgets that it ever happened.  If it is @C { true },
the ejector checks whether the repair reduced the cost of the
solution, whether there is a single new defect worth recursing on,
and so on.  The writer of an augment function can forget that all
this is happening behind the scenes.
@PP
If @C { KheEjectorRepairEnd } returns @C { true }, the ejector has
found a successful chain.  When this happens, the rule is that the
augment function should return @C { true } immediately.  It does not
matter whether any entity is marked unvisited or not before exit.
@PP
However, there is an exception to this rule.  We illustrate this by
the following example.
@PP
Suppose that, in order to encourage ejection chains to remove a cluster
busy times defect, some days when the resource will be busy are chosen,
all meets assigned the resource outside those days are unassigned, and
repairs are tried which move those meets to the chosen days.
@PP
While the repairs are underway, it is desired to limit the domains of
the resource's meets to the chosen days, to keep the repairs on track.
So the repair altogether consists of unassigning some of the resource's
meets and adding a meet bound to each of the resource's meets.
@PP
Whether the repair is successful or not, after it and the chains below
it are finished, the meet bound must be removed from the resource's
meets, since the domains of the meets should not be restricted
permanently.  If the repair is unsuccessful, the meet bound is removed
by the ejector as part of undoing the repair.  But if the repair is
successful there is a problem, because the repair is not undone.
So in this case, in between receiving the @C { true } result from
@C { KheEjectorRepairEnd } and returning @C { true } itself, the
augment function should remove the meet bounds that it added.
@PP
In general, this kind of cleanup should not change the cost of
the solution.  It might remove meet or task bounds, unfix meets
or tasks, and so on.  Because it is done after a successful
chain, it cannot be assumed that the solution is in the same
state as when the repair began, although the user may be able
to prove that certain aspects of it cannot have changed, based
on his knowledge of what the augment functions do.  In the
example, if no repairs remove meet bounds other than those
they add themselves, then the meet bound will still be present
and it is safe to delete it.
@PP
@I { Repairs, multi-repairs, and augment functions. }
The author has made several attempts to organize the code that
makes up augment functions hierarchically.  At present the hierarchy
has three levels:  repairs, multi-repairs, and augment functions.
# This has not been
# entirely successful, especially since entity visiting code disappeared
# behind the scenes.  However
@PP
A @I repair is a code fragment that makes one or more changes to
the solution, aiming to repair some defect.  The changes work
together; they are not alternatives.  In concrete terms, a repair is the
code lying between one call to @C { KheEjectorRepairBegin } and
the matching call to @C { KheEjectorRepairEnd }.  Often we include
those two calls in the repair, but sometimes we speak of the repair
as just the code between them.  A repair is often packaged into a
function, which is then called a @I { repair function }.  It may be
defined elsewhere (@C { KheMeetMove }, @C { KheTaskAssign },
etc.).  We also refer to the changes made to the solution by this
code as one repair.
@PP
A @I multi-repair is a code fragment that tries a sequence of
zero or more alternative repairs, each enclosed as usual in
@C { KheEjectorRepairBegin } and @C { KheEjectorRepairEnd }.  A
@I { multi-repair function } is a function whose body is a multi-repair.
@PP
An @I { augment function } is a multi-repair function of type
@C { KHE_EJECTOR_AUGMENT_FN } which is passed to an ejector.  It
typically calls repair functions and other multi-repair functions.
@End @SubSection

# @SubSection
#     @Title { Obtaining ejector objects }
#     @Tag { eject.repair.ejectors }
# @Begin
# @LP
# The first issue is where the ejector objects come from.  They
# need to be loaded with the right augment functions, and they
# need to be available after the solve, to give access to their
# statistics.  KHE offers a function for creating an ejector
# object with its augment functions loaded:
# @ID {0.98 1.0} @Scale @C {
# KHE_EJECTOR KheEjectionChainEjectorMake(KHE_OPTIONS options, HA_ARENA a);
# }
# This returns a new ejector object, ready to solve with, stored in
# arena @C { a }.  Its schedule come from the @C { es_schedule }
# option of @C { options }, and its augment functions are the ones
# defined by KHE.  However, the three ejection chain solvers obtain
# their ejector objects indirectly, by calling
# @ID {0.98 1.0} @Scale @C {
# KHE_EJECTOR KheEjectionChainEjectorOption(KHE_OPTIONS options, char *key);
# }
# This retrieves the object from @C { options } with the given key,
# casts it to @C { KHE_EJECTOR }, and returns it; or if there is no
# such object, it calls @C { KheEjectionChainEjectorMake(options, a) }
# to create one, adds it to @C { options } under the given key, and
# returns it.  The ejector's lifetime is the lifetime of @C { options },
# because @C { a } is @C { KheOptionsArena(options) }.
# @PP
# For @C { KheEjectionChainNodeRepairTimes } and
# @C { KheEjectionChainLayerRepairTimes } the key is @C { "es_ejector1" };
# for @C { KheEjectionChainRepairResources } it is @C { "es_ejector2" }.
# This allows for collecting two sets of statistics, for time
# assignment and resource assignment.
# @End @SubSection

@SubSection
    @Title { Limiting the scope of changes }
    @Tag { eject.practice.limiting }
@Begin
@LP
Ejection chains work best when they are free to follow chains into
any part of a solution, and make any repairs that help.  This
freedom can conflict with the caller's desire to limit the scope
of the changes they make, typically because initial assignments
have not yet been made to some parts of the solution, and an
ejection chain repair should not anticipate them.
@PP
For example, suppose resource @M { r } is preassigned to some
tasks, and there are others it could be assigned to.  The
preassigned tasks go into @M { r }'s timetable when their meets
are assigned times, and could then create resource defects that
an ejection chain time repair algorithm knows about.  Suppose a
limit busy times underload defect is created (quite likely when
the workload on some day first becomes non-zero), and its augment
function tries (among other things) to assign more tasks to
@M { r } to increase its workload on that day.  This is not
done at present, but it is plausible.  Then there will be an
unexpected burst of resource assignment during time assignment.
@PP
One romantic possibility is to `let a thousand flowers bloom'
and just accept such repairs.  The problem with this is that
a carefully organized initial assignment can be much better
than the result of a set of uncoordinated individual repairs.
@PP
Another possibility is to fix the assignments of all variables
beyond the scope of the current phase of the solve to their
current values, often null values.  This is a very reliable
approach, and arguably the most truthful, because it says to the
ejection chain algorithm, in effect, `for reasons beyond your
comprehension, you are not permitted to change these variables.'
But it suffers from a potentially severe efficiency problem:  a
large amount of time could be spent in discovering a large number
of repairs, which all fail through trying to change fixed variables.
@PP
Yet another possibility is to have one ejector object for each
kind of call (one for repairing time assignments, another for
repairing resource assignments, and so on), with different
augment functions.  The augment functions for time repair would
never assign a task, for example.  This was the author's original
approach, but as the code grew it became very hard to maintain.
@PP
At present the author is using the following somewhat ad-hoc ideas
to limit the scope of changes.  They do the job well at very little
cost in code and run time.
@PP
The start group monitor is one obvious aid to restricting the scope
of a call.  For example, time repair calls do not include event
resource monitors in their start group monitors.
@PP
Many repairs move meets and tasks, but do not assign them.  It seems
that once a meet or task has been assigned, it is always reasonable to
move it during repair.  So the danger areas are augment functions that
assign meets and tasks, not augment functions that merely move them.
@PP
Augment functions for assign time and assign resource defects must
contain `dangerous' assignments.  But suppose that the assign time
or assign resource monitor for some meet or task is not in the start
group monitor.  Then a repair of that monitor cannot occur first on any
chain; and if the meet or task is unassigned to begin with, it
cannot occur later either, since the monitor starts off with
maximum cost, so its cost cannot increase, and only monitors whose
cost has increased are repaired after the first repair on a
chain.  So assign time and assign resource augment functions can
be included without risk of the resulting time and resource
assignments being out of scope.  This is just as well,
since they are needed after ejecting meet and task moves.
@PP
If it can be shown, as was just done, that certain events will
remain unassigned, then they can have no other event defects,
since those require the events involved to be assigned.
Similarly, unassigned event resources will never give rise to
other event resource defects.
@PP
Another idea is to add options to the options object that control
which repairs are tried.  This is as general as different ejector
objects with different augment functions are, but, if the options
are few and clearly defined, it avoids the maintenance problems.
If many calls on augment functions achieve nothing because options
prevent them from trying things, that would be an efficiency problem,
but there is no problem of that kind in practice.
@PP
We've already seen that the @C { KHE_AUGMENT_OPTIONS } object contains
a @C { repair_times } field, which when @C { true } allows repairs
that assign and move meets, and a @C { repair_resources } field, which
when @C { true } allows repairs that assign and move tasks.  It takes
virtually no code or time to consult these options; often, just one
test at the start of an augment function is enough.
@PP
When moving a meet, its chain of assignments is followed upwards,
trying moves at each level.  But if the aim is to repair only a
small area (one runaround, say), then even if a repair starts
within scope, it can leave it as it moves up the chain.  This has
happened and caused problems.  So the @C { KHE_AUGMENT_OPTIONS }
object contains a @C { limit_node } field, whose value is a node.
If it is non-@C { NULL }, meet assignments and moves are not permitted
outside its proper descendants.
@PP
{0.95 1.0} @Scale @C { KheEjectionChainNodeRepairTimes }
and {0.95 1.0} @Scale @C { KheEjectionChainLayerRepairTimes } set
option @C { es_repair_times } to @C { true },
@C { es_repair_resources } to @C { false }, and
@C { es_limit_node } to the parent node, or to @C { NULL } if it is
the cycle node.  The @C { false } value for @C { es_repair_resources }
solves the hypothetical problem, given as an example at the start
of this section, of limit busy times repairs assigning resources
during time assignment.
@PP
{0.95 1.0} @Scale @C { KheEjectionChainRepairResources } sets option
@C { es_repair_times } to @C { false }, option @C { es_repair_resources }
to @C { true }, and option @C { es_limit_node } to @C { NULL }.
Setting @C { es_repair_times } to @C { true } here would also
be reasonable, although slow; it would allow the repairs to try
meet moves while repairing task assignments.
@End @SubSection

@EndSubSections
@End @Section

@Section
    @Title { Repairing time assignments }
    @Tag { eject.time }
@Begin
@LP
# Arguably, it is wrong to refer
# to this as an augment function, because so much of what an
# augment function has to do is done behing the scenes, before
# or after the function is called, and within the calls to
# @C { KheEjectorRepairBegin } and @C { KheEjectorRepairEnd }.
# However, this is our terminology and we're sticking with it.
# The user-defined functions passed to the ejector are augment
# functions in this sense, but so are many smaller functions
# that try just a few alternative repairs.
# @PP
# The augment functions passed to the ejector are private to KHE.
# This section explains what they do in detail.  It may be somewhat
# out of date.
# @PP
# The augment functions consult several options.  The
# @C { repair_times }, @C { limit_node }, and
# @C { repair_resources } options are particularly important
# because they limit the scope of repairs.  They cannot be set
# by the user---or rather, they can, but that would be futile
# because they are reset within the main functions.
# Any repair which assigns or moves a meet consults
# @C { repair_times }, and only proceeds if it is
# @C { true }.  It tries moving each ancestor of the meet, since
# that will also move the original meet; but if
# @C { limit_node } is non-@C { NULL }, it omits moves
# of meets lying within nodes which are not proper descendants of
# @C { limit_node }.  Any repair which assigns or moves a
# task consults @C { repair_resources }, and only
# proceeds if it is @C { true }.
This section is concerned with the augment functions supplied
by KHE for repairing defects in the assignments of times to
meets.  We'll start by presenting the full list of repairs
used by the KHE augment functions that do this.
@PP
@I { Meet bound repairs } reduce the domains of all meets
assigned a given resource @C { r } to a subset of a given
time group @C { tg }, by adding @C { tg } as a meet bound
to all those meets.  Any meets assigned times outside @C { tg }
are unassigned first.  This is used to fix resource overload
problems by moving meets away from over-used times; but it gets
very little use in practice, because both resource repair and
time repair have to be enabled if it is to be called.
@PP
@I { Node meet swaps } call repair function @C { KheNodeMeetSwap }
(Section {@NumberOf time_solvers.misc}) to swap over the assignments
of the meets of two nodes.  If the @C { nodes_before_meets }
option is @C { true }, then if node swaps are tried at all, they
are tried before (rather than after) meet moves.
@PP
@I { Meet moves } call repair function @C { KheTypedMeetMove }
(Section {@NumberOf time_solvers.kempe}) to move the assignments of
meets, performing either a basic move, an ejecting move, or a Kempe
move as requested.  Where it is stated below that a Kempe meet
move is tried, it is in fact tried only when the @C { kempe_moves }
option is @F { true }.  Where it is stated below that an ejecting
meet assignment or move is tried, a basic meet assignment or move
is tried instead when @C { no_ejecting_moves } is @C { true }.
# , or it is
# @F { large_layers } and the meet to be moved lies in at least
# one layer whose duration is at least 80% of the duration of the
# cycle.
@PP
@I { Kempe"/"ejecting meet moves } are multi-repairs consisting
of one or two alternative repairs, first a Kempe meet move, then
an ejecting meet move with the same parameters.  The ejecting meet
move is omitted when the Kempe meet move reports that it did only
what a basic meet move would have done, since in that case the
ejecting move is identical to the Kempe move.  This sequence
is similar to making an ejecting move and then, on the next
repair, favouring a particular reassignment of the ejected
meet(s) which is likely to work well.
# Fuzzy and split moves
# may follow the Kempe and ejecting meet moves, as explained above.
@PP
@I { Fuzzy meet moves } move meets by calling repair function
@C { KheFuzzyMeetMove } (Section {@NumberOf time_solvers.repair.meet_set}).
Fuzzy meet moves are not mentioned below, but they are tried after Kempe
and ejecting meet moves, although only when the @C { fuzzy_moves } option
is @C { true } and the current length is 1.
@PP
@I { Split moves } split a meet into two and Kempe-move one of the
fragments.  Conversely, @I { merge moves } Kempe-move one meet to
adjacent to another and merge the two fragments.  Split and merge
moves are used similarly to fuzzy meet moves:  although not mentioned
below, they are tried after Kempe and ejecting meet moves, but only when
the @C { split_moves } option is @C { true } and the current length is 1.
These Kempe moves are not influenced by the @C { kempe_moves } option.
# @PP
# @I { Ejecting task assignments and moves }, which assign or move a
# task to a given resource and then unassign any clashing tasks
# (Section {@NumberOf resource_solvers.kempe}).
# @PP
# @I { Ejecting task-set moves }, which use ejecting task moves to
# move a set of tasks to a common resource, succeeding only if all
# of the moves that change anything succeed.
@PP
@I { Meet-and-task moves } Kempe-move a meet at the same time
as moving one of its tasks, succeeding only if both moves succeed.
# @PP
# Each repair is enclosed in calls to @C { KheEjectorRepairBegin }
# and @C { KheEjectorRepairEnd } as usual.  In the more complex cases,
# such as the last two on the list above, the @C { success } argument
# of @C { KheEjectorRepairEnd } is set to @C { true } only if all
# parts of the repair succeed.  Some of the more complex repairs are
# tried only when the current length is 1, that is, when the defect
# being repaired is truly present, not introduced by some other repair.
# @PP
# A @I { complex repair } is a combined repair that changes more
# than just a few entities.  Complex repairs are problematical,
# because of the many choices, and because they are likely to
# introduce many defects.  If they are used at all, it seems best
# to aim for a moderate number of changes, and a moderate number
# of alternatives.  To conserve running time, it may be best to
# allow them only at length 1, limiting them to repairing real
# defects, not defects introduced by previous repairs.
# @PP
# Complex repairs are similar to VLSN searches:  both rearrange a
# large chunk of the solution.  However, there are differences.
# A VLSN search has a single outcome which aims to reduce the cost
# of the solution.  A complex repair is one element of a set of
# alternative repairs, and it aims to either reduce the cost of
# the solution or else introduce just one significant new defect.
# @PP
# Some alternative repairs are naturally tried one after another.
# The ejecting task moves of a given task to each resource in its
# domain is one example.  Here are three less obvious, but nevertheless
# very useful sequences of alternative repairs.
# @PP
# A @I { resource underload repair } for resource @M { r } and time
# group @M { g } is a sequence of alternative repairs which aim to
# increase the number of times @M { r } is busy within @M { g }.
# Unless @M { g } is the whole cycle, for each task assigned @M { r }
# whose @I overlap (the number of times it is running within @M { g })
# is less that its duration, it tries all ejecting meet moves of the
# task's meet which increase its overlap.  After that it tries an
# ejection tree repair like the one described below for cluster busy
# times defects, which aims to empty out the entire time group---a
# quite different way to remove the defect.
# @PP
# A @I { resource overload repair } for resource @M { r } and time
# group @M { g } is a sequence of alternative repairs which aims to
# decrease the number of times @M { r } is busy within @M { g }.
# First, for each task assigned but not preassigned @M { r } whose
# overlap is non-zero, it tries all ejecting task moves of the task
# to its domain's resources.  Then, for each task assigned (including
# preassigned) @M { r } whose overlap is non-zero, it tries all
# ejecting meet moves of the task's meet which decrease the overlap.
@PP
Most of these repairs are packaged into function @C { KheMeetMultiRepair },
which tries, using whatever means the options settings allow, to move
the assignment of a given meet.
@PP
Following is a description of what each augment function does when
given a non-group monitor with non-zero cost.  When given a group
monitor (which must come from a correlation grouping) with non-zero
cost, since the elements of the group all monitor the same thing,
the augment function takes any individual defect from the group and
repairs that defect.
@PP
Wherever possible, augment functions change the starting points of
the sequences of alternative repairs that they try.  For example,
when trying alternative times, the code might be
@ID {0.95 1.0} @Scale @C {
for( i = 0;  i < KheTimeGroupTimeCount(tg);  i++ )
{
  index = (KheEjectorCurrAugmentCount(ej) + i) % KheTimeGroupTimeCount(tg);
  t = KheTimeGroupTime(tg, index);
  ... try a repair using t ...
}
}
The first time tried depends on the number of augments so far,
an essentially random number.  This simple idea significantly
decreases final cost and run time.
@PP
@I { Split events and distribute split events defects. }  Most
events are split into meets of suitable durations during layer
tree construction, but sometimes the layer tree does not remove all
these defects, or a split move introduces one.  In those cases, the
split analyser (Section {@NumberOf time_structural.split.analyse})
from the @C { options } object is used to analyse the defects and
suggest splits and merges which correct them.  For each split
suggestion, for each meet conforming to the suggestion, a repair
is tried which splits the meet as suggested.  For each merge
suggestion, for each pair of meets conforming to the suggestion,
four combined repairs are tried, each consisting of, first, a
Kempe meet move which brings the two meets together, and second,
the merge of the two meets.  The four Kempe moves move the first
meet to immediately before and after the second, and the second to
immediately before and after the first, where possible.
@PP
@I { Assign time defects. }  For each monitored unassigned meet,
all ejecting meet moves to a parent meet and offset that would
assign the meet to a time within its domain are tried.
@PP
@I { Prefer times defects. }  For each monitored meet assigned
an unpreferred time, all Kempe"/"ejecting meet moves to a
parent meet and offset giving a preferred time are tried.
@PP
@I { Spread events defects. }  For each monitored meet in an
over-populated time group, all Kempe"/"ejecting moves of the
meet to a time group that it would not over-populate are tried;
and for each under-populated time group, for each meet whose
removal would not under-populate its time group, all
Kempe"/"ejecting moves of it to the under-populated time group
are tried.
@PP
@I { Link events defects. }  These are not repaired; they are
expected to be handled structurally.
@PP
@I { Order events defects. }  These are currently ignored, because,
as far as the author is aware, there are no instances containing order
events constraints.  It will not be difficult to find suitable meet
moves when such instances appear.
# @PP
# @I { Assign resource defects. }  For each monitored unassigned task,
# all ejecting assignments of the task to resources in its domain are
# tried.  Then if the @C { es_repair_times } option permits, all
# combinations of a Kempe meet move of the enclosing meet and an
# ejecting assignment of the task to resources in its domain are tried.
# @PP
# @I { Prefer resources defects. }  For each monitored task assigned
# an unpreferred resource, all ejecting moves of the task to
# preferred resources are tried.
# @PP
# @I { Avoid split assignments defects. }  For each resource participating
# in a split assignment there is one repair:  all involved tasks
# assigned that resource are unassigned, all involved tasks' domains
# are restricted to the other participating resources, and a set of
# ejection chains is tried, each of which attempts to reassign one of
# the unassigned tasks.  The repair succeeds only if all these chains
# succeed, making an ejection tree, not a chain.  This is expensive
# and unlikely to work, so it is only tried when the defect is a main
# loop defect or only one task needs to be unassigned.
# # @I { Avoid split assignments defects. }  For each pair of resources
# # assigned to monitored tasks, and for each resource in the domain of
# # one of those tasks, an ejecting task-set move of those tasks to that
# # resource is tried.  Then, for each resource assigned to exactly one
# # of the monitored tasks, a meet-and-task move of that task's meet to
# # a different time is tried, combined with a move of the task to one
# # of the other resources assigned to any of the monitored tasks.
# @PP
# @I { Avoid clashes defects. }  Avoid clashes monitors are detached
# during ejection chain repair, since their work is done by demand
# monitors.  So there are no avoid clashes defects.
# # For each clashing task, try moving it
# # to any other resource in its domain.  This function expects to be called
# # only during resource repair, because during time repair avoid clashes
# # monitors are usually detached.
# # Even during resource repair it would probably be better to ignore
# # these defects, since they are basically unrepairable then anyway.
# @PP
# @I { Avoid unavailable times defects. }  A resource overload
# repair (see above) for the monitored resource and the unavailable
# times is tried.
# @PP
# @I { Limit idle times defects. }  For each task assigned the
# monitored resource at the start or end of a `day' (actually, a time
# group being monitored), each Kempe"/"ejecting meet move of that
# task's meet such that the initial meet move reduces the number of
# idle times for that resource is tried.  Calculating this condition
# is not trivial, but the augment function does it exactly.  Task moves
# could help to repair limit idle times defects for unpreassigned
# resources, but in current data sets the resources involved are
# usually preassigned, so task moves are not currently being tried.
# @PP
# After the repairs just given are tried, if the repair has length 1
# (if the defect was not created by a previous repair on the current
# chain), a complex repair is tried which eliminates all idle times for
# one resource on one day.  Take the meets assigned that resource on
# that day.  A retimetabling of those meets on that day with no clashes
# and no idle times is defined by a starting time for the first meet
# and a permutation of the meets (their chronological order in the
# assignment).  If there are @M { k } meets and @M { s } starting
# times that don't put the last meet off the end of the day, then
# there are @M { s cdot k! } retimetablings in total.  In practice
# this is a moderate number.  For safety, only a limited number of
# retimetablings is tried, by switching to a single permutation at
# each new node after a fixed limit (currently 1000) is reached.
# # , especially if we prune the repairs as
# # they are being generated, by allowing at most one assignment that
# # increases the number of unmatched demand tixels (two would not be
# # repairable on the next step anyway).
# @PP
# @I { Cluster busy times defects. }  If the resource is busy in too
# few monitored time groups, all ejecting meet moves are tried which
# move a meet which is not the only meet in a monitored time group
# (either because every monitored time group it overlaps with overlaps
# with at least one other meet, or because it does not overlap with
# any monitored time groups) to a monitored time group in which it
# is.  If the resource is busy in too many monitored time groups,
# then for each monitored time group @C { tg } containing at least
# one meet, if the length is 1 or @C { tg } contains exactly one meet,
# all the meets in @C { tg } are unassigned, and @C { tg } and all
# monitored time groups containing no meets are removed from the
# domains of all meets assigned the resource.  This is an ejection
# tree repair if more than one meet is unassigned:  all of the
# unassigned meets must be reassigned for success.  The repair
# must ensure that the domains are restored on success as well
# as on failure.
# # @C { on_success_fn } parameter of @C { KheEjectorRepairEndLong }
# # is used to ensure that the domains are restored on success as well
# # as on failure.
# @PP
# @I { Limit busy times defects. }  For each set of times when
# the resource is underloaded (resp. overloaded), a resource
# underload (resp. overload) repair of the resource at those
# times is tried.
# @PP
# @I { Limit workload defects. }  If the resource is overloaded,
# a resource overload repair is tried, taking the set of times to be
# the entire cycle.  There is currently no repair for underloads.
# @PP
# @I { Ordinary and workload demand defects. }  If the defect has
# a workload demand monitor competitor (possibly itself, although
# workload demand monitors rarely fail to match), a resource overload
# repair is tried for the workload demand monitor's resource and
# the domain of its originating monitor.  If the defect has no
# workload demand defect competitor, all ejecting meet moves of
# competitor meets to anywhere different are tried; but meets
# within vizier nodes are not moved in this case, since that
# would only shift the problem to a different time.
@End @Section

@Section
    @Title { Repairing resource assignments }
    @Tag { eject.resource }
@Begin
@LP
Solutions have two kinds of assignments:  assignments of times to
meets, and of resources to tasks.  Repairs that change assignments
of times to meets were treated in Section {@NumberOf eject.time}.
Repairs that do both are possible in high school timetabling, but
in this section we consider only repairs that change assignments
of resources to tasks, in high school timetabling and nurse rostering.
@BeginSubSections

@SubSection
    @Title { High school timetabling and nurse rostering }
    @Tag { eject.resource.high }
@Begin
@LP
In high school timetabling, the main issue that arises in resource
assignment is avoiding split assignments (also called creating
resource constancy).  If split assignments are allowed, then
resource assignment is essentially room assignment, which is well
known to be easy in practice.  Idle times may become an issue if
there are many part-time teachers.
@PP
In nurse rostering, each resource is assigned to at most one task
per day, and there are many constraints that relate what a resource
does on one day to what it does on adjacent days.  So it makes sense
to use repairs that move sets of tasks from adjacent days from one
resource to another.  Many authors of nurse rostering solvers have
come to this conclusion.
@PP
Accordingly, we distinguish between the @I { basic repair }, which
does the minimum needed to remove some defect, and the
@I { widened repair }, which is a basic repair enlarged by changes
on adjacent days.  For example, if resource @M { r } is overloaded,
reassigning any of @M { r }'s tasks from @M { r } to any other
resource will remove (or reduce) the defect, making one basic repair
for each task assigned @M { r }.  Widened versions of these repairs
reassign several tasks assigned @M { r } on adjacent days.
@PP
The same code is used for repairing resource assignments in both
high school timetabling and nurse rostering.  But there are two
differences in setup between the two:
@NumberedList

@LI {
All repairs assume that each resource can be assigned to at
most one task per day.  The days are represented by the time
groups of the common frame.  For nurse rostering, these time
groups come from the hard constraints that specify that each
nurse can take at most one shift per day.  For high school
timetabling, when the solver realizes that there are none of
these constraints, it sets the common frame in the only way
consistent with the assumption, which is to make each individual
time into one day.  So in this case the solver distinguishes
nurse rostering from high school timetabling without any
intervention by the user.
}

@LI {
High school timetabling uses basic repairs, and nurse rostering
uses widened repairs.  The @C { KHE_AUGMENT_OPTIONS } object has
options for controlling widened repairs.  By setting option
@F { widening_off } to @C { true }, or by setting option
@F { widening_max } to 0, one can restrict the solver to basic
repairs.  In this case the user is expected to set these
options appropriately for nurse rostering or high school
timetabling.  There is nothing to prevent widened repairs
being applied in high school timetabling, but they are not likely
to be useful there.
}

@EndList
Our focus will be on nurse rostering's widened repairs.  High
school timetabling repairs are the same, only (as just explained)
with a different definition of one day, and with widening off.
@PP
In the following subsections, we'll work bottom-up, from individual
repairs to multi-repairs (Section {@NumberOf eject.practice.augment})
to augment functions.
@End @SubSection

@SubSection
    @Title { Repairs }
    @Tag { eject.resource.basic }
@Begin
@LP
This section presents the individual repairs used when repairing
resource assignments.
@PP
But first, there is a question to answer.  All resource repairs use
mtasks (Section {@NumberOf resource_structural.mtask_finding.solver}).
When we repair a defect @C { d } by reassigning some task @C { t },
we will be reassigning @C { t }'s enclosing mtask @C { mt }, which
means that we will be reassigning some task of @C { mt }, which is
not necessarily @C { t } itself.  The question is:  can we say that
@C { d } is repaired by this mtask reassignment?
@PP
It may not matter if @C { d } is not repaired.  The specification
of the ejection chains @C { Augment } function does not say that
@C { d } must be repaired, merely that a certain cost target must be
reached.  The augment function uses @C { d } as a hint, nothing more.
@PP
It might seem that there is a problem arising from the decision to
make monitors the sole entities that are marked visited.  If @C { d }
is unrepaired, then a recursive call might attempt to repair @C { d }
again, which will fail because @C { d } will have been marked
visited on the first attempt to repair it.
@PP
But in fact this is not a problem.  What happens is that some task
@C { t2 } from @C { mt } receives the reassignment intended for
@C { t }.  An examination of cases will show that @C { t2 } must
have needed the repair at least as much as @C { t } did, so other
defects are repaired even if @C { d } is not.  The repair causes
further defects to appear, and these are used to continue the chain.
The fact that @C { d } cannot now be visited does not matter, any more
than it matters if @C { d } is repaired.  This answers our question.
# Working bottom-up, our first step is to choose a small space of
# individual repairs.  Larger spaces may find better solutions,
# but they run more slowly.  Also, experience shows that extra
# repairs are likely to be more complicated repairs, and subject
# to diminishing returns.
@PP
A repair is said to @I fail if it cannot be carried out for any
reason; otherwise it {@I succeeds}.  (Success is not the same as
finding a successful ejection chain; for that, we need a whole
sequence of successful repairs, ending in an improved solution.
Nor is failing the same as aborting; an abort should never happen.)
When we say here that some condition must hold, we mean that the
repair will fail if it doesn't hold.  When a repair discovers
that it must fail, it abandons the rest of the repair, passes
@C { false } for the @C { success } argument of
@C { KheEjectorEnd }, and returns @C { false }.
# ; but we won't list all
# causes of failure, since many are simple and obvious:  resource
# not in mtask's domain, mtask's assignment is fixed, and so on.
@PP
An @I { interval } is a sequence of consecutive days (time groups
of the common frame).  It is represented by type @C { KHE_INTERVAL }
(Section {@NumberOf general_solvers.intervals}), which is a pair
of integers.  These are used here to represent the index in the
common frame of the first day and of the last day.  An interval
can be empty (when the first index is one larger than the second),
although that is uncommon.
@PP
Like many solvers, resource repair has two fundamental repair
functions:  @I { move } and @I { swap }.  It would be better to
call the move operation an @I { ejecting reassignment }, for
reasons that will become clear; but that name is too long for
daily use.  Both functions implement one repair:  each calls
@C { KheEjectorRepairBegin } once, on entry, and
@C { KheEjectorRepairEnd } once, on exit.  Both guarantee that
if no resource attends two tasks on the same day beforehand,
then no resource attends two tasks on the same day afterwards.
We'll see shortly how this comes about.
@PP
The move repair function has header
@ID @C {
bool KheMoveRepair(KHE_EJECTOR ej, KHE_AUGMENT_OPTIONS ao,
  KHE_INTERVAL in, KHE_RESOURCE from_r, KHE_RESOURCE to_r,
  KHE_MTASK from_mt);
}
@C { KheMoveRepair } finds a maximal set of mtasks lying in
interval @C { in } which are assigned @C { from_r }, and
changes all those assignments to @C { to_r }, which must be
different from @C { from_r }.  It returns @C { true } when all
this succeeds.  The mtasks it reassigns must lie entirely within
@C { in } and must be reassignable from @C { from_r } to @C { to_r }.
They must run during the first and last times of @C { in } at least.
This last rule is included so that repairs which could and should
have been tried in a smaller interval are not reattempted in a
larger interval.  Assuming that @C { in } is non-empty, this
rule also implies that the move cannot succeed by doing nothing.
@PP
If @C { from_r != NULL }, then when @C { KheMoveRepair }
succeeds it guarantees that @C { from_r } is completely free on
the days of @C { in }.  It does this by failing if it finds an
mtask assigned @C { from_r } which lies partly inside and partly
outside @C { in } (it could include such an mtask in the reassignment,
but that could cause problems for @C { to_r }), and by failing if
any mtask assigned @C { from_r } lying entirely within @C { in }
cannot be reassigned to @C { to_r } for any reason.  
@PP
If @C { from_r == NULL }, then @C { to_r != NULL }, and the move is an
@I { assignment }.  The days of @C { in } are searched for mtasks that
can be assigned @C { to_r }.  A maximal set of these mtasks which do
not overlap on any days is chosen and assigned @C { to_r }.  This is
the only case where @C { from_mt } is used:  it must be non-@C { NULL },
and it is included in the set.  Preference is given to mtasks which
start at the same time of day as @C { from_mt } (all morning shifts,
all night shifts, etc.).
# need assignment and
@PP
If @C { to_r == NULL }, then @C { from_r != NULL }, and the
move is an @I { unassignment }.  The guarantee that @C { from_r }
will be completely free after a successful repair still applies,
but the chosen mtasks will be unassigned rather than assigned
another resource.
@PP
When @C { from_r } is non-@C { NULL }, it is clear that it
can only lose assignments, not gain them, and so if it does
not attend two tasks on the same day beforehand, it will not
attend two tasks on the same day afterwards.  But as things
stand, there is no such guarantee for @C { to_r }.  This
brings us to our final wrinkle, the one that makes this
operation an @I { ejecting } reassignment:  if @C { to_r }
is non-@C { NULL }, then before the main reassignment of
@C { from_r } to @C { to_r }, @C { KheMoveRepair }
carries out another reassignment, of @C { to_r } to @C { NULL }.
If this is successful (and if it isn't, the whole operation fails),
it guarantees to clear out @C { to_r }'s timetable throughout
@C { in }.  Since the main reassignment only moves mtasks that
lie entirely within @C { in } to @C { to_r }, if @C { to_r }
does not attend two tasks on the same day beforehand, it will not
attend two tasks on the same day afterwards.
@PP
The move from @C { to_r } to @C { NULL } differs from the main move
in two ways.  It unassigns mtasks that lie partly within @C { in }
as well as mtasks that like wholly within @C { in }.  And it
does not care whether the mtasks it unassigns cover @C { in }
completely, indeed it is happy to unassign no mtasks at all.
@PP
The implementation searches for mtasks to assign to @C { to_r }
before carrying out the initial ejecting step.  The order of
these two steps does not matter when @C { from_r != NULL },
because the choice of mtasks to move is determined by @C { from_r },
but it matters when @C { from_r == NULL }, because unassigning
@C { to_r } from an mtask may make that mtask available for
assignment.  Whatever the value of @C { from_r }, it is quite
possible for the ejecting step to unassign @C { to_r } from some
mtask, and then for the main step to assign @C { to_r } back to
that same mtask again.
@PP
The swap repair function has header
@ID @C {
bool KheSwapRepair(KHE_EJECTOR ej, KHE_AUGMENT_OPTIONS ao,
  KHE_INTERVAL in, KHE_RESOURCE from_r, KHE_RESOURCE to_r,
  /* KHE_MTASK from_mt, */ KHE_TIME_GROUP blocking_tg);
}
@C { KheSwapRepair } reassigns the mtasks initially assigned
@C { from_r } in @C { in } to @C { to_r }, and reassigns the mtasks
initially assigned @C { to_r } in @C { in } to @C { from_r }.  For
this to succeed, every mtask assigned @C { from_r } or @C { to_r }
lying wholly or partially within @C { in } must lie wholly within
@C { in } and must be reassignable to the other resource.  So each
resource gains a fresh timetable within @C { in }, but there are
no changes outside @C { in }.  The interval covered by the mtasks
that move must equal @C { in }, for the usual reason that we do
not want to retry repairs that could and should have been tried
in smaller intervals.
# and @C { blocking_m },
@PP
A swap can be interpreted as two moves, one in each direction,
provided the mtasks to be reassigned are identified before any
assignments are changed.  (The ejecting aspect is irrelevant,
because the ejected tasks are assigned the other resource.)
We can use this interpretation to work out what a swap would
mean when one of the resources, @C { from_r } say, is
@C { NULL }.  The move from @C { to_r } to @C { NULL } unassigns
the mtasks initially assigned @C { to_r }, and the move from
@C { NULL } to @C { to_r } finds a set of suitable unassigned
mtasks from @C { in } (including @C { from_mt } as usual) and
assigns @C { to_r } to them.  Apart from a slight difference at
the edges of @C { in }, this is just what a move from @C { NULL }
to @C { to_r } would do.  Accordingly, @C { KheSwapRepair } requires
@C { from_r } and @C { to_r } to be non-@C { NULL } as well as
distinct.  This is why, unlike in @C { KheMoveRepair }, there
is no @C { from_mt } parameter.
# For regularity, the author has decided
# to allow @C { KheSwapRepair } to accept one @C { NULL } resource
# parameter, but his own solvers do not use this feature, since that
# would just duplicate a move which will be tried separately.
@PP
@C { KheSwapRepair(in, from_r, to_r) } and
@C { KheSwapRepair(in, to_r, from_r) } (we are abbreviating the
argument lists) should have identical effect.  And so they do,
except that the last parameter, @C { blocking_tg }, applies only
to the move from @C { to_r } to @C { from_r }.  Its purpose is to
prevent fruitless operations.  If @C { blocking_tg != NULL },
and the move from @C { to_r } to @C { from_r } would make
@C { from_r } busy during @C { blocking_tg }, then the swap fails.
# For example, if we are trying to make
# @C { from_r } free on a certain day, there is no point swapping
# with @C { to_r } if @C { to_r } is busy on that day.  We omit the
# details, but cases like this are blocked by @C { blocking_tg }.
# @C { from_mt } is used only
# when @C { from_r } is @C { NULL }, not when @C { to_r } is @C { NULL },
# and
@PP
During a swap, an mtask @C { mt } may be a part of the move from
@C { from_r } to @C { to_r }, and at the same time it may be part
of the move from @C { to_r } to @C { from_r }.  This may happen if
@C { mt } is initially assigned both @C { from_r } and @C { to_r }.
The effect should be to not change @C { mt }.  This is indeed what
the two moves accomplish:  the first reassigns a task of @C { mt }
initially assigned @C { from_r } to @C { to_r }, and the second
reassigns a task assigned @C { to_r } (possibly the same task) to
@C { from_r }.  Altogether, nothing changes except possibly the order
in which the resources appear within the mtask, which does not matter.
# This argument works even if one of the resources is @C { NULL },
# provided there is an unassigned task in @C { mt }, which there will
# be, because @C { mt } would not have been selected otherwise.
@PP
It seems like good policy to try swaps only between non-@C { NULL }
resources, because swaps involving @C { NULL } are very similar to
moves, as we have seen.  And it seems like good policy to try moves
only when one of the resources is @C { NULL }, because a move from
one non-@C { NULL } resource to another will often eject several
tasks, leaving them unassigned, when a swap would have reassigned
them.  The author's solvers adopt both policies.
@PP
The reader might wonder whether moves and swaps that follow these
policies could be unified into a single operation.  Indeed they
could, simply by delegating calls with one @C { NULL } argument
to @C { KheMoveRepair }.  But those who want to interpret this
as more than mere relabelling face a serious obstacle.  The
literature contains good evidence that moves are only worth
trying over small intervals, while swaps are worth trying over
much larger intervals.  Accordingly, two separate options,
@C { es_move_widening_max } and @C { es_swap_widening_max }, are
supplied for the maximum width of a move and of a swap.  Their
default values are 4 and 16 respectively.  Any deep unification
of moves with swaps is doomed to shipwreck on this important difference.
@End @SubSection

@SubSection
    @Title { Avoiding repeated repairs }
    @Tag { eject.resource.avoid_dup }
@Begin
@LP
Before considering multi-repairs, we need to tackle the issue of
avoiding repeated repairs.
@PP
A defect @C { d } invokes a set of alternative repairs.  When repairing
resource assignments, these are calls to @C { KheMoveRepair(in, from_r, to_r) }
and @C { KheSwapRepair(in, to_r, from_r) } for various values of
@C { in }, @C { from_r }, and @C { to_r }.  Regardless of how each
repair comes to be included, we want to avoid trying the same repair
twice when repairing @C { d }, because that wastes time.
@PP
The perfect solution is to cache, for each defect @C { d }, the argument
lists of the repairs of @C { d } that have been tried so far, and to
check the cache before each repair.  But that is very clumsy.  We need
something simpler that produces the same effect, although it does not
have to be perfect.
@PP
We start with a few miscellaneous points.
@C { KheSwapRepair(in, to_r, from_r) }
repeats @C { KheSwapRepair(in, from_r, to_r) }, so we'll need to be
careful not to try every swap twice.  The author uses
moves only to and from @C { NULL }, and swaps only between non-@C { NULL }
resources, so no swap repeats any move.  Two moves with the same @C { in },
@C { from_r }, and @C { to_r } can be different when @C { from_r == NULL },
since @C { from_mt } is used then.  We've arranged for moves and swaps to
fail when they don't use all of @C { in }, ensuring that repairs over
different intervals are genuinely different.
@PP
Some iterations are based on calls on functions
@C { KheMTaskFinderMTasksInTimeGroup }
and @C { KheMTaskFinderMTasksInInterval }
(Section {@NumberOf resource_structural.mtask_finding.solver}).
They use sorting to uniqueify the mtask sets they return, and
so avoid all revisiting.
# Repeats are more likely with swaps than with moves, because swapping's
# larger maximum length produces more intervals.
# If we limit swapping
# to non-@C { NULL } values of @C { from_r } and @C { to_r }, all moves
# are different from all swaps, as we saw previously.
# By the way, the use of mtasks like @C { from_mt } is a powerful way
# to avoid some kinds of repeated repairs.
@PP
The main cause of repeated repairs is repeated intervals.  For example,
suppose resource @C { r } is overloaded within time groups @C { tg1 }
and @C { tg2 }.  We will try repairs that move tasks assigned @C { r }
in several intervals based on @C { tg1 }, and in several intervals
based on @C { tg2 }.  The set of repairs based on @C { tg1 } can
easily be organized to avoid repeats, as can the set based on
@C { tg2 }.  But if @C { tg1 } and @C { tg2 } are chronologically
close, these two sets are likely to include repairs with the same interval.
@PP
We use caching to prevent repeats in this cases, but we don't cache
complete argument lists; instead, the cache holds a set of first
days.  When generating intervals, we skip intervals whose first day
is already in the cache.  What one first day represents, in effect,
is a large set of argument lists, all those whose interval's first
day is that day, and whose last day, @C { from_r }, @C { to_r }, and
@C { from_mt } could be anything at all.
@PP
We represent the set of cached first days by an interval of day
indexes.  This can lead to problems when the time groups (or
whatever) iterated over are not in chronological order.  We
handle this by caching only the most recent set of first days.
So each set of repairs which is known to not generate repeats
within itself avoids repeating repairs generated by the previous
such set of repairs.  This is enough to avoid all repeated
repairs, except in unlikely cases.
@PP
These ideas are implemented using type
@ID @C {
typedef struct khe_exclude_days_rec {
  KHE_INTERVAL			unassign_in;
  KHE_INTERVAL			swap_in;
} *KHE_EXCLUDE_DAYS;
}
The intervals tried by moves are different from those tried by swaps,
so two intervals are kept, one representing the first days of the
previous set of moves (in fact, unassignments), the other the first
days of the previous set of swaps.  One of these intervals is passed
to each interval iterator, which uses it to avoid repeats.  A typical
call on an interval iterator looks like this:
@ID @C {
KheIntervalIteratorInit(&swap_ii_rec, ao, kernel_in,
  ao->swap_widening_max, &ed->swap_in);
KheForEachInterval(&swap_ii_rec, in)
{
  ... try swaps in interval in ...
}
}
The iterator object, @C { swap_ii_rec }, generates all intervals that
enclose @C { kernel_in }, cover up to @C { ao->swap_widening_max }
extra days, and do not begin on any of the days of @C { ed->swap_in }.
It updates @C { ed->swap_in } with its own first days, but uses the
original value itself.
@End @SubSection

@SubSection
    @Title { Multi-repairs }
    @Tag { eject.resource.multi }
@Begin
@LP
We now move up a level to some multi-repair functions
(Section {@NumberOf eject.practice.augment}).  Here is the
first of two that underlie all augment functions for
resource monitors:
@ID @C {
bool KheDecreaseLoadMultiRepair(KHE_EJECTOR ej,
  KHE_AUGMENT_OPTIONS ao, KHE_RESOURCE r, KHE_TIME_GROUP tg,
  bool require_zero, KHE_EXCLUDE_DAYS ed);
}
Here @C { r } is any non-@C { NULL } resource, and @C { tg } is any
non-empty set of times.
@PP
@C { KheDecreaseLoadMultiRepair } is called when @C { r } is too
busy during @C { tg }; it looks for repairs that reduce the number of
tasks assigned @C { r } during @C { tg }.  If @C { require_zero } is
@C { true }, only repairs that make @C { r } completely free during
@C { tg } are of any use; otherwise, any reduction is useful.  As
explained in Section {@NumberOf eject.resource.avoid_dup}, when
iterating over intervals, intervals whose first day appears in
@C { ed } are to be skipped over, because they have already been tried.
@PP
@C { KheDecreaseLoadMultiRepair } has to convert this
specification into a set of moves and swaps, passing each
an interval and two resources.  It does this as follows.
@PP
Step 1.  If @C { require_zero } is @C { false }, each mtask assigned
@C { r } in @C { tg } needs a separate repair.  So the code iterates
over those mtasks, and for each it determines the interval of days it
occupies and the set of resources it could be reassigned to (including
@C { NULL }, meaning unassignment).  These we call the
@I { kernel interval } and the @I { domain }.  For each mtask it then
applies Step 2.
@PP
If @C { require_zero } is @C { true }, the mtasks assigned
@C { r } in @C { tg } need to be moved as a block.  The interval
containing all of them becomes the sole kernel interval,
and the set of resources that any one of them could be moved to
becomes the sole domain.  Then Step 2 is applied just once.
@PP
Step 2.  Here we have a kernel interval and domain, and the aim is
to swap or move the tasks assigned @C { r } in the kernel interval
from @C { r } to any element of  the domain.  We'll present the code
for this first; it is packaged into function @C { KheDoDecreaseLoad }:
@ID {0.95 1.0} @Scale @C {
bool KheDoDecreaseLoad(KHE_EJECTOR ej, KHE_AUGMENT_OPTIONS ao,
  KHE_RESOURCE r, KHE_TIME_GROUP tg, KHE_EXCLUDE_DAYS ed,
  KHE_INTERVAL kernel_in, KHE_RESOURCE_GROUP domain)
{
  struct khe_interval_iterator_rec unassign_ii_rec, swap_ii_rec;
  struct khe_resource_iterator_rec other_ri_rec;
  KHE_INTERVAL in;  KHE_RESOURCE other_r;

  /* try widened swaps */
  KheIntervalIteratorInit(&swap_ii_rec, ao, kernel_in,
    ao->swap_widening_max, false,
    ao->full_widening_on && KheEjectorCurrLength(ej) == 1, &ed->swap_in);
  KheResourceIteratorInit(&other_ri_rec, ao, domain, NULL, r, false);
  KheForEachInterval(&swap_ii_rec, in)
    KheForEachResource(&other_ri_rec, other_r)
      if( KheSwapRepair(ej, ao, in, true, r, other_r, /* NULL, */ tg) )
	return true;

  /* try widened unassignments */
  KheIntervalIteratorInit(&unassign_ii_rec, ao, kernel_in,
    ao->move_widening_max, false, false, &ed->unassign_in);
  KheForEachInterval(&unassign_ii_rec, in)
    if( KheMoveRepair(ej, ao, in, r, NULL, NULL) )
      return true;

  /* no luck */
  return false;
}
}
Notice that @C { tg } is passed to @C { KheSwapRepair }, causing
swaps that leave @C { r } busy during @C { tg } to fail.
@PP
We are now ready to see @C { KheDecreaseLoadMultiRepair }:
@ID {0.95 1.0} @Scale @C {
bool KheDecreaseLoadMultiRepair(KHE_EJECTOR ej, KHE_AUGMENT_OPTIONS ao,
  KHE_RESOURCE r, KHE_TIME_GROUP tg, bool require_zero, KHE_EXCLUDE_DAYS ed)
{
  KHE_RESOURCE_GROUP domain;  KHE_RESOURCE_TYPE rt;  KHE_RESOURCE r2;
  KHE_INTERVAL kernel_in;  KHE_MTASK from_mt;
  struct khe_resource_mtask_iterator_rec rmi_rec;
  struct khe_resource_task_iterator_rec rti_rec;

  rt = KheResourceResourceType(r);
  if( require_zero )
  {
    /* move all of r's tasks running during tg together away from r */
    if( ao->repair_resources && !KheResourceTypeDemandIsAllPreassigned(rt)
        && KheGetIntervalAndDomain(ej, ao, r, tg, &kernel_in, &domain)
	&& KheDoDecreaseLoad(ej, ao, r, tg, ed, kernel_in, domain) )
      return true;
  }
  else
  {
    /* move each of r's tasks running during tg separately away from r */
    if( ao->repair_resources && !KheResourceTypeDemandIsAllPreassigned(rt) )
    {
      /* iterate over each separate mtask that it would be good to move */
      KheResourceMTaskForEach(&rmi_rec, ao, r, tg, from_mt)
        if( !KheMTaskIsPreassigned(from_mt, &r2) &&
	    !KheMTaskAssignIsFixed(from_mt) &&
	    KheDoDecreaseLoad(ej, ao, r, tg, ed, KheMTaskInterval(from_mt),
	      KheMTaskDomain(from_mt)) )
	  return true;
    }
  }
  return false;
}
}
If @C { require_zero } is @C { true }, this code calls
@C { KheGetIntervalAndDomain } (not shown) to find a suitable
kernel interval and domain for all of @C { r }'s tasks running
during @C { tg }, then calls @C { KheDoDecreaseLoad } to swap
or move them away.  If @C { require_zero } is @C { false },
it does much the same thing, only for each mtask that @C { r }
is assigned to during @C { tg } separately.
# @PP
# to any
# of the resources of the domain.  For each resource @C { r2 } of the
# domain, and for each widening @C { in } of the kernel interval,
# the code tries a move of @C { r } to @C { r2 } in @C { in }, and
# a swap of @C { r } with @C { r2 } in @C { in }.  (In each swap,
# @C { blocking_tg } is set to @C { tg }.  This recognises that it
# is futile to reduce @C { r }'s load in @C { tg } only to increase
# it again when moving tasks in the opposite direction.)
# @PP
# Each widened interval @C { in } should satisfy three conditions:
# (a) it must include one of the kernel intervals; (b) its length
# must be at most the value of the @C { es_move_widening_max } or
# @C { es_swap_widening_max } option; and (c) it should preferably
# be tried only once.
# # Every widened interval satisfying these conditions is legal.
# @PP
# The main problem is (c).  We could widen one kernel interval in one
# way, producing some interval @C { in }, then widen another kernel
# interval another way, producing @C { in } again.  We don't need a
# perfect solution to this problem, so we'll aim for something simple
# that works most of the time.
# # @PP
# # The problem has several puzzling aspects.  First, it can occur within
# # a single call to @C { KheResourceOverloadMultiRepair }, but it can also
# # occur across multiple calls to @C { KheResourceOverloadMultiRepair }
# # within a single augment, for example when there is one call for each
# # active time group of a cluster busy times monitor.  Then again, the
# # kernel intervals are the same for moves and swaps, but the widened
# # intervals are different, owing to the different maximum lengths.
# # And finally duplicate intervals are allowed, indeed needed, when
# # we are moving from or to different resources.
# @PP
# To achieve (c), when we traverse of a set of widened intervals,
# we need to exclude intervals that have been tried on previous
# traversals.  We could remember all intervals tried previously,
# over the course of the whole augment, but that would be awkward.
# Just as useful is to remember the first days of all intervals
# tried previously, and exclude intervals with those first days.
# This works because whenever a traversal visits one interval
# with a given first day, it visits all intervals with that
# first day, up to the maximum length.  The first day is a
# proxy for all these intervals.
# @PP
# The set of first days of one traversal of a set of intervals is
# itself an interval.  Our strategy for avoiding duplication is
# to pass the previous traversal's set of first days to the next
# traversal, which then skips intervals whose first day was a first
# day in the previous traversal.  This prevents the same day being
# used twice on two adjacent traversals, which is good enough in
# practice.
# @PP
# Swaps usually visit more intervals that moves, owing to their larger
# maximum length.  We handle this by storing one interval for moves
# and another for swaps.  Another issue is that we use the same
# interval traversal many times over for different resources.  We
# don't want to exclude intervals when we are moving different
# resources.  We handle this by setting up a single interval
# iterator, with its exclude interval, and using it repeatedly
# on all the resources before resetting it with a new exclude
# interval.  Moves from @C { r } other resources can share an
# exclude interval, but moves from another resource to @C { r }
# need a separate exclude interval since they move different
# resources.  This makes four intervals altogether:  for each
# of move and swap, we need one interval for moving from @C { r }
# and one for moving to @C { r }.  A small type containing these
# four intervals, @C { KHE_EXCLUDE_DAYS }, is defined, and a
# value of this type is passed to the relevant functions.
@PP
Our second multi-repair function is a kind of inverse of our first:
@ID @C {
bool KheIncreaseLoadMultiRepair(KHE_EJECTOR ej,
  KHE_AUGMENT_OPTIONS ao, KHE_RESOURCE r, KHE_TIME_GROUP tg,
  bool allow_zero, KHE_EXCLUDE_DAYS ed);
}
Once again, @C { r } is any non-@C { NULL } resource, and @C { tg }
is any non-empty set of times.
@PP
@C { KheIncreaseLoadMultiRepair } is called when @C { r } is not
busy enough during @C { tg }; it looks for repairs that increase
the number of tasks assigned @C { r } during @C { tg }.  If
@C { allow_zero } is @C { true }, reducing the number of tasks
assigned @C { r } during @C { tg } to zero is an odd but acceptable
alternative.  As usual, when iterating over intervals, intervals
whose first day appears in @C { ed } are to be skipped over,
because they have already been tried.
@PP
Once again, @C { KheIncreaseLoadMultiRepair } has to convert
this specification into a set of moves and swaps, passing each
an interval and two resources.  It does this as follows.
@PP
First, if @C { allow_zero } is @C { true },
@C { KheIncreaseLoadMultiRepair } calls
@ID @C {
KheDecreaseLoadMultiRepair(ej, ao, r, tg, true, ed)
}
to try to clear out @C { r } completely during @C { tg }.
@PP
Next, it tries swaps that might increase the load:
@ID @C {
/* try widened swaps */
day_in = KheTimeGroupInterval(tg, ao);
for( i = KheIntervalFirst(day_in);  i <= KheIntervalLast(day_in);  i++ )
{
  KheIntervalIteratorInit(&swap_ii_rec, ao, KheIntervalMake(i, i),
    ao->swap_widening_max, &ed->swap_in);
  KheResourceIteratorInit(&other_ri_rec, ao, 
    KheResourceTypeFullResourceGroup(rt), NULL, r, false);
  KheForEachInterval(&swap_ii_rec, in)
    KheForEachResource(&other_ri_rec, other_r)
      if( KheSwapRepair(ej, ao, in, r, other_r, NULL, NULL) )
	return true;
}
}
@C { KheTimeGroupInterval } returns the smallest interval of days
whose first and last elements intersect with @C { tg }.  For each
of these days, this code iterates over all intervals @C { in } with
this one day as kernel, and for each resource @C { other_r } of
@C { r }'s type except for @C { r }, it tries a swap with
@C { other_r } in @C { in }.  Repeated intervals are almost
certain here, but @C { ed->swap_in } avoids them as usual.
@PP
Finally, @C { KheIncreaseLoadMultiRepair } tries to assign
unassigned mtasks:
@ID @C {
/* try widened assignments */
for( need = 1;  need >= 0;  need-- )
{
  KheAllMTaskIteratorTimeGroupInit(&ami_rec, ao, rt, tg);
  KheAllMTaskForEach(&ami_rec, mt)
    if( need == (int) KheMTaskNeedsAssignment(mt) )
    {
      KheIntervalIteratorInit(&assign_ii_rec, ao, KheMTaskInterval(mt),
	ao->move_widening_max, false, false, NULL);
      KheForEachInterval(&assign_ii_rec, in)
	if( KheMoveRepair(ej, ao, in, NULL, r, mt) )
	  return true;
    }
}
}
This iterates over all mtasks of the right type lying wholly or
partly in @C { tg }, first those that need assignment, then those
that don't, and tries moving each of those, plus other mtasks that
come from the widening, to @C { r }.  There is no reference here
to @C { ed }, because each mtask @C { mt } is distinct and makes
for a different repair.  (Some @C { mt } might intersect with two
time groups @C { tg }, and then repairs with @C { mt } as kernel
will be repeated.  This cannot be prevented by using @C { ed },
since several mtasks may run on the same day or days.)
@PP
It was remarked earlier that there was some danger of trying each
swap twice, because @C { KheSwapRepair(in, to_r, from_r) } repeats
@C { KheSwapRepair(in, from_r, to_r) }.  We can see, however, that
there is no danger of that here, because each swap has @C { r }
for its first resource and something else for its second resource.
Indeed, entire sequences of arbitrarily intermixed calls to
@C { KheDecreaseLoadMultiRepair } and @C { KheIncreaseLoadMultiRepair }
with the same @C { r } parameter are safe from this kind of repetition
for this reason.
@PP
The key multi-repair function for repairing event resource defects is
@ID @C {
bool KheEventResourceMultiRepair(KHE_EJECTOR ej,
  KHE_AUGMENT_OPTIONS ao, KHE_EVENT_RESOURCE er,
  KHE_RESOURCE_ITERATOR to_ri, KHE_EXCLUDE_DAYS ed,
  KHE_MTASK *prev_mt, KHE_RESOURCE *prev_r)
}
Parameter @C { to_ri } is a @I { resource iterator }, a set of
resources with some fancy features:  one can specify a resource
group of resources to include, and optionally a resource group
and resource to exclude.  One can also specify whether to include
@C { NULL }, meaning not assigned.
@PP
For each task @C { t } derived from @C { er } which is not already
assigned a resource from @C { to_ri }, @C { KheEventResourceMultiRepair }
tries to reassign @C { t } to a resource from @C { to_ri }.  If @C { t }
lies in mtask @C { from_mt } and is assigned resource @C { from_r },
it tries similar widened swaps and unassignments to those tried by
@C { KheDecreaseLoadMultiRepair }:
@ID @C {
/* try widened swaps */
KheIntervalIteratorInit(&swap_ii_rec, ao, KheMTaskInterval(from_mt),
  ao->swap_widening_max, &ed->swap_in);
KheForEachInterval(&swap_ii_rec, in)
  KheForEachNonNullResource(to_ri, other_r)
    if( KheSwapRepair(ej, ao, in, from_r, other_r, NULL, NULL) )
      return true;

/* try widened unassignments, if allowed by to_ri */
if( KheResourceIteratorContainsResource(to_ri, NULL) )
{
  KheIntervalIteratorInit(&unassign_ii_rec, ao,
    KheMTaskInterval(from_mt), ao->move_widening_max, &ed->unassign_in);
  KheForEachInterval(&unassign_ii_rec, in)
    if( KheMoveRepair(ej, ao, in, from_r, NULL, NULL) )
      return true;
}
}
If @C { t } is unassigned (but still lying in mtask @C { from_mt }),
it tries widened assignments:
@ID @C {
/* try widened assignments */
KheIntervalIteratorInit(&assign_ii_rec, ao, KheMTaskInterval(from_mt),
  ao->move_widening_max, NULL);
KheForEachInterval(&assign_ii_rec, in)
  KheForEachNonNullResource(to_ri, other_r)
    if( KheMoveRepair(ej, ao, in, NULL, other_r, from_mt) )
      return true;
}
Repeated attempts to reassign the same resource from the same mtask are
avoided; this is what @C { *prev_mt } and @C { *prev_r } are used for.
Intervals forbidden by @C { ed } are skipped as usual, except that when
assigning, the distinct values of @C { from_mt } make this unnecessary
and indeed unwanted.
@PP
Again, we want to avoid trying swaps twice
because @C { KheSwapRepair(in, to_r, from_r) } repeats
@C { KheSwapRepair(in, from_r, to_r) }.  There seems to be no easy
proof that repetitions of this kind cannot happen here.  However, it is
hard to construct cases where they do happen.
# It passes the monitor being repaired as @C { blocking_d }, recognizing
# that swapping two tasks monitored by @C { blocking_d } would be futile,
# because it would not change anything for @C { blocking_d }.
@End @SubSection

@SubSection
    @Title { Augment functions }
    @Tag { eject.resource.augment }
@Begin
@LP
We are now ready to implement the augment functions for event resource
and resource monitors @C { m }, by calling functions
@C { KheDecreaseLoadMultiRepair }, @C { KheIncreaseLoadMultiRepair },
and @C { KheEventResourceMultiRepair } from
Section {@NumberOf eject.resource.multi}, as follows.
@PP
@I { Assign resource monitor. }  This monitors one event resource @C { er },
and it is repaired by one call to @C { KheEventResourceMultiRepair }.  The
set of resources to try is just the domain of @C { er }.
@PP
@I { Prefer resources monitor. }  This also monitors one event resource
@C { er }, and is repaired by one call to @C { KheEventResourceMultiRepair }.
The set of resources to try is the monitor's set of preferred resources,
plus unassignment, which also removes a prefer resources defect.
@PP
@I { Avoid split assignments monitor. }  This has a different
character from the other event resource monitors, and it is
handled in a different way.  Documenting it is @I { still to do }.
@PP
@I { Limit resources monitor. }  This monitors a set of event resources.
For each of them it makes one call to @C { KheEventResourceMultiRepair }.
If there are not enough tasks assigned resources from @C { rg }, the
monitor's set of resources of interest, then the set of resources passed
to @C { KheEventResourceMultiRepair } is just @C { rg }.  If there are
too many tasks assigned resources from @C { rg }, then the set of
resources passed to @C { KheEventResourceMultiRepair } is the set of
resources acceptable to each event resource plus unassignment but
excluding @C { rg }.
@PP
@I { Avoid unavailable times monitor }.  This is handled by
a single call to
@ID @C {
KheDecreaseLoadMultiRepair(ej, ao, r, tg, false, ed)
}
where @C { tg } is the set of times that @C { m } requires @C { r }
to be free.
@PP
This call to @C { KheDecreaseLoadMultiRepair } does not convert
@C { tg } into an interval.  This is just as well, because here
the times of @C { tg } may be scattered at random through the cycle,
and repairing the interval covering them all would be mad.  Instead,
each interval containing a task running when @C { r } should be free
becomes one kernel interval, as we have seen.
@PP
@I { Cluster busy times monitor }.  If @C { m }'s defect is too many
active time groups, then the repair visits each active time group
@C { tg } of @C { m } and tries to make it inactive, by calling
@ID @C {
KheDecreaseLoadMultiRepair(ej, ao, r, tg, true, ed)
}
when @C { tg } is positive, and
@ID @C {
KheIncreaseLoadMultiRepair(ej, ao, r, tg, false, ed)
}
when it is negative.  Here @C { require_zero } is @C { true } because
to make a positive @C { tg } inactive one must reduce the number of
tasks running during @C { tg } to zero.  If @C { m }'s defect is too
few active time groups, then the repair visits each inactive time
group @C { tg } of @C { m } and tries to make it active, by calling
@ID @C {
KheIncreaseLoadMultiRepair(ej, ao, r, tg, false, ed)
}
when @C { tg } is positive, and
@ID @C {
KheDecreaseLoadMultiRepair(ej, ao, r, tg, true, ed)
}
when it is negative.  Once again @C { require_zero } must be @C { true }.
The usual method of avoiding repeated intervals, by keeping track of
first days in variable @C { ed }, is used.
@PP
If the monitor's @C { allow_zero } flag is set, then another way
to handle too few active time groups is to reduce the number of
active time groups to zero.  The repair tries this too, using
calls to @C { KheDecreaseLoadMultiRepair } and
@C { KheIncreaseLoadMultiRepair } similar to those just given,
but only when there is exactly one active time group.  Making
several active time groups inactive is awkward to implement under
current arrangements, and unlikely to succeed.
@PP
@I { Limit busy times monitor }.  Limit busy times constraints can be
converted into cluster busy times constraints.  The repair does what
would be done if this conversion were carried out.
@PP
@I { Limit workload monitor }.  These are like limit busy times monitors,
except that they monitor total workload rather than busyness.  Accordingly,
the same repairs are used for them.
@PP
@I { Limit active intervals monitor }.
Suppose limit active intervals monitor @C { m } for resource
@C { r } has a sequence of consecutive busy time groups @C { s }
which is too long.  The multi-repairs for this are a call to
@C { KheDecreaseLoadMultiRepair } which make @C { s }'s
first time group free for @C { r }, and a call to
@C { KheDecreaseLoadMultiRepair } which does the same for
@C { s }'s last time group.  These are widened in the same way as
for a cluster busy times monitor, including remembering first days.
@PP
If @C { m } monitors sequences of free time groups rather than busy
ones, we switch to calls to @C { KheIncreaseLoadMultiRepair },
to make these time groups busy rather than free.  Again this is
what cluster busy times repairs do.  If we have a sequence of busy
time groups which is too short, we treat that as though the
adjacent sequences of free time groups are too long, leading
to repairs to the first free time group to the left, and the first
free time group to the right.
@PP
When a sequence is too short, another option is to remove it
altogether by trying repairs which clear out its time groups
(or make them busy for sequences of free time).  We try this too,
but only when the time groups are positive.
# when the sequence length is 1 or 2.
@End @SubSection

@SubSection
    @Title { Enumeration of basic repairs }
    @Tag { eject.resource.enum }
@Begin
@LP
In this section we investigate whether moves and swaps are the
only possible repairs.  Of course, if we allow any number of
mtask sets and resources to be affected by a repair, then the
number of repairs is unlimited.  But if we limit repairs to
neighbourhoods similar to the ones we have already decided to
use, the choices become correspondingly more limited.
@PP
Accordingly, we define a @I { simple repair operation } to be one
satisfying these conditions:
@BulletList

@LI {
The operation has one or two mtask set parameters, called
@C { mts1 } and (optionally) @C { mts2 }.  These will usually
contain all the mtasks lying in a given interval, but we are
not concerned here with where these mtask sets come from.
When there are two mtask sets, we assume here that their
values are disjoint.
}

@LI {
The operation has one or two resource parameters, called @C { r1 }
and (optionally) @C { r2 }.  Their values may be @C { NULL }.  When
there are two, their values must be different.
}

@LI {
When there is one mtask set parameter, the operation is
expressed by
@ID @C {
KheMTaskSetResourceReassign(mts1, ?, ?)
}
When there are two mtask set parameters, it is expressed by
@ID @C {
KheMTaskSetResourceReassign(mts1, ?, ?) &&
KheMTaskSetResourceReassign(mts2, ?, ?)
}
Here @C { ? } stands for @C { r1 }, or @C { r2 } (when available),
or @C { NULL }.
}

@EndList
@C { KheMoveRepair } and @C { KheSwapRepair } are simple repair
operations, for example.  We ask what operations satisfying these
conditions there can be.
@PP
Suppose first that the operation has one mtask set parameter.  There
are three choices for the first @C { ? } and three for the second,
making nine altogether.  Dropping three whose first and second
@C { ? } are the same, which causes @C { KheMTaskSetResourceReassign }
to fail, we get six possibilities:
@IndentedList

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, NULL)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r2, r1)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r2, NULL)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, NULL, r1)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, NULL, r2)
}

@EndList
However, expressions that do not mention @C { r1 } can be dropped,
because there is always an @C { r1 } parameter and not using it
is not reasonable.  This brings us back to four possibilities:
@IndentedList

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, NULL)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r2, r1)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, NULL, r1)
}

@EndList
The third is symmetrical with the first:  it can be obtained
from the first by reordering arguments.  Dropping it leaves
three cases, all offered by @C { KheMoveRepair } if we ignore
its ejecting aspect.
@PP
The same method will work for repair functions with two mtask set
parameters.  There are 6 legal combinations for the first two
parameters, and 6 legal combinations for the second two parameters,
making 36 cases in all.
@PP
Here are the cases starting with
@C { KheMTaskSetResourceReassign(mts1, r1, r2) }:
@NumberedList

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2) &&    /* union case */
KheMTaskSetResourceReassign(mts2, r1, r2)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2) &&
KheMTaskSetResourceReassign(mts2, r1, NULL)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2) &&
KheMTaskSetResourceReassign(mts2, r2, r1)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2) &&
KheMTaskSetResourceReassign(mts2, r2, NULL)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2) &&
KheMTaskSetResourceReassign(mts2, NULL, r1)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2) &&
KheMTaskSetResourceReassign(mts2, NULL, r2)
}

@EndList
We can rule out the first of these because it is what we
will call a @I { union case }:  it is equivalent to
a move on the union of @C { mts1 } and @C { mts2 }.
The other five seem to be genuinely distinct.
@PP
Here are the cases starting with
@C { KheMTaskSetResourceReassign(mts1, r1, NULL) }:
@NumberedList start { 7 }

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, NULL) &&  /* symm. with (2) */
KheMTaskSetResourceReassign(mts2, r1, r2)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, NULL) &&  /* union case */
KheMTaskSetResourceReassign(mts2, r1, NULL)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, NULL) &&  /* symm. with (4) */
KheMTaskSetResourceReassign(mts2, r2, r1)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, NULL) &&
KheMTaskSetResourceReassign(mts2, r2, NULL)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, NULL) &&
KheMTaskSetResourceReassign(mts2, NULL, r1)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, NULL) &&
KheMTaskSetResourceReassign(mts2, NULL, r2)
}

@EndList
The cases with adjacent comments can be ruled out for
the reasons given.
@PP
Cases starting with @C { KheMTaskSetResourceReassign(mts1, r2, r1) }
are symmetrical with cases starting with
@C { KheMTaskSetResourceReassign(mts1, r1, r2) }, so we skip them.
@PP
Cases starting with
@C { KheMTaskSetResourceReassign(mts1, r2, NULL) }
are symmetrical with cases starting with
@C { KheMTaskSetResourceReassign(mts1, r1, NULL) }, so we skip them.
@PP
Here are the cases starting with
@C { KheMTaskSetResourceReassign(mts1, NULL, r1) }:
@NumberedList start { 25 }

@LI @C {
KheMTaskSetResourceReassign(mts1, NULL, r1) &&   /* symm. with (5) */
KheMTaskSetResourceReassign(mts2, r1, r2)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, NULL, r1) &&   /* symm. with (11) */
KheMTaskSetResourceReassign(mts2, r1, NULL)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, NULL, r1) &&   /* symm. with (6) */
KheMTaskSetResourceReassign(mts2, r2, r1)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, NULL, r1) &&   /* symm. with (12) */
KheMTaskSetResourceReassign(mts2, r2, NULL)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, NULL, r1) &&   /* union case */
KheMTaskSetResourceReassign(mts2, NULL, r1)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, NULL, r1) &&
KheMTaskSetResourceReassign(mts2, NULL, r2)
}

@EndList
There is only one genuinely new operation here.
@PP
Cases starting with
@C { KheMTaskSetResourceReassign(mts1, NULL, r2) }
are symmetrical with cases starting with
@C { KheMTaskSetResourceReassign(mts1, NULL, r1) }, so we skip them.
@PP
That ends the enumeration.  Altogether we have found nine distinct
simple repairs with two mtask set parameters:
@IndentedList

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2) &&
KheMTaskSetResourceReassign(mts2, r1, NULL)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2) &&   /* KheSwapRepair */
KheMTaskSetResourceReassign(mts2, r2, r1)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2) &&   /* KheMoveRepair */
KheMTaskSetResourceReassign(mts2, r2, NULL)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2) &&
KheMTaskSetResourceReassign(mts2, NULL, r1)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, r2) &&
KheMTaskSetResourceReassign(mts2, NULL, r2)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, NULL) &&
KheMTaskSetResourceReassign(mts2, r2, NULL)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, NULL) &&
KheMTaskSetResourceReassign(mts2, NULL, r1)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, r1, NULL) &&
KheMTaskSetResourceReassign(mts2, NULL, r2)
}

@LI @C {
KheMTaskSetResourceReassign(mts1, NULL, r1) &&
KheMTaskSetResourceReassign(mts2, NULL, r2)
}

@EndList
@C { KheSwapRepair } and @C { KheMoveRepair } are
here, although the latter is actually
@ID @C {
KheMTaskSetResourceReassign(mts2, r2, NULL) &&
KheMTaskSetResourceReassign(mts1, r1, r2)
}
Note the change of order of the two calls:  order may be important
when @C { mts1 } and @C { mts2 } are not disjoint, a detail that
we have not tried to capture here.  It is an interesting exercise
to look through the other seven operations and ponder what they
do and whether that might be useful in practice.  To this author,
none of them stand out.
# Some (the first,
# for example) have parts which appear unrelated, which seems to
# be not useful.  Others might be useful but seem unintuitive---they
# have no simple name, for example.
@PP
It may seem that we have also omitted to consider the various kinds
of mtask sets.  The main ones are sets whose mtasks are assigned a
particular resource @C { r }, and sets whose mtasks contain at least
one task that needs assignment.  But these kinds are implicit in the
calls above:  moving @C { mts1 } from @C { r1 } to @C { r2 } implies that
the mtasks of @C { mts1 } are initially assigned @C { r1 }, and so on.
@End @SubSection

# @SubSection
#     @Title { Repairs and augment functions for resource assignment (old) }
#     @Tag { eject.practice.repair_resource_old }
# @Begin
# @LP
# This section presents the repairs and augment functions for resource
# assignment.
# @PP
# Resource assignment is complicated by the need to support the two
# different applications of high school timetabling and nurse
# rostering.  The main issue in high school timetabling is avoiding
# split assignments (also called resource constancy); without that,
# resource assignment is essentially room assignment, which is well
# known to be easy in practice.
# @PP
# # All times are preassigned in nurse rostering, so there are no event
# # monitor defects, and no repairs that change the time of an event.
# # Consequently, although there are many kinds of event resource and
# # resource monitors, giving rise to many kinds of defects, in the
# # end there is only one way to repair these defects:  by @I moving
# # (changing the assignments of) one or more tasks.
# # @PP
# # For the most part we will be moving a single task.  When we do move
# # more than one task, we will move them all in the same way:  from
# # some resource @M { r sub 1 } to another resource @M { r sub 2 }.
# # Repair operations that change nothing are not allowed, so the set
# # of tasks must be non-empty, and @M { r sub 1 } and @M { r sub 2 }
# # must be different.  Either resource may be @C { NULL }, meaning a
# # non-assignment of the tasks involved.  This supports assignment
# # and unassignment as well as the true move.
# # @PP
# # Only proper root tasks ever get moved.  This ensures that any
# # pre-existing task grouping is not disturbed by the ejection chain
# # algorithm.
# In nurse rostering, each resource is assigned to at most one task
# per day, and there are many constraints that relate what a resource
# does on one day to what it does on adjacent days.  So it makes sense
# to use repairs that move small sets of tasks from adjacent days as
# a block from one resource to another.  Many authors of nurse
# rostering solvers have come to this conclusion.
# @PP
# Accordingly, we distinguish between @I { basic repairs }, which do
# the minimum needed to remove some defect, and @I { enhanced repairs },
# which are larger repairs, containing the basic ones plus moves on
# adjacent days.  For high school timetabling, only basic repairs are
# tried, but for nurse rostering we try enhanced versions of the
# basic repairs.  For example, if resource @M { r } is overloaded,
# moving any one of @M { r }'s tasks to any other resource will remove
# (or at least reduce) the defect, making one basic repair for each
# task assigned @M { r }.  These are applied as described in high school
# timetabling, but for nurse rostering they need to be enhanced.
# @PP
# All of the resource repair options in the @C { KHE_AUGMENT_OPTIONS }
# object relate to enhanced moves, except @C { repair_resources }.  They
# turn various kinds of enhanced moves on and off, and supply maximum
# values for the number of adjacent days to involve.
# @PP
# At the centre of the implementation of all this is this private
# multi-repair function:
# @ID @C {
# static bool KheMTaskSetMoveAugment(KHE_MTASK_SET from_r_mts,
#   KHE_RESOURCE from_r, KHE_RESOURCE_GROUP rg,
#   KHE_RESOURCE_GROUP not_rg, bool allow_unassign)
# }
# We have omitted some parameters to allow us to focus on the essentials.
# Parameter @C { from_r_mts } contains a non-empty set of mtasks, all
# assigned the same resource @C { from_r }.  Here @C { from_r } may be
# @C { NULL }, in which case all the mtasks need assignment.  Parameter
# @C { rg } contains a set of non-@C { NULL } resources, as does
# @C { not_rg }.  Let @C { to_r } be a resource that is not equal to
# @C { from_r }, that lies in @C { rg }, does not lie in @C { not_rg },
# and may be @C { NULL } only when @C { allow_unassign } is @C { true }.
# Then @C { KheMTaskSetMoveAugment } tries one repair for each such
# @C { to_r }, which moves the tasks of @C { from_r_mts } from
# @C { from_r } to @C { to_r }.
# @PP
# These repairs are basic repairs.  Before each is tried it is
# analysed for possible enhancement, depending on the augment
# options.  Several enhanced repairs may be derived from one
# basic repair; they are all tried.
# @PP
# Unlike time repair, which utilizes many repair functions,
# @C { KheMTaskSetMoveAugment }'s repairs are the only ones tried during
# resource repair.  This leaves us with
# two questions.  First, how are the various defects converted into
# calls on @C { KheMTaskSetMoveAugment }?  And second, in the nurse
# rostering case, what enhancements does @C { KheMTaskSetMoveAugment }
# apply to each of the basic repairs it finds?  We begin by answering
# the first question for each kind of event resource and resource monitor
# @C { m }, beginning with event resource monitors.
# @PP
# @I { Assign resource monitor }.  For each unassigned task @C { t }
# monitored by @C { m }, call
# @ID @C {
# KheMTaskSetMoveAugment({mt(t)}, NULL, R, {}, false);
# }
# where @C { m(t) } is the mtask containing @C { t } (which evidently
# needs assignment), @C { {mt(t)} } is the mtask set containing just
# @C { m(t) }, and @C { R } is the set of resources acceptable to @C { t }
# (its domain).
# @PP
# @I { Prefer resources monitor }.  For each task @C { t } monitored
# by @C { m } which is assigned a resource @C { r(t) } that is not
# preferred by @C { m }, call
# @ID @C {
# KheMTaskSetMoveAugment({m(t)}, r(t), R * D, {}, true);
# }
# where @C { {m(t)} } is as before, @C { R } is the set of resources
# acceptable to @C { t } (its domain), @C { D } is the set of resources
# that @C { m } prefers, and @C { * } is set intersection.  Here
# @C { allow_unassign } is @C { true } because prefer resources
# monitors have zero cost when the tasks they monitor are unassigned.
# @PP
# @I { Limit resources monitor }.  When the defect is an @I { underload }
# (not enough tasks assigned resources from @C { m }'s set of resources
# @C { D }), for each task @C { t } monitored by @C { m } which is
# either unassigned or assigned a resource not from @C { D } we call
# @ID @C {
# KheMTaskSetMoveAugment({m(t)}, r(t), R * D, {}, false);
# }
# where @C { R } is the set of resources acceptable to @C { t }.  This
# attempts to move @C { t } to any resource in @C { R * D }.  When the
# defect is an @I { overload } (too many tasks assigned resources from
# @C { m }'s set of resources @C { D }), for each task @C { t } monitored
# by @C { m } which is assigned a resource from @C { D } we call
# @ID @C {
# KheMTaskSetMoveAugment({m(t)}, r(t), R, D, true);
# }
# where @C { R } is the set of resources acceptable to @C { t }.
# This attempts to move @C { t } to any resource in @C { R } but
# not @C { D }, or to unassign it.
# @PP
# We move on now to resource monitors.  Two key helper functions are
# used for them:
# @ID @C {
# bool KheResourceOverloadMultiRepair(KHE_RESOURCE r,
#   KHE_TIME_GROUP tg, bool require_zero)
# }
# and
# @ID @C {
# bool KheResourceUnderloadMultiRepair(KHE_RESOURCE r,
#   KHE_TIME_GROUP tg, bool allow_zero)
# }
# Here @C { r } is the resource being monitored by @C { m }, and @C { tg } is
# an arbitrary non-empty set of times.  @C { KheResourceOverloadMultiRepair }
# is called when @C { r } is too busy during @C { tg }; it looks for
# repairs that reduce the number of tasks assigned @C { r } that run
# during @C { tg }.  If @C { require_zero } is @C { true }, only reductions
# that make @C { r } completely free during @C { tg } are useful; otherwise,
# any reduction is useful.  @C { KheResourceUnderloadMultiRepair }
# is called when @C { r } is not busy enough during @C { tg }; it
# looks for repairs that increase the number of tasks assigned
# @C { r } that run during @C { tg }.  If @C { allow_zero } is
# @C { true }, reducing the number of tasks assigned @C { r } during
# @C { tg } to zero is an odd but acceptable alternative.
# @PP
# Before we implement these two functions, we'll see how they can be
# used to implement the augment functions for the resource monitors.
# @PP
# @I { Avoid unavailable times monitor }.  This translates into
# a single call to
# @ID @C {
# KheResourceOverloadMultiRepair(r, tg, false)
# }
# where @C { tg } is the set of times that @C { m }'s constraint
# requires @C { r } to be free.
# @PP
# @I { Cluster busy times monitor }.  If @C { m }'s defect is too many
# active time groups, then the repair visits each active time group
# @C { tg } of @C { m } and tries to make it inactive, by calling
# @ID @C {
# KheResourceOverloadMultiRepair(r, tg, true)
# }
# when @C { tg } is positive, and
# @ID @C {
# KheResourceUnderloadMultiRepair(r, tg, false)
# }
# when it is negative.
# We set @C { require_zero } to @C { true } here because to make a
# positive @C { tg } inactive one must reduce the number of tasks
# running during @C { tg } to zero.  It is true that in nurse
# rostering, @C { tg } usually contains the times of one day at most,
# so that @C { r } can be busy with at most one task during @C { tg },
# making @C { require_zero } irrelevant.  But there are cases,
# notably constraints on the number of busy weekends, where
# it may be necessary to free up @C { r } on two days.
# @PP
# If @C { m }'s defect is too few active time groups, then the repair
# visits each inactive time group @C { tg } of @C { m } and tries to
# make it active, by calling
# @ID @C {
# KheResourceUnderloadMultiRepair(r, tg, false)
# }
# when @C { tg } is positive, and
# @ID @C {
# KheResourceOverloadMultiRepair(r, tg, true)
# }
# when it is negative.  Once again @C { require_zero } must be @C { true }.
# @PP
# If the monitor's @C { allow_zero } flag is set, then another way
# to handle too few active time groups is to reduce the number of
# active time groups to zero.  The repair tries this too, using
# calls to @C { KheResourceOverloadMultiRepair } and
# @C { KheResourceUnderloadMultiRepair } similar to those just given,
# but only when there is exactly one active time group:  making
# several active time groups inactive is awkward to implement under
# current arrangements, and unlikely to succeed.
# @PP
# @I { Limit busy times monitor }.  Limit busy times constraints can be
# converted into cluster busy times constraints.  The repair does what
# would be done if this conversion were carried out.
# @PP
# @I { Limit workload monitor }.  These are like limit busy times monitors,
# except that they monitor total workload rather than busyness.  Accordingly,
# the same repairs are used for them.
# @PP
# @I { Limit active intervals monitor }.  If the defect is a sequence
# @C { s } of too many consecutive active time groups, the repair tries
# to make the first active time group of @C { s } inactive (using the
# same calls as were used above by the cluster busy times monitor for
# the same purpose), and separately it tries to make the last active
# time group of @C { s } inactive.  It also tries the time groups in
# between, but that is not likely to work well.  These basic repairs
# will need adjustment by the enhancement phase.
# @PP
# If the defect is a sequence @C { s } of too few consecutive active
# time groups, then if that number is 1 or 2 the repair first tries
# to make the active time groups inactive, so that there is no sequence
# at all; then it tries to extend @C { s } one place to the left, by
# making the inactive time group there active; and finally it tries to
# extend @C { s } one place to the right.
# @PP
# Now let's see how to implement @C { KheResourceOverloadMultiRepair }
# and @C { KheResourceUnderloadMultiRepair }.  In
# @C { KheResourceOverloadMultiRepair }, the first step is to build the
# set @C { S } of all tasks which are assigned @C { r } and which run
# during @C { tg }, by consulting the timetable monitor for @C { r }
# maintained by the KHE platform.  @C { S } will be non-empty because
# @C { r } is overloaded during @C { tg }.  If @C { require_zero } is
# @C { false }, the aim is to move any one of these tasks away from
# @C { r }, so for each task @C { t } in @C { S } the code calls
# @ID @C {
# KheMTaskSetMoveAugment({m(t)}, r, R, {}, true)
# }
# where @C { R } is the domain of @C { t }.  If @C { require_zero } is
# @C { true }, a single call is made, to
# @ID @C {
# KheMTaskSetMoveAugment(S, r(t), R, {}, true)
# }
# where @C { R } is the domain of the first task of @C { S }.
# @PP
# There are two points to make about this second case.  First, @C { R } may
# not suit some tasks of @C { S } other than the first.  If so, some of
# the repairs tried by @C { KheMTaskSetMoveAugment } may not be able to
# be carried out.  That is not a problem:  as always in such cases, they
# will just be abandoned with no harm done except a small loss of time.
# @PP
# Second, it only tries repairs that move the tasks to the same new
# resource.  This restriction to one new resource is not a requirement
# of @C { KheResourceOverloadMultiRepair } and could be dropped.  However,
# allowing the tasks to move to different resources would greatly
# increase the number of possible repairs, and probably yield little
# benefit, given that @C { S } will usually be small.
# @PP
# @C { KheResourceUnderloadMultiRepair } loops through each time of @C { tg },
# skipping times from days when @C { r } is already busy.  For each of the
# unskipped times, it finds all tasks @C { t } (assigned and unassigned)
# which run at that time and are willing to be moved to or assigned
# @C { r }, and calls
# @ID @C {
# KheMTaskSetMoveAugment({m(t)}, r(t), {r}, {}, false)
# }
# When @C { allow_zero } is @C { true }, one additional repair is
# tried, namely a single call to
# @ID @C {
# KheResourceOverloadMultiRepair(r, tg, true)
# }
# As we know, this tries repairs that make @C { r } free during @C { tg }.
# @PP
# We can now implement the augment functions for the resource monitors
# using calls to @C { KheResourceOverloadMultiRepair } and
# @C { KheResourceUnderloadMultiRepair }.
# @PP
# @I { Obsolete below here.  Some of it relates to enhanced moves,
# but it all needs a complete redo. }
# @PP
# Which tasks should be chosen?  Initially, they should all be assigned to
# whatever @C { task } is assigned to initially (possibly @C { NULL }),
# otherwise the rationale for this kind of move is lost.  The number of
# adjacent days to include tasks from is harder to be sure about, except
# that, as we search in each direction from @C { task }'s day, we are
# obliged to stop after we reach the first or last day, and if there is
# no task assigned what @C { task } is assigned to initially.  Furthermore,
# if @C { r2 } is already busy on some day, it is not likely to be useful
# to give it another task on that day.  So the search should stop at the
# first case of that.  So we have a different set of moves to make for
# each @C { r2 }.
# @PP
# Our plan is to move just @C { task } to begin with, then @C { task }
# plus one day, then @C { task } plus 2 days, and so on up to @C { k }
# days, where @C { k } is a parameter that we can choose, by means of
# an option perhaps.  We will try moving all sets of from 1 to @C { k }
# days that include @C { task }.  Clearly, @C { k } must be small, say
# 4 at most.  Only @C { task } will be marked visited and checked for
# being visited.
# @PP
# The set of possible repairs here has two dimensions, one indexed
# by sets of days to move, the other by @C { r2 }.  We could iterate
# in any order that maximizes efficiency.  It would be good, for
# example, to build task sets as required, and remember them
# from one resource to the next.
# @End @SubSection

# @SubSection
#     @Title { Correlation grouping }
#     @Tag { eject2.correlation }
# @Begin
# @LP
# To @I group two or more monitors means to make them children of a
# common parent group monitor.  There are several reasons why this
# might be done, the main ones being @I { correlation grouping },
# the subject of this section, where monitors are grouped because
# they monitor the same thing, and @I { focus grouping }, the subject
# of the following section, where the parent is a focus for some operation.
# # As previously mentioned, when using ejection chains it is important to
# # group monitors together when they monitor the same thing.  This both
# # reduces the number of defects and increases their cost, increasing the
# # chance of being able to follow an ejection chain to a successful conclusion.
# # @PP
# # Although ejection chains are the leading user of monitor grouping,
# # they are not the only one.  This section covers all forms of
# # monitor grouping, including those needed for Kempe and ejecting
# # meet moves (Section {@NumberOf time_solvers.kempe}).
# @BeginSubSections

# @SubSection
#     @Title { Introduction }
#     @Tag { eject2.correlation.intro }
# @Begin
# @LP
# # Monitor grouping works like this.  Suppose we know that monitors
# # @C { m1 } and @C { m2 } monitor the same thing.  We introduce a group
# # monitor @C { g }, make @C { m1 } and @C { m2 } children of @C { g },
# # and ensure that when handling defects we use @C { g }, not @C { m1 }
# # and @C { m2 }.  For example, when using ejection chains we ensure
# # that @C { start_gm } and @C { continue_gm } have @C { g } among
# # their children, not @C { m1 } and @C { m2 }.  In this way, when
# # @C { m1 } and @C { m2 } signal that there is a defect, the signal
# # is received via @C { g }, not directly from @C { m1 } and @C { m2 }.
# # @PP
# # Some solvers need specific groupings.  The Kempe meet move
# # operation (Section {@NumberOf time_solvers.kempe}) is an
# # example:  its precondition specifies that a particular group
# # monitor must be present.  This is permissible, and as with all
# # preconditions it imposes a requirement on the caller of the
# # operation to ensure that the precondition is satisfied when the
# # operation is called.  But such requirements should not prohibit the
# # presence of other group monitors.  For example, the implementation
# # of the Kempe meet move operation begins with a tiny search for the
# # group monitor it requires.  If other group monitors are present
# # nearby, that is not a problem.  If this example is followed,
# # multiple requirements for group monitors will not conflict.
# # @PP
# # There is a danger that group monitors will multiply, slowing down
# # the solve and confusing its logic.  It is best if each function
# # that creates a group monitor takes responsibility for deleting it
# # later, even if this means creating the same group monitors over and
# # over again.  Timing tests conducted by the author show that adding
# # and deleting the group monitors used by the various solvers in this
# # guide takes an insignificant amount of time.
# # @PP
# Two monitors are @I correlated when they monitor the same thing, not
# formally usually, but in reality.  For example, if two events are
# joined by a link events constraint, and one is fixed to the other,
# then two spread events monitors, one for each event, will be correlated.
# @PP
# A @I defect is a specific point of imperfection in a solution,
# represented concretely by a monitor with non-zero cost.
# @I { Correlated defects } are a problem for ejection chains.  The cost
# of each defect separately might not be large enough to end the chain
# if removed, causing the chain to terminate in failure, whereas if
# it was clear that there was really only one problem, the chain
# might be able to repair it and continue.
# @PP
# The solution to this problem works like this.  Suppose we know that
# monitors @C { m1 } and @C { m2 } are correlated.  We introduce a group
# monitor @C { g }, make @C { m1 } and @C { m2 } children of @C { g },
# and ensure that when handling defects we use @C { g }, not @C { m1 }
# and @C { m2 }.  For example, when using ejection chains we ensure
# that @C { start_gm } and @C { continue_gm } have @C { g } among
# their children, not @C { m1 } and @C { m2 }.  In this way, when
# @C { m1 } and @C { m2 } signal that there is a defect, the signal
# is received via @C { g }, not directly from @C { m1 } and @C { m2 }.
# # @PP
# # So correlated monitors
# # should be grouped, whenever possible.  These groups are the
# # equivalence classes of the correlation relation, which is
# # clearly an equivalence relation.  A grouping of correlated
# # monitors is called a @I { correlation grouping }.
# @PP
# A @I { correlation grouping } is a whole set of group monitors
# created to group together correlated monitors.  A function which
# creates a correlation grouping works as follows.  Monitors not
# relevant to the grouping remain as they were.  Relevant monitors
# are deleted from any parents they have, and partitioned into groups
# of correlated monitors.  For each group containing two or more
# monitors, a group monitor called a @I { correlation group monitor }
# is made, the monitors are made children of it, and it is made a
# child of the solution object.  For each group containing one monitor,
# that monitor is made a child of the solution, and no group monitor
# is made.  Any group monitors other than the solution object which
# lose all their children because of these changes are deleted, possibly
# causing further deletions of childless group monitors.
# @PP
# A function which deletes a correlation grouping visits all monitors
# relevant to the grouping and deletes those parents of those
# monitors whose @C { sub_tag } indicates that they are part of
# the correlation grouping.  The deleting is done by calls to
# @C { KheGroupMonitorBypassAndDelete }.
# @End @SubSection
# 
# @SubSection
#     @Title { Correlation problems involving demand defects }
#     @Tag { eject2.correlation.demand }
# @Begin
# @LP
# # Section {@NumberOf time_structural.monitor} discusses the problem
# # of correlated monitors, and how it can be solved by grouping.
# Demand monitors (Chapter {@NumberOf matchings}), representing nodes in
# the global tixel matching, are a particularly difficult case for
# correlation grouping.  They often correlate with other monitors,
# but not in a simple way.  This section discusses the problems they
# raise and how KHE handles them.
# @PP
# Demand monitors correlate with avoid clashes monitors:  when there is
# a clash, there will be both an avoid clashes defect and an ordinary
# demand defect.  They also correlate with avoid unavailable times, limit
# busy times, and limit workload monitors:  when there is a hard avoid
# unavailable times defect, for example, there will also be a demand
# defect.  There are several possible ways to handle these correlations.
# # This section explores several
# # ways of handling these correlations, beginning with grouping.
# @PP
# @I { Group the correlated monitors. }  Grouping is the ideal solution
# for correlation problems, but it does not work here.  There are two
# reasons for this.
# @PP
# First, although every avoid clashes defect has a correlated ordinary
# demand defect, unless the resource involved is preassigned there is
# no way to predict which monitors will be correlated, since that
# depends on which resource is assigned to the demand monitors' tasks.
# @PP
# Second, grouping workload demand monitors with the resource monitors
# they are derived from has a subtle flaw.  A demand defect is really
# the whole set of demand monitors that compete for the insufficient
# supply.  (These sets are quite unpredictable and cannot themselves
# be grouped.)  A workload demand defect shows up, not in the workload
# demand monitor itself, but in an ordinary demand monitor that
# competes with it.  This is because the last demand tixel to change
# its domain and require rematching is the one that misses out on the
# available supply, and workload demand monitors never change their
# domains.
# # So this grouping still leaves two correlated defects
# # ungrouped:  the group monitor and the unmatched ordinary demand monitor.
# @PP
# If grouping does not work, then one of the correlated monitors has
# to be detached or otherwise ignored.  There are several ways to do this.
# @PP
# @I { Detach demand monitors. }  This does not work, because no-one
# notices that six Science meets are scheduled simultaneously when
# there are only five Science laboratories, and the resulting time
# assignment is useless.
# @PP
# @I { Attach demand monitors but exclude them from continue group monitors. }
# This prevents correlated monitors from appearing on defect lists,
# but both costs continue to be added to the solution cost, so
# removing the resource defect alone does not produce an overall
# improvement.  The chain terminates in failure; the ejector cannot
# see that repairing the resource defect could work.
# @PP
# @I { Attach demand monitors but exclude them from the solution and
# continue group monitors; add a limit monitor holding them. }  Then
# other monitors will be repaired, but no chain which increases the total
# number of demand defects will be accepted.  This has two problems.
# @PP
# First, it can waste time constructing chains which fall at the last
# hurdle when it is discovered that they increase demand defects.  This
# is particularly likely during time repair:  the six Science meets
# problem could well occur and pass unnoticed for a long time.
# # Resource repair usually
# # runs faster, and wasting time then does not matter so much.
# @PP
# Second, although it prevents demand defects from increasing, it
# does not repair them.  This rules it out for time repair, which
# is largely about repairing demand defects, but it may not matter
# for resource repair.  Resource repair cannot reduce the number of
# demand defects unless it moves meets:  merely assigning resources
# reduces the domains of demand tixels, which cannot reduce demand
# defects.  Even moving meets is unlikely to reduce demand defects
# during resource repair, since many of those moves will have been
# tried previously, during time repair.
# @PP
# @I { Detach correlated resource monitors. }  Instead of detaching
# demand monitors, detach the resource monitors they correlate with.
# This is our preferred approach, and we will now go on to consider
# how to carry it out for each kind of resource monitor.  The main
# danger is @I { inexactness }:  if some detached resource monitor is
# not modelled exactly by the demand monitors that replace it, then
# some defects may be handled imperfectly.
# @PP
# @I { Detach all avoid clashes monitors. }  For every avoid clashes
# defect there is an ordinary demand defect.  The only inexactness
# is that avoid clashes monitors may have any weights, whereas demand
# monitors have equal weight, usually 1 (hard).  But avoid clashes
# constraints usually have weight 1 (hard) or more, so this does not
# seem to be a problem in practice, given that, as the specification
# says, hard constraint violations should be few in good solutions.
# @PP
# @I { Detach avoid unavailable times monitors that give rise to
# workload demand monitors. }  These are monitors with weight at least 1
# (hard).  The modelling here is exact apart from any difference in
# hard weight, so again there is no problem in practice.
# @PP
# @I { Detach limit busy times monitors that give rise to workload
# demand monitors. }  These are monitors with weight at least 1
# (hard) which satisfy the subset tree condition
# (Section {@NumberOf matchings.workload.tree}).  Apart from the
# possible difference in hard weight, this is exact except for one
# problem:  limit busy times constraints can impose both a lower
# and an upper limit on resource availability in some set of times,
# and workload demand monitors do not model lower limits at all.
# @PP
# This can be fixed by (conceptually) breaking each limit busy times
# monitor into two, an underload monitor and an overload monitor, and
# detaching the overload monitor but not the underload monitor.  KHE
# expresses this idea in a different way, chosen because it also
# solves the problem presented by limit workload monitors, to be
# discussed in their turn.
# @PP
# Limit busy times monitors have two attributes, @C { Minimum } and
# @C { Maximum }, such that less than @C { Minimum } busy times is an
# underload, and more than @C { Maximum } is an overload.  Add a third
# attribute, @C { Ceiling }, such that @C { Ceiling >= Maximum }, and
# specify that, with higher priority than the usual rule, when the number
# of busy times exceeds @C { Ceiling } the deviation is 0.
# @PP
# Function @C { KheLimitBusyTimesMonitorSetCeiling }
# (Section {@NumberOf monitoring.limitbusy}) may be called to set the
# ceiling.  Setting it to @C { INT_MAX } (the default value) produces
# the usual rule.  Setting it to @C { Maximum } is equivalent to
# detaching overload monitoring.
# @PP
# @I { Detach limit workload monitors that give rise to workload
# demand monitors. }  Limit workload monitors are similar to limit
# busy times monitors whose set of times is the entire cycle.
# However, the demand monitors derived from a limit workload
# monitor do not necessarily model even the upper limit exactly
# (Section {@NumberOf matchings.workload.construct}).  This
# problem can be solved as follows.
# @PP
# Consider a resource with a hard limit workload monitor and some
# hard workload demand monitors derived from it, and suppose that
# all of these monitors are attached.  As the resource's workload
# increases, it crosses from a `white region' of zero cost into a
# `grey region' where the limit workload monitor has non-zero cost
# but the workload demand monitors do not, and then into a `black
# region' where both the limit workload monitor and the workload
# demand monitors have non-zero cost.  This black region is the problem.
# @PP
# The problem is solved by adding a @C { Ceiling } attribute to
# limit workload monitors, as for limit busy times monitors.
# Function @C { KheLimitWorkloadMonitorSetCeiling }
# (Section {@NumberOf monitoring_resource_monitors_workload})
# sets the ceiling.  As before, the default value
# is @C { INT_MAX }.  The appropriate alternative value is not
# @C { Maximum }, but rather a value which marks the start of
# the black region, so that the limit workload monitor's cost
# drops to zero as the workload crosses from the grey region
# to the black region.  In this way, all workload overloads
# are reported, but by only one kind of monitor at any one time.
# @PP
# There is one anomaly in this arrangement:  a repair that
# reduces workload from the black region to the grey region
# does not always decrease cost.  This is a pity but it is
# very much a second-order problem, given that the costs
# involved are all hard costs, so that in practice repairs
# are wanted that reduce them to zero.  What actually happens is
# that one repair puts a resource above the white zone, and this
# stimulates a choice of next repair which returns it to the white
# zone.  Repairs which move between the grey and black zones are
# possible but are not likely to lie on successful chains anyway,
# so it does not matter much if their handling is imperfect.
# @PP
# The appropriate value for @C { Ceiling } is the number of times
# in the cycle minus the total number of workload demand monitors
# for the resource in question, regardless of their origin.  When
# the resource's workload exceeds this value, there will be at
# least one demand defect, and it is time for the limit workload
# monitor to bow out.
# @PP
# To summarize all this:  there is some choice during resource
# repair, but detaching resource monitors (with ceilings) always
# works, and it is the only method that works during time repair.
# @End @SubSection
# 
# @SubSection
#     @Title { Practical support for correlation grouping }
#     @Tag { eject2.correlation.correlation }
# @Begin
# @LP
# To install and remove the correlation groupings used by ejection chains, call
# @ID @C {
# void KheEjectionChainPrepareMonitors(KHE_SOLN soln,
#   KHE_OPTIONS options);
# void KheEjectionChainUnPrepareMonitors(KHE_SOLN soln);
# }
# This includes detaching some resource monitors, as in the plan
# from Section {@NumberOf eject2.correlation.demand}.  This section
# explains exactly what @C { KheEjectionChainPrepareMonitors } does.
# # @PP
# # Function @C { KheEjectionChainPrepareMonitors }
# # (Section {@NumberOf eject2.grouping.correlation})
# # creates correlation groupings of some correlated monitors, and
# # detaches others, in preparation for ejection chain repair.
# @PP
# It is convenient to have standard values for the sub-tags and
# sub-tag labels of the group monitors created by grouping functions,
# both correlation and focus.  So KHE defines type
# @ID {0.90 1.0} @Scale @C {
# typedef enum {
#  KHE_SUBTAG_SPLIT_EVENTS,	      /* "SplitEventsGroupMonitor"           */
#  KHE_SUBTAG_DISTRIBUTE_SPLIT_EVENTS, /* "DistributeSplitEventsGroupMonitor" */
#  KHE_SUBTAG_ASSIGN_TIME,	      /* "AssignTimeGroupMonitor"            */
#  KHE_SUBTAG_PREFER_TIMES,	      /* "PreferTimesGroupMonitor"           */
#  KHE_SUBTAG_SPREAD_EVENTS,	      /* "SpreadEventsGroupMonitor"          */
#  KHE_SUBTAG_LINK_EVENTS,	      /* "LinkEventsGroupMonitor"            */
#  KHE_SUBTAG_ORDER_EVENTS,	      /* "OrderEventsGroupMonitor"           */
#  KHE_SUBTAG_ASSIGN_RESOURCE,	      /* "AssignResourceGroupMonitor"        */
#  KHE_SUBTAG_PREFER_RESOURCES,	      /* "PreferResourcesGroupMonitor"       */
#  KHE_SUBTAG_AVOID_SPLIT_ASSIGNMENTS, /* "AvoidSplitAssignmentsGroupMonitor" */
#  KHE_SUBTAG_AVOID_CLASHES,	      /* "AvoidClashesGroupMonitor"          */
#  KHE_SUBTAG_AVOID_UNAVAILABLE_TIMES, /* "AvoidUnavailableTimesGroupMonitor" */
#  KHE_SUBTAG_LIMIT_IDLE_TIMES,	      /* "LimitIdleTimesGroupMonitor"        */
#  KHE_SUBTAG_CLUSTER_BUSY_TIMES,      /* "ClusterBusyTimesGroupMonitor"      */
#  KHE_SUBTAG_LIMIT_BUSY_TIMES,	      /* "LimitBusyTimesGroupMonitor"        */
#  KHE_SUBTAG_LIMIT_WORKLOAD,	      /* "LimitWorkloadGroupMonitor"         */
#  KHE_SUBTAG_LIMIT_ACTIVE_INTERVALS,  /* "LimitActiveIntervalsGroupMonitor"  */
#  KHE_SUBTAG_LIMIT_RESOURCES,         /* "LimitResourcesGroupMonitor"        */
#  KHE_SUBTAG_ORDINARY_DEMAND,	      /* "OrdinaryDemandGroupMonitor"        */
#  KHE_SUBTAG_WORKLOAD_DEMAND,	      /* "WorkloadDemandGroupMonitor"        */
#  KHE_SUBTAG_KEMPE_DEMAND,	      /* "KempeDemandGroupMonitor"           */
#  KHE_SUBTAG_NODE_TIME_REPAIR,	      /* "NodeTimeRepairGroupMonitor"        */
#  KHE_SUBTAG_LAYER_TIME_REPAIR,	      /* "LayerTimeRepairGroupMonitor"       */
#  KHE_SUBTAG_TASKING,		      /* "TaskingGroupMonitor"               */
#  KHE_SUBTAG_ALL_DEMAND		      /* "AllDemandGroupMonitor"             */
# } KHE_SUBTAG_STANDARD_TYPE;
# }
# for the sub-tags, and the strings in comments, obtainable by calling
# @ID @C {
# char *KheSubTagLabel(KHE_SUBTAG_STANDARD_TYPE sub_tag);
# }
# for the corresponding sub-tag labels.  There is also
# @ID @C {
# KHE_SUBTAG_STANDARD_TYPE KheSubTagFromTag(KHE_MONITOR_TAG tag);
# }
# which returns the appropriate sub-tag for a group monitor whose
# children have the given @C { tag }.
# @PP
# Let's return now to @C { KheEjectionChainPrepareMonitors }.  It begins
# by partitioning the events of @C { soln }'s instance into classes,
# placing events into the same class when following the fixed assignment
# paths out of their meets proves that their meets must run at the same
# times.  It then groups event monitors as follows.
# @PP
# @I { Split events and distribute split events monitors. } For each
# class, it groups together the split events and distribute split
# events monitors that monitor the events of that class.  It gives
# sub-tag @C { KHE_SUBTAG_SPLIT_EVENTS } to any group monitors it
# creates.  There is also a @C { KHE_SUBTAG_DISTRIBUTE_SPLIT_EVENTS }
# subtag, but it is not used.
# # Event splitting
# # is handled structurally, so @C { KheEjectionChainPrepareMonitors }
# # does nothing with these monitors.
# # They usually have provably zero fixed cost, so are already detached.
# @PP
# @I { Assign time monitors. }  For each class, it groups the assign
# time monitors that monitor the events of that class, giving sub-tag
# @C { KHE_SUBTAG_ASSIGN_TIME } to any group monitors.
# @PP
# @I { Prefer times monitors. }  Within each class, it groups those
# prefer times monitors that monitor events of that class whose
# constraints request the same set of times, giving sub-tag
# @C { KHE_SUBTAG_PREFER_TIMES } to any group monitors.
# @PP
# @I { Spread events monitors. }  For each spread events monitor, it
# finds the set of classes that hold the events it monitors.  It
# groups attached spread events monitors whose sets of classes are
# equal, giving sub-tag @C { KHE_SUBTAG_SPREAD_EVENTS } to any group
# monitors.  Strictly speaking, only monitors whose constraints
# request the same time groups with the same limits should be grouped,
# but that check is not currently being made.
# @PP
# @I { Link events monitors. }  Like split events monitors, these are
# usually handled structurally, so it does nothing with them.  They
# usually have provably zero fixed cost, so are already detached.
# @PP
# @I { Order events monitors. }  For each order events monitor, it
# finds the sequence of classes that hold the two events it monitors.
# It groups attached order events monitors whose sequences of classes
# are equal, giving sub-tag @C { KHE_SUBTAG_ORDER_EVENTS } to any group
# monitors.  Strictly speaking, only monitors whose constraints request
# the same event separations should be grouped, but that check is not
# currently being made.
# @PP
# Next, @C { KheEjectionChainPrepareMonitors } partitions the event
# resources of @C { soln }'s instance into classes, placing event
# resources into the same class when following the fixed assignment
# paths out of their tasks proves that they must be assigned the same
# resources.  It then groups event resource monitors as follows.
# @PP
# @I { Assign resource monitors. }  For each class, it groups the
# assign resource monitors of that class's event resources, giving
# sub-tag @C { KHE_SUBTAG_ASSIGN_RESOURCE } to any group monitors.
# @PP
# @I { Prefer resources monitors. }  Within each class, it groups those
# prefer resources monitors that monitor the event resources of that
# class whose constraints request the same set of resources, giving
# sub-tag @C { KHE_SUBTAG_PREFER_RESOURCES } to any group monitors.
# @PP
# @I { Avoid split assignments monitors. }  There seems to be no
# useful correlation grouping of these monitors, so nothing is done with
# them.  They may be handled structurally, in which case they will
# have provably zero fixed cost and will be already detached.
# @PP
# Students who follow the same curriculum have the same timetable.  So
# for each resource type @C { rt } such that a call to
# {0.95 1.0} @Scale @C { KheResourceTypeDemandIsAllPreassigned(rt) }
# (Section {@NumberOf resource_types})
# shows that its resources are all preassigned,
# {0.95 1.0} @Scale @C { KheEjectionChainPrepareMonitors }
# groups the resource monitors of @C { rt }'s resources as follows.
# @PP
# @I { Avoid clashes monitors. }  It groups those avoid clashes monitors
# derived from the same constraint whose resources attend the same
# events, giving sub-tag
# {0.95 1.0} @Scale @C { KHE_SUBTAG_AVOID_CLASHES } to any group monitors.
# @PP
# @I { Avoid unavailable times monitors. }  It groups avoid unavailable
# times monitors derived from the same constraint whose resources attend
# the same events, giving any group monitors sub-tag
# @C { KHE_SUBTAG_AVOID_UNAVAILABLE_TIMES }.
# @PP
# @I { Limit idle times monitors }.  It groups limit idle times monitors
# derived from the same constraint whose resources attend the same events,
# giving sub-tag @C { KHE_SUBTAG_LIMIT_IDLE_TIMES } to any group monitors.
# @PP
# @I { Cluster busy times monitors. }  It groups cluster busy times monitors
# derived from the same constraint whose resources attend the same events,
# giving any group monitors sub-tag @C { KHE_SUBTAG_CLUSTER_BUSY_TIMES }.
# @PP
# @I { Limit busy times monitors. }  It groups limit busy times monitors
# derived from the same constraint whose resources attend the same events,
# giving sub-tag @C { KHE_SUBTAG_LIMIT_BUSY_TIMES } to any group monitors.
# @PP
# @I { Limit workload monitors. }  It groups limit workload monitors
# derived from the same constraint whose resources attend the same events,
# giving sub-tag @C { KHE_SUBTAG_LIMIT_WORKLOAD } to any group monitors.
# @PP
# @I { Limit active intervals monitors. }  It groups limit active intervals
# monitors derived from the same constraint whose resources attend the same
# events, giving sub-tag @C { KHE_SUBTAG_LIMIT_ACTIVE_INTERVALS } to any
# group monitors.
# @PP
# Fixed assignments between meets are taken into account when deciding
# whether two resources attend the same events.  As far as resource
# monitors are concerned, it is when the resource is busy that matters,
# not which meets it attends.
# @PP
# When there is a global tixel matching,
# @C { KheEjectionChainPrepareMonitors } also treats some resource
# monitors as in Section {@NumberOf eject2.correlation.demand},
# whether they are grouped or not:
# @BulletList
# 
# @LI @OneRow {
# It detaches all attached avoid clashes monitors.
# }

# @LI @OneRow {
# For each attached avoid unavailable times monitor @C { m } for which
# a workload demand monitor with originating monitor @C { m } is
# present (all hard ones, usually), it detaches @C { m }.
# }
# 
# @LI @OneRow {
# For each attached limit busy times monitor @C { m } for which a
# workload demand monitor with originating monitor @C { m } is
# present (all hard ones satisfying the subset tree condition of
# Section {@NumberOf matchings.workload.tree}, usually), if
# @C { m }'s lower limit is 0 it detaches @C { m }, otherwise it
# sets @C { m }'s @C { ceiling } attribute to its @C { maximum }
# attribute, by calling @C { KheLimitBusyTimesMonitorSetCeiling }.
# }
# 
# @LI @OneRow {
# For each attached limit workload monitor @C { m } for which a workload
# demand monitor with originating monitor @C { m } is present (all hard
# ones, usually), it sets @C { m }'s @C { ceiling } attribute to the cycle
# length minus the number of workload demand monitors for @C { m }'s
# resource (regardless of origin), or 0 if this is negative, by
# calling @C { KheLimitWorkloadMonitorSetCeiling }.
# }
# 
# @EndList
# Section {@NumberOf eject2.correlation.demand} explains all this.
# @PP
# Finally, @C { KheEjectionChainPrepareMonitors } groups demand monitors as
# follows.  If a limit monitor containing these monitors is wanted, a
# separate call is needed (Section {@NumberOf eject2.repair.focus}).
# @PP
# @I { Ordinary demand monitors. }  For each set of meets such that
# the fixed assignment paths out of those meets end at the same meet,
# it groups the demand monitors of those meets' tasks, giving sub-tag
# @C { KHE_SUBTAG_MEET_DEMAND } to any group monitors.  The reasoning
# is that the only practical way to repair an ordinary demand defect
# is to change the assignment of its meet (or some other clashing meet),
# which will affect all the demand monitors grouped with it here.
# @PP
# @I { Workload demand monitors. }  These remain ungrouped.  As
# explained in Section {@NumberOf eject2.correlation.demand},
# workload demand defects appear only indirectly, as competitors
# of ordinary demand defects.
# @End @SubSection
# 
# @EndSubSections
# @End @SubSection
# 
# @SubSection
#     @Title { Focus grouping }
#     @Tag { eject2.focus }
# @Begin
# @LP
# @I { Focus groupings } are useful monitor groupings that are not
# correlation groupings.  They group diverse sets of monitors for
# various purposes.
# @PP
# Focus groupings are often built on correlation groupings:  if a
# monitor that a focus grouping handles is a descendant of a correlation
# group monitor, the correlation group monitor goes into the focus
# grouping, replacing the individual monitors which are its children.
# @PP
# A focus grouping makes one group monitor, called a
# @I { focus group monitor }, not many.  The focus group monitor is
# not made a child of the solution object, nor are its children unlinked
# from any other parents that they may have.  So the focus group monitor
# does not disturb existing calculations in any way; rather, it adds a
# separate calculation on the side.  A focus grouping can be removed
# by passing its focus group monitor to @C { KheGroupMonitorDelete }.
# @PP
# # Functions for creating focus groupings appear elsewhere in this
# # guide.  They include @C { KheKempeDemandGroupMonitorMake }, needed by
# # Kempe and ejecting meet moves (Section {@NumberOf time_solvers.kempe}),
# # and several functions used by ejection chain repair algorithms
# # (Section {@NumberOf ejection.repair.focus}).
# # @PP
# # @LP
# # Section {@NumberOf time_structural.monitor} introduces the concept
# # of focus groupings.
# Ejection chain functions need focus groupings for their start group
# monitors (but not their continue group monitors, since they use the
# solution object for that), and for their limit monitors.
# @C { KheEjectionChainNodeRepairTimes } uses the group monitor
# returned by
# @ID @C {
# KHE_GROUP_MONITOR KheNodeTimeRepairStartGroupMonitorMake(KHE_NODE node);
# }
# as its start group monitor.  The result has sub-tag
# @C { KHE_SUBTAG_NODE_TIME_REPAIR }.  Its children are monitors, or
# correlation groupings of monitors where these are already present, of
# two kinds.  First are all assign time, prefer times, spread events,
# order events, and ordinary demand monitors that monitor the meets
# of @C { node } and its descendants, plus any meets whose assignments
# are fixed, directly or indirectly, to them.  Second are all resource
# monitors.  Only preassigned resources are assigned during time repair,
# but those assignments may cause resource defects which can only be
# repaired by changing time assignments, just because the resources
# involved are preassigned.
# @PP
# @C { KheEjectionChainLayerRepairTimes } chooses one of the group monitors
# returned by
# @ID @C {
# KHE_GROUP_MONITOR KheLayerTimeRepairStartGroupMonitorMake(
#   KHE_LAYER layer);
# KHE_GROUP_MONITOR KheLayerTimeRepairLongStartGroupMonitorMake(
#   KHE_LAYER layer);
# }
# as its start group monitor, depending on option
# @C { es_layer_repair_long }.  The result has
# sub-tag @C { KHE_SUBTAG_LAYER_TIME_REPAIR }, with the same children
# as before, only limited to those that monitor the meets and resources
# of @C { layer }, or (if @C { es_layer_repair_long } is @C { true})
# of layers whose index number is less than or equal to @C { layer }'s.
# @PP
# @C { KheEjectionChainRepairResources } uses the group monitor returned
# by
# @ID @C {
# KHE_GROUP_MONITOR KheTaskingStartGroupMonitorMake(KHE_TAS KING tasking);
# }
# for its start group monitor.  The result has sub-tag
# @C { KHE_SUBTAG_TASKING }, and its children are the following
# monitors (or correlation groupings of those monitors, where those already
# exist):  the assign resource, prefer resources, and avoid split
# assignments monitors, and the resource monitors that monitor the
# tasks and resources of @C { tasking }.  If the tasking is for a
# particular resource type, only monitors of entities of that type
# are included.
# @PP
# To allow an ejection chain to unassign meets temporarily but prevent
# it from leaving meets unassigned in the end, a limit monitor is
# imposed which rejects chains that allow the total cost of assign
# time defects to increase.  This monitor is created by calling
# @ID @C {
# KHE_GROUP_MONITOR KheGroupEventMonitors(KHE_SOLN soln,
#   KHE_MONITOR_TAG tag, KHE_SUBTAG_STANDARD_TYPE sub_tag);
# }
# passing @C { KHE_ASSIGN_TIME_MONITOR_TAG } and
# @C { KHE_SUBTAG_ASSIGN_TIME } as tag parameters.
# @PP
# To prevent the number of unmatched demand tixels from increasing,
# when that is requested by the @C { resource_invariant } option,
# the group monitor returned by function @ID @C {
# KHE_GROUP_MONITOR KheAllDemandGroupMonitorMake(KHE_SOLN soln);
# }
# is used as a limit monitor.  Its sub-tag is @C { KHE_SUBTAG_ALL_DEMAND },
# and its children are all ordinary and workload demand monitors.
# Correlation groupings are irrelevant to limit monitors, so these last
# two functions take no account of them.
# @PP
# When building focus groupings, these public functions
# are often helpful:
# @ID @C {
# bool KheMonitorHasParent(KHE_MONITOR m, int sub_tag,
#   KHE_GROUP_MONITOR *res_gm);
# void KheMonitorAddSelfOrParent(KHE_MONITOR m, int sub_tag,
#   KHE_GROUP_MONITOR gm);
# void KheMonitorDeleteAllParentsRecursive(KHE_MONITOR m);
# }
# Consult the documentation in the source code to find out what they do.
# @End @SubSection

@EndSubSections
@End @Section

@EndSections
@End @Chapter
