@Chapter
    @PartNumber { Part B }
    @PartTitle { Solvers }
    @Title { Introducing Solvers }
    @Tag { general_solvers }
@Begin
@LP
A solver is an operation that makes large-scale changes to a solution.
This chapter introduces solvers, defines interfaces for them, presents
a few high-level ones, and explains some general ideas related to
solving, including setting options and gathering statistics.
@PP
Solvers operate at a high level and should not be cluttered with
implementation details:  their source files will include
@C { khe_platform.h } as usual, but should not include header
file @C { khe_interns.h } which gives access to KHE's internals.
Thus, the user of KHE is as well equipped to write a solver as
its author.
@PP
Many solvers are packaged with KHE.  They are the subject of this
part of the manual, all of which is implemented using
@C { khe_platform.h } but not @C { khe_interns.h }.  To gain
access to these solvers, include header file @C { khe_solvers.h },
which lies in subdirectory @C { src_solvers } of the KHE distribution.
It includes header file @C { khe_platform.h }, so you don't need
that.
@BeginSections

@Section
    @Title { Keeping track of running time }
    @Tag { general_solvers.runningtime }
@Begin
@LP
This section describes KHE's functions for handling time (real
time, that is).
@PP
For the sake of compilations that do not have the Unix system functions
that report time, file @C { khe_solvers.h } has a @C { KHE_USE_TIMING }
preprocessor flag.  Its default value is 1; changing it to 0 will turn
off all calls to Unix timing system functions.  If that is done, the
functions documented in this section will still compile and run
without error, but they will return placeholder values.
@PP
First up we have
@ID @C {
char *KheDateToday(void);
}
This returns the current date as a string in static memory,
or @C { "?" } if @C { KHE_USE_TIMING } is 0.
@PP
KHE offers @I timer objects, of type @C { KHE_TIMER }, which keep
track of running time.  A timer object stores its @I { start time },
the time that it was most recently created or reset.  It may also
store a @I { time limit }, in which case it can report whether that
much time has passed since its start time.  Storing a time limit
does not magically stop the program at the time limit; it is up
to solvers to check the time limit periodically and stop themselves
when it is reached.
@PP
Timers represent a time as a floating point number of seconds.
@C { KHE_NO_TIME }, a synonym for @C { -1.0 }, means `no time'.
Function
@ID @C {
float KheTimeFromString(char *str);
}
converts a string into a floating point time.  If @C { str } is
@C { "-" }, it returns @C { KHE_NO_TIME}, otherwise it returns
the number of seconds represented by @C { str }, in the format
@F { secs }, or @F { mins:secs }, or @F { hrs:mins:secs }.  For
example, @F { 0.5 } is 0.5 seconds, and @F { 5:0 } is 5 minutes.
Conversely,
@ID @C {
char *KheTimeShow(float secs, char buff[20]);
}
returns @C { secs } in string form, using @C { buff } for scratch
memory.  It writes in a more readable format than the input format,
for example @C { "2.5 secs" } or @C { "5.0 mins" }.
@PP
To make a new timer object in arena @C { a }, call
@ID @C {
KHE_TIMER KheTimerMake(char *tag, float limit_in_seconds, HA_ARENA a);
}
The @C { tag } parameter, which must be non-@C{ NULL }, identifies the
timer, and also appears in debug output.  The @C { limit_in_seconds }
parameter is the time limit; it may be @C { KHE_NO_TIME }.  The timer's
start time is set to the time that @C { KheTimerMake } is called.  Also,
@ID @C {
KHE_TIMER KheTimerCopy(KHE_TIMER timer, HA_ARENA a);
}
returns a copy of @C { timer } in arena @C { a }.  Nothing is reset.
@PP
To retrieve the attributes of a timer, call
@ID @C {
char *KheTimerTag(KHE_TIMER timer);
float KheTimerTimeLimit(KHE_TIMER timer);
}
@C { KheTimerTimeLimit } may return @C { KHE_NO_TIME }.
To change them, call
@ID @C {
void KheTimerResetStartTime(KHE_TIMER timer);
void KheTimerResetTimeLimit(KHE_TIMER timer, float limit_in_seconds);
}
@C { KheTimerResetStartTime } resets @C { timer }'s start time
to the time that it is called.  @C { KheTimerResetTimeLimit } resets
@C { timer }'s time limit to @C { limit_in_seconds }, which may
be @C { KHE_NO_TIME } as usual.  Two functions give access to
elapsed time:
@ID @C {
float KheTimerElapsedTime(KHE_TIMER timer);
bool KheTimerTimeLimitReached(KHE_TIMER timer);
}
@C { KheTimerElapsedTime } returns the amount of time that has
elapsed since the most recent call to @C { KheTimerMake } or
@C { KheTimerResetStartTime } for the timer.  @C { KheTimerTimeLimitReached }
returns @C { true } when the elapsed time is equal to or greater than
the time limit (always false when the time limit is @C { KHE_NO_TIME }).
Finally,
@ID {0.98 1.0} @Scale @C {
void KheTimerDebug(KHE_TIMER timer, int verbosity, int indent, FILE *fp);
}
produces a debug print of @C { timer } onto @C { fp } with the
given verbosity and indent.
@PP
Complex solvers may want to keep track of several time limits
simultaneously, for example a global limit plus a limit on the
running time of one phase.  For this there are objects of type
@C { KHE_TIMER_SET }, representing sets of timers.  To create
a new, empty timer set in arena @C { a }, call
@ID @C {
KHE_TIMER_SET KheTimerSetMake(HA_ARENA a);
}
To make a copy of a timer set, call
@ID @C {
KHE_TIMER_SET KheTimerSetCopy(KHE_TIMER_SET timer_set, HA_ARENA a);
}
To add and delete timers, call
@ID @C {
void KheTimerSetAddTimer(KHE_TIMER_SET timer_set, KHE_TIMER timer);
void KheTimerSetDeleteTimer(KHE_TIMER_SET timer_set, KHE_TIMER timer);
}
@C { KheTimerSetDeleteTimer } aborts if @C { timer } is not present
in @C { timer_set }.  There is also
@ID @C {
bool KheTimerSetContainsTimer(KHE_TIMER_SET timer_set, char *tag,
  KHE_TIMER *timer);
}
which seaches for a timer with the given @C { tag } in @C { timer_set }.
If there is one, it sets @C { *timer } to one such timer and returns
@C { true }, otherwise it returns @C { false }.  Function
@ID @C {
bool KheTimerSetTimeLimitReached(KHE_TIMER_SET timer_set);
}
return @C { true } if at least one of the timers of @C { timer_set }
has reached its time limit.  This is the logical moment to stop if
several time limits are present.  Finally,
@ID @C {
void KheTimerSetDebug(KHE_TIMER_SET timer_set, int verbosity,
  int indent, FILE *fp)
}
produces a debug print of @C { timer_set } onto @C { fp } with the
given verbosity and indent.
@PP
The usual way to keep track of running time is by calling the timer
functions of options objects (Section {@NumberOf general_solvers.options}).
These just delegate to a timer set object stored within the options
object.
@End @Section

@Section
    @Title { Options, running time, and time limits }
    @Tag { general_solvers.options }
@Begin
@LP
Solvers have an @C { options } parameter of type @C { KHE_OPTIONS },
holding options that influence their behaviour.  This type is similar
to a Unix environment:  it is a symbol table with strings for its
keys and values.  The KHE main program allows options to be passed
in via the command line.
@PP
To create a new options object containing the empty set of options, call
@ID @C {
KHE_OPTIONS KheOptionsMake(HA_ARENA a);
}
It is created in arena @C { a }, which it remembers and returns in
@ID @C {
HA_ARENA KheOptionsArena(KHE_OPTIONS options);
}
There is no operation to delete an options object when it is no longer
needed; instead, delete or recycle its arena.
#@ID @C {
#void KheOptionsDelete(KHE_OPTIONS options);
#}
#This works by deleting the arena.
@PP
Options can be changed at any time, so when solving in parallel it
is important for different options objects to be passed to each solve.
These can be created by copying using
@ID @C {
KHE_OPTIONS KheOptionsCopy(KHE_OPTIONS options, HA_ARENA a);
}
The copy is stored in arena @C { a }.  @C { KheArchiveParallelSolve }
and @C { KheInstanceParallelSolve }
(Section {@NumberOf general_solvers.parallel}) do this.
@PP
To set an option, and to retrieve the previously set value, the calls
are
@ID @C {
void KheOptionsSet(KHE_OPTIONS options, char *key, char *value);
char *KheOptionsGet(KHE_OPTIONS options, char *key, char *dft);
}
@C { KheOptionsGet } returns the value associated with @C { key }
in the most recent call to @C { KheOptionsSet } with that key.
If there is no such call, it returns @C { dft }, reflecting the
principle that solvers should not rely on their options being
set, but rather should be able to choose a suitable value when
they are absent---a value that may depend upon circumstances,
not necessarily a fixed default value.
@PP
By convention, when an option represents a Boolean, its legal values
are @C { "false" } and @C { "true" }.  On the KHE command line, omitting
the option omits it from the options object, which usually means that
its value is intended to be @C { false }, while including it, either
in the full form @C { "option=true" } or the short form @C { "option" },
gives it value @C { "true" }.  Functions
@ID @C {
void KheOptionsSetBool(KHE_OPTIONS options, char *key, bool value);
bool KheOptionsGetBool(KHE_OPTIONS options, char *key, bool dft);
}
make it easy to handle Boolean options.  @C { KheOptionsSetBool }
calls @C { KheOptionsSet }, with value @C { "true" } or @C { "false" }
depending on @C { value }.  @C { KheOptionsGetBool } calls
@C { KheOptionsGet }, returning an actual Boolean rather than a string.
It aborts if the value is not @C { "false" } or @C { "true" }.  If
there is no value it returns @C { dft }, which, as explained above,
would usually be @C { false }.
@PP
Another common case is when an option represents an integer.
Convenience functions
@ID @C {
void KheOptionsSetInt(KHE_OPTIONS options, char *key, int value);
int KheOptionsGetInt(KHE_OPTIONS options, char *key, int dft);
}
make this case easy.  @C { KheOptionsSetInt } calls @C { KheOptionsSet },
with value equal to @C { value } in string form.  @C { KheOptionsGetInt }
calls @C { KheOptionsGet }, then returns the value converted to an
integer.  It aborts if the value is not an integer.  If there is no
value it returns @C { dft }.
@PP
It is also possible to associate an arbitrary pointer with a key,
by calling functions
@ID @C {
void KheOptionsSetObject(KHE_OPTIONS options, char *key, void *value);
void *KheOptionsGetObject(KHE_OPTIONS options, char *key, void *dft);
}
These work in much the same way as the other functions.
@PP
When @C { KheOptionsCopy } is called, by @C { KheArchiveParallelSolve }
for example, object options are shared between the copies.  Care is
needed, since sharing mutable objects between threads is not safe.
The KHE solvers avoid problems here by not adding any object options
until after the copying has been done:  only single-threaded solve
functions add them.
@PP
Options can be roughly classified into two kinds.  One kind is for
end users, to allow them to try out different possibilities.  Options
of this kind are not set by KHE's solvers, only used.  The other
kind is for KHE's solvers, to allow them to vary the behaviour of
other solvers that they call.  These are set by KHE's solvers, so
it is usually futile for the end user to set them.
@PP
Each option is described along with the solver it affects.  As an aid
to managing option names, there is a convention for beginning option
names with a three-character prefix:
@ID @Tbl
   aformat { @Cell ml { 0i } @F A | @Cell mr { 0i } B }
   mv { 0.5vx }
{
@Rowa
   A { gs_ }
   B { Options set or consulted by general solvers }
@Rowa
   A { ps_ }
   B { Options set or consulted by parallel solvers }
@Rowa
   A { ss_ }
   B { Options set or consulted by structural solvers }
@Rowa
   A { ts_ }
   B { Options set or consulted by time solvers }
@Rowa
   A { rs_ }
   B { Options set or consulted by resource solvers }
@Rowa
   A { es_ }
   B { Options set or consulted by ejection chain solvers }
}
Some options are set by one kind of solver and consulted by
another; such options are hard to classify.  The sole option
consulted by the KHE main program has no prefix.  It is:
@TaggedList

@DTI { @F no_print }
{
If this Boolean option appears in the first list of options on the
@C { khe -s } or @C { khe -r } command line, then solving will
proceed as usual but the result archive will not be printed.
}

# @DTI { @F soln_group }
# {
# The name of the solution group created by @C { khe -s }.  When
# absent, the name is @C { "KHE" } plus a version number.  Absent
# or not, if a solution group with the specified name already exists,
# then @C { "_1" }, @C { "_2" } etc. is appended to the name until
# a fresh name is created.
# }

@EndList
The default values of all Boolean options consulted by KHE code are
always @C { false }; for the other options, a default value is always
given as part of the description of the option.
@PP
Options objects are passed around through solvers, and they are
the natural place to keep other things which are not options,
strictly speaking.  In particular, each option contains a timer
set (Section {@NumberOf general_solvers.runningtime}) which may
be used to keep track of running time and impose time limits.
The relevant functions are
@ID @C {
KHE_TIMER KheOptionsAddTimer(KHE_OPTIONS options, char *tag,
  float limit_in_seconds);
void KheOptionsDeleteTimer(KHE_OPTIONS options, KHE_TIMER timer);
bool KheOptionsContainsTimer(KHE_OPTIONS options, char *tag,
  KHE_TIMER *timer);
bool KheOptionsTimeLimitReached(KHE_OPTIONS options);
void KheOptionsTimerSetDebug(KHE_OPTIONS options, int verbosity,
  int indent, FILE *fp);
}
@C { KheOptionsAddTimer } creates a new timer with the given
attributes and adds it to the timer set within @C { options }.
@C { KheOptionsDeleteTimer } deletes the given timer from that
timer set; it must be present.  @C { KheOptionsContainsTimer }
searches the timer set for a timer with the given @C { tag }.
@C { KheOptionsTimeLimitReached } returns @C { true } if any of
the timer set's time limits have been reached, and
@C { KheOptionsTimerSetDebug } produces a debug print of
the timer set of @C { options } onto @C { fp } with the given
verbosity and indent.  These functions are simple delegations
to the corresponding timer set functions.
# @PP
# @ID @C {
# void KheOptionsTimeLimitBegin(KHE_OPTIONS options, char *tag,
#   float limit_in_secs);
# float KheOptionsTimeLimitEnd(KHE_OPTIONS options, char *tag);
# bool KheOptionsTimeLimitNow(KHE_OPTIONS options, char *tag,
#   float *elapsed_time_in_secs);
# bool KheOptionsTimeLimitReached(KHE_OPTIONS options);
# void KheOptionsTimeLimitReset(KHE_OPTIONS options, char *tag,
#   float limit_in_secs);
# }
# Calls on functions @C { KheOptionsTimeLimitBegin } and
# @C { KheOptionsTimeLimitEnd } must occur in matching pairs;
# their tags must also match, and be distinct from the tags
# of any other time limits that lie in @C { options } at the
# same time.  For example:
# @ID @C {
# KheOptionsTimeLimitBegin(options, "tabu_search", 10.0);
# TabuSearch(soln, options);
# KheOptionsTimeLimitEnd(options, "tabu_search");
# }
# limits the amount of time spent in function @C { TabuSearch } to
# at most 10 seconds, in addition to any other time limits currently
# in effect.  The tags are not copied, they are shared with the caller.
# @PP
# Within a matching
# @C { KheOptionsTimeLimitBegin }"/"@C { KheOptionsTimeLimitEnd } pair
# with its tag, @C { KheOptionsTimeLimitNow } returns @C { true } and
# sets @C { elapsed_time_in_secs } to the amount of time, in seconds,
# that has elapsed since the time limit with the given tag was begun
# (or reset, see below).  Outside such a pair it returns @C { false }.
# @PP
# Sadly, setting a time limit does not mean that the solve will magically
# end after that amount of time.  Instead, solvers that are likely to
# consume large amounts of time are expected to decide for themselves
# when it is time to stop.  Function @C { KheOptionsTimeLimitReached } is
# a convenient way to do this.  It returns @C { true } when, for at
# least one of the time limits currently in force, the amount of time
# elapsed since that time limit began equals or exceeds its limit.
# @PP
# @C { KheOptionsTimeLimitReset } does nothing if it does not lie inside a
# matching @C { KheOptionsTimeLimitBegin }"/"@C { KheOptionsTimeLimitEnd }
# pair with its tag.  If it does lie inside such a pair, it resets that
# time limit's starting time to the current time, and either resets the
# time limit to @C { limit_in_secs } or, if @C { limit_in_secs } is
# @C { -1.0 }, leaves it.  To terminate a solver if it has found no
# improvement in a given time, reset the time limit each time an
# improvement is found.
# @C { KheOptionsTimeLimit(options) } is not @C { -1.0 },
# @C { KheOptionsTimeNow(options) } is not @C { -1.0 }, and
# @C { KheOptionsTimeNow(options) >=  KheOptionsTimeLimit(options) }.
# @PP
# Options objects are passed around through solvers, and they are
# the natural place to keep other things which are not options,
# strictly speaking.  In particular, each option contains an
# optional time limit, as well as a timer object that can
# be used to find out how much time has passed since a solve
# began.  The relevant functions are
# @ID @C {
# float KheOptionsTimeNow(KHE_OPTIONS options);
# void KheOptionsSetTimeLimit(KHE_OPTIONS options, float limit_in_secs);
# float KheOptionsTimeLimit(KHE_OPTIONS options);
# bool KheOptionsTimeLimitReached(KHE_OPTIONS options);
# }
# @C { KheOptionsTimeNow } returns the wall clock time in seconds since
# @C { options } was created, either from scratch or by copying.  The
# caller needs to make sure that this is the time when the solution
# being solved was
# created.  @C { KheOptionsSetTimeLimit } sets a time limit variable in
# @C { options } to @C { limit_in_secs }, and @C { KheOptionsTimeLimit }
# returns that value.  Its default value is @C { -1.0 }, a special value
# meaning `no limit'.
# @PP
# Setting a time limit does not mean that the solve will magically end
# after that amount of time.  Instead, solvers that are likely to consume
# large amounts of time are expected to compare @C { KheOptionsTimeNow }
# with @C { KheOptionsTimeLimit } and decide for themselves that it
# is time to stop.  Function @C { KheOptionsTimeLimitReached } is
# a convenient way to do this.  It returns @C { true } when
# @C { KheOptionsTimeLimit(options) } is not @C { -1.0 },
# @C { KheOptionsTimeNow(options) } is not @C { -1.0 }, and
# @C { KheOptionsTimeNow(options) >=  KheOptionsTimeLimit(options) }.
@PP
Finally, there is one stray function,
@ID {0.98 1.0} @Scale @C {
KHE_FRAME KheOptionsFrame(KHE_OPTIONS options, char *key, KHE_SOLN soln);
}
This returns a shared common frame for use by solvers, as described
in Section {@NumberOf extras.frames}.
@End @Section

@Section
    @Title { General solving }
    @Tag { general_solvers.general }
@Begin
@LP
A @I { solver } is a function that finds solutions, or partial
solutions, to instances.  A @I { general solver } solves an instance
completely, unlike, say, a @I { time solver } which only finds time
assignments, or a @I { resource solver } which only finds resource
assignments.  A general solver may split meets, build layer trees and
task trees, assign times and resources, and so on without restriction.
@PP
The recommended interface for a general solver, defined in @C { khe.h }, is
@ID {0.95 1.0} @Scale @C {
typedef KHE_SOLN (*KHE_GENERAL_SOLVER)(KHE_SOLN soln, KHE_OPTIONS options);
}
It will usually return the solution it is given, but it may return a
different solution to the same instance, in which case it should delete
the solution it is given.  Its second parameter, @C { options }, is a
set of options (Section {@NumberOf general_solvers.options}) which may
be used to vary the behaviour of the solver.
@PP
The main general solver distributed with KHE is
@ID @C {
KHE_SOLN KheGeneralSolve2020(KHE_SOLN soln, KHE_OPTIONS options);
}
This single-threaded general solver works by calling functions
defined elsewhere in this guide.  It returns the solution it is
given.  The name includes the year it was completed and will change
from time to time.  In publications and solution group names
it is referred to as KHE20.
@PP
@C { KheGeneralSolve2020 } assumes that @C { soln } is as returned by
@C { KheSolnMake }, so it begins with @C { KheSolnSplitCycleMeet } and
@C { KheSolnMakeCompleteRepresentation }.  Then it calls solvers
defined in this guide:  it builds a layer tree and task tree,
attaches demand monitors, calls @C { KheCycleNodeAssignTimes } to
assign times, and the three @C { KheTaskingAssignResources } functions
to assign resources, ending with @C { KheSolnEnsureOfficialCost }.
@PP
For convenience, @C { KheGeneralSolve2020 } calls
@ID @C {
KheOptionsSetRunningTime(options, elapsed_time);
}
(Section {@NumberOf general_solvers.options}) just before returning,
where @C { elapsed_time } is its running time, obtained by calling
@C { KheOptionsTimeLimitNow } (Section {@NumberOf general_solvers.options})
on a time limit with tag @C { "general" } which it creates at its start
and deletes at its end.  Arguably, this is not quite right, because
@C { soln } has to be created before @C { KheGeneralSolve2020 } is
called, and more work could be done on @C { soln } afterwards.  However,
callers can easily reset the running time if they wish to.
@PP
By convention, options set or consulted directly by @C { KheGeneralSolve2020 }
have names beginning with @C { gs_ }.  Here is the full list:
@TaggedList

@DTI { @F gs_diversifier }
{
An integer option which, when set, causes @C { KheGeneralSolve2020 }
to set the diversifier of the solution it is given to the given
value.  When omitted, the diversifier retains the value it has
when @C { KheGeneralSolve2020 } is called.
}

@DTI { @F gs_time_limit }
{
A string option defining a soft time limit for the solve.
Enforcement is up to particular solvers; this option merely calls
@C { KheOptionsSetTimeLimit } (Section {@NumberOf general_solvers.options}).
The format is as for function @C { KheTimeFromString } described
above (Section {@NumberOf general_solvers.runningtime}):  either
@C { "-" }, meaning no time limit (the default value), or
@F { secs }, or @F { mins:secs }, or @F { hrs:mins:secs }.  For
example, @F { 10 } is 10 seconds, and @F { 5:0 } is 5 minutes.
}

@DTI { @F gs_matching_off }
{
A Boolean option which, when @C { "true" }, instructs
@C { KheGeneralSolve2020 } to refrain from installing
the global tixel matching (Chapter {@NumberOf matchings}).
}

@DTI { @F gs_hard_constraints_only }
{
A Boolean option which, when @C { "true" }, instructs
@C { KheGeneralSolve2020 } to detach monitors for soft
constraints, by calling @C { KheDetachLowCostMonitors }
with @C { min_weight } set to @C { KheCost(1, 0) } at the
start of the solve, and @C { KheAttachLowCostMonitors } at
the end.  See Section {@NumberOf general_solvers.monitor.detach}
for these functions.
}

@DTI { @F gs_monitor_evenness }
{
A Boolean option which, when @C { "true" }, instructs
@C { KheGeneralSolve2020 } to install evenness monitors
(Section {@NumberOf matchings.evenness}).
}

@DTI { @F gs_propagate_unavailable_times_off }
{
A Boolean option which, when @C { "true" }, instructs
@C { KheGeneralSolve2020 } to omit its usual call to
@C { KhePropagateUnavailableTimes }
(Section {@NumberOf general_solvers.monitor.unavail}).
}

@DTI { @F gs_time_assignment_only }
{
A Boolean option which, when @C { "true" }, instructs
@C { KheGeneralSolve2020 } to exit early, leaving the
solution in its state after time assignment.
}

@DTI { @F gs_unassignment_off }
{
A Boolean option which, when @C { "true" }, instructs
@C { KheGeneralSolve2020 } to omit the calls to
@C { KheSolnTryTaskUnAssignments }
(Section {@NumberOf resource_solvers.unassignments}) and
@C { KheSolnTryMeetUnAssignments }
(Section {@NumberOf time_solvers.basic}) during the cleanup phase.
}

@DTI { @F gs_event_timetable_monitor }
{
During the resource assignment phase of @C { KheGeneralSolve2020 },
this option has a value of type @C { KHE_EVENT_TIMETABLE_MONITOR },
and holds the result of the call
@ID @C {
KheEventTimetableMonitorMake(soln, KheInstanceFullEventGroup(ins)); 
}
The monitor is attached.  Before and after that phase, the option is
either absent or has value @C { NULL }.  The point of this is that
this event timetable monitor is expensive to create and probably
too expensive to update during time assignment, but it is useful
during resource assignment.  So this arrangement gives resource
assignment algorithms access to a single shared event timetable
monitor, at little cost.
}

@DTI { @F gs_debug_monitor_id }
{
This option is a string identifying a monitor.  It has two or more
fields, separated by slashes.  The first field is a constraint Id;
the others identify a point of application of the constraint.  For
example, @C { "Constraint:5/Nurse3/27" } is the monitor for constraint
@C { "Constraint:5" } at point of application @C { Nurse3 }, offset
@C { 27 }.  This option is used by @C { KheGeneralSolve2020 } to
define option @F { gs_debug_monitor }, as explained next.  The
conversion from string to monitor is carried out by function
@C { KheSolnRetrieveMonitor } (Section {@NumberOf monitoring_monitors}).
}

@DTI { @F gs_debug_monitor }
{
This option is set at the start of @C { KheGeneralSolve2020 }, when
@C { gs_debug_monitor_id } is present, to the monitor identified by
@C { gs_debug_monitor_id }.  Any solver can reference it and use it
as a hint to produce debug output relevant to that monitor.  At
present only ejectors do this:  they produce debug output focussed on
answering the question `Why is the defect represented by this monitor
not removed by the ejection chain algorithm?'.
}

@DTI { @F gs_debug_rtm }
{
This option is a string whose format is @C { resource:timegroup }.
When present it causes @C { KheGeneralSolve2020 } to call function
@C { KheResourceTimetableMonitorSetDebug } from
Section {@NumberOf monitoring_timetables_resource} to set up
debugging of the resource timetable monitor of @C { resource } at the
times of @C { timegroup }.  This will abort if macro @C { DEBUG_CELL }
in source file @C { khe_resource_timetable_monitor.c } does not have
value 1.
}

@EndList
@C { KheGeneralSolve2020 } is affected indirectly by many other
options, via the solvers it calls.
@PP
Function
@ID @C {
void KheSolveDebug(KHE_SOLN soln, KHE_OPTIONS options, char *fmt, ...);
}
produces a one-line debug of the current state of a solve.  For
conciseness it always prints onto @C { stderr } with indent
2.  The print contains @C { soln }'s instance name, diversifier,
cost, and running time (if @C { options } contains a timer called
@C { "global" }; if not, the running time is omitted), and ends
with whatever @C { fprintf(stderr, fmt, ...) } would produce,
followed by a newline.
@End @Section

@Section
    @Title { Parallel solving }
    @Tag { general_solvers.parallel }
@Begin
@LP
Function
@ID @C {
void KheArchiveParallelSolve(KHE_ARCHIVE archive,
  KHE_GENERAL_SOLVER solver, KHE_OPTIONS options,
  KHE_SOLN_TYPE soln_type, HA_ARENA_SET as);
}
solves the instances of @C { archive } in parallel.
@PP
Each individual solve is carried out by @C { solver }, which is
passed a fresh solution and a copy of @C { options }.  The fresh
solution is as returned by @C { KheSolnMake } except that the
diversifier is set, as explained below.
@PP
If solutions are saved (see options @C { ps_soln_group } and
@C { ps_first_soln_group } below), parameter @C { soln_type }
determines whether they are left as they are or reduced to
placeholders (Section {@NumberOf solutions.top.placeholder}).
@C { KHE_SOLN_WRITABLE_PLACEHOLDER } is recommended because
it recycles a large amount of memory, while still permitting
the solutions to be written.  Only if further processing of
the solutions is intended would they be left as they are,
by passing @C { KHE_SOLN_ORDINARY }.
@PP
If @C { as != NULL }, each call to @C { KheSolnMake } is passed an
arena set, as Section {@NumberOf solutions.top.arenas} suggests.
There is one arena set per thread, with @C { as } serving one thread
and freshly created arena sets serving the others.  At the end, the
idle arenas in all arena sets other than @C { as } are moved into
@C { as }, and the arena sets in all solutions kept by the two
functions are set to @C { as } by calls to @C { KheSolnSetArenaSet }
(Section {@NumberOf solutions.top.arenas}).  If further parallel
solving of these solutions is attempted, it will be necessary to
install distinct arena sets first.
@PP
There is also
@ID @C {
KHE_SOLN KheInstanceParallelSolve(KHE_INSTANCE ins,
  KHE_GENERAL_SOLVER solver, KHE_OPTIONS options,
  KHE_SOLN_TYPE soln_type, HA_ARENA_SET as);
}
Behind the scenes it is the same, but it solves a single instance
rather than an entire archive, and it returns any one best solution
rather than storing solutions in a solution group.
@PP
All objects created by these two functions, except for solutions
that are kept, are deleted before they return.  This includes
all copies of @C { options }, and all freshly created arena sets.
@PP
Options consulted by parallel solvers have names beginning
with @C { ps_ }.  Here is the full list:
@TaggedList

@DTI { @F ps_threads }
{
The number of threads used for solving.  This includes the initial
thread, the one that called @C { KheArchiveParallelSolve } or
@C { KheInstanceParallelSolve }, so the value must be at least 1.
If @F ps_threads is absent, or present but KHE has been compiled
with multi-threading off, its value is taken to be 1.
}

@DTI { @F ps_make }
{
The number of solutions @C { KheArchiveParallelSolve } and
@C { KheInstanceParallelSolve } make per instance.  If
@F ps_make is absent, its value is taken to be 1.
}

@DTI { @F ps_no_diversify }
{
For each instance, the solutions passed to @C { solver } are identical
except that the diversifier of the first is 0, the diversifier of the
second is 1, and so on.  The solver may use these values to create
diverse solutions.  Boolean option {@F ps_no_diversify}, when @C { "true" },
gives the same diversifier (namely 0) to all solutions.  All
solutions should then turn out the same, except when there are time
limits:  they can cut off solving at slightly different moments.
}
 
@DTI { @F ps_keep }
{
The maximum number of solutions that @C { KheArchiveParallelSolve }
keeps (stores in @C { ps_soln_group } below) per instance.  If
@F ps_keep is absent, its value is taken to be 1.  The best
@F ps_keep solutions are kept.  @C { KheInstanceParallelSolve }
does not consult this option; it always keeps (in fact, returns)
one solution, the best it found.
}

@DTI { @F ps_soln_group }
{
A string option, which, if present, causes a solution group to
be added to @C { archive } holding the best @F ps_keep solutions
to each instance.  The value of the string is the name of the
solution group.  If there is already a solution group in
@C { archive } with that name, the name is extended so that
it does not clash with existing solution group names.
@LP
If @F ps_soln_group is omitted, or its name is wrong, no solution
group is made.  When solutions have been found but they are not in
the result archive, this is the usual reason.
}

@DTI { @F ps_first_soln_group }
{
Like @C { ps_soln_group } except that the solution group
holds one solution for each instance, the one whose solve was
started first.  This solution will thus be added to two solution
groups if @C { ps_soln_group } and @C { ps_first_soln_group }
are both present and the solution is one of the @F ps_keep best
for its instance.  (Actually, in that case the solution is
copied, owing to the possible need to store different running
times in the two versions, as explained just below under
option {@F ps_time_measure}).  The author uses
@ID @F {
ps_first_soln_group=KHE20 ps_soln_group=KHE20x8
}
to get the results of a single run and of a best of 8 run,
while producing only eight (not nine) solutions.  If present,
@F ps_first_soln_group will precede the other in the archive.
}

@DTI { @F ps_time_measure }
{
Measuring running time is awkward for parallel solving.
This option says how to do it.
@LP
If @C { ps_time_measure } is @C { "omit" }, the parallel solver does not
set the solutions' running times.  They have the values given to them by
@C { solver }.  If @C { solver } is @C { KheGeneralSolve2020 }, for example,
each holds the wall clock time from when @C { KheGeneralSolve2020 }
was called to when it returns.  This is useful when all solutions
are kept, for showing how running times vary.  It is misleading
when @C { ps_threads } exceeds the number of processors.
@LP
If @C { ps_time_measure } is @C { "shared" }, each instance monopolizes
all threads while its solutions are being constructed.  There
is some idle time for some threads while they wait for others to
finish off the current instance, making the total wall clock
time of the solve somewhat larger than for @C { "omit" }.  Then
the running times of all solutions for one instance are set to
the same value:  the wall clock time from when the first solve of
their instance began until the last solve ended.  This is useful
when only the best, or the few best, solutions are being kept,
because it records in those solutions how long it really takes to
find them, given that all the solutions have to be found, albeit
in parallel, before the few can be chosen.
@LP
If @C { ps_time_measure } is @C { "auto" } (the default value), then
the behaviour is as for @C { "omit" } when @C { ps_keep >= ps_make },
and as for @C { "shared" } when @C { ps_keep < ps_make }.
@LP
This option only affects the solutions stored in @F { ps_soln_group }.
The solutions stored in @F { ps_first_soln_group } have the running
times given to them by @C { solver }.
}

@DTI { @F ps_time_limit }
{
A string option defining a soft time limit for solving each
instance.  The parallel solver will stop initiating solves of
an instance once the wall clock time since it initiated the
first solve of that instance exceeds this limit, even if
the requested { @F ps_make } solves have not all begun.  The
format is as for function @C { KheTimeFromString } described
above (Section {@NumberOf general_solvers.runningtime}):  either
@C { "-" }, meaning no time limit (which is the default value), or
@F { secs }, or @F { mins:secs }, or @F { hrs:mins:secs }.  For
example, @F { 10 } is 10 seconds, and @F { 5:0 } is 5 minutes.
}

#@DTI { @F ps_free_memory }
#{
#A Boolean option saying what to do with memory (other than
#memory used to hold solutions) at the end of the parallel
#solve.  If @C { true }, all such excess memory is freed,
#which sounds reasonable but which could be very slow,
#depending on what @C { malloc } and @C { free } do behind
#the scences.  If @C { false }, the memory is not freed, which is
#definitely the right choice when the whole run will end shortly
#(e.g. after an archive write) anyway.
#@LP
#@I { more to say here -- sti ll to do }
#}

@EndList
On the author's quad-core machine, finding 8 solutions by running
8 threads is usually somewhat faster than finding them by running
4 threads.  The effect is not large.  It is presumably due to the
hardware hyper-threading feature, which allows up to two threads
to run on each processor in an attempt to improve throughput.  But
there is also a random element concerning whether two slow solves
happen to be allocated to the same thread, so it is hard to be sure.
@PP
Parallelism is obtained via functions @C { pthread_create } and
@C { pthread_join } from the Posix threads library.  KHE has been
carefully designed to ensure that operations carried out in parallel
on distinct solutions cannot interfere with each other.  If you do not
have Posix threads, a simple workaround documented in KHE's makefile
will allow you to compile KHE without it.  The only difference is that
@C { KheArchiveParallelSolve } and @C { KheInstanceParallelSolve }
will find their solutions sequentially rather than in parallel.
@End @Section

#@Section
#    @Title { Benchmarking }
#    @Tag { general_solvers.benchmarking }
#@Begin
#@LP
#@I { This section is still applicable, but its general approach
#has been rendered obsolete by the advent of command-line options.
#So in time it is likely to be deleted. }
#@PP
#For benchmarking (that is, for gathering statistics while a solver runs),
#KHE offers
#@ID @C {
#void KheBenchmark(KHE_ARCHIVE archive, KHE_GENERAL_SOLVER solver,
#  char *solver_name, char *author_name, char test_label,
#  KHE_STATS_TABLE_TYPE table_type);
#}
#It solves @C { archive }, possibly several times, using @C { solver },
#writing the results into files in directory @C { "stats" } of the
#current directory.  Some files are archives, others contain tables of
#statistics recording the performance of @C { solver }, printed by KHE's
#statistics functions (Section {@NumberOf general_solvers.stats}).
#@PP
#Parameter @C { solver_name } is a brief name for @C { solver }, suited
#for use in the header of a table column; @C { author_name } is the name
#of the author of the solver; and @C { test_label } (a character between
#@C { 'A' } and @C { 'Z' }) determines which tests are performed and
#which files are written.  These may change from time to time.  See
#the top of file @C { khe_sm_benchmark.c } for current details.
#@PP
#Parameter @C { table_type } determines the format of any tables
#written.  Its values are
#@ID @C {
#typedef enum {
#  KHE_STATS_TABLE_PLAIN,
#  KHE_STATS_TABLE_LOUT,
#  KHE_STATS_TABLE_LATEX
#} KHE_STATS_TABLE_TYPE;
#}
#which request plain text, Lout, or LaTeX format.
#@PP
#@C { KheBenchmark } takes it upon itself to skip some instances of
#the archive it is given.  To see which are skipped, consult function
#@C { KheBenchmarkTryInstance } in file @C { khe_sm_benchmark.c }.
#If it comes upon such an instance, it includes a row for it in the
#tables it prints, but it does not attempt to solve it, and it leaves
#the entries for that row blank.
## For example, it skips instance
## @C { DK-HG-12 } of the standard XHSTT-2013 data set.  Solving
## @C { DK-HG-12 } well involves choosing a good set of meets to
## @I not assign times to, something KHE's solvers have no idea about.
#@End @Section

@Section
    @Title { Monitor adjustments }
    @Tag { general_solvers.monitor }
@Begin
@LP
In this section we present solvers which adjust monitors.  The monitor
grouping solvers from Chapter {@NumberOf ejection} would also fit here.
#At present we have only one to present here, although
@BeginSubSections

@SubSection
    @Title { Detaching low-cost monitors }
    @Tag { general_solvers.monitor.detach }
@Begin
@LP
On difficult instances it might make sense to forget about
monitors whose violation only costs a small amount, and
concentrate on monitors whose violations are more serious.
This idea is implemented by
@ID @C {
void KheDetachLowCostMonitors(KHE_SOLN soln, KHE_COST min_weight,
  KHE_GROUP_MONITOR low_cost_gm);
}
It detaches all monitors whose combined weight is less than
@C { min_weight }.  For example,
@ID @C {
KheDetachLowCostMonitors(soln, KheCost(1, 0), NULL);
}
detaches all monitors for soft constraints.
@PP
If @C { low_cost_gm } is non-@C { NULL }, all monitors that
were detached by this operation (all monitors whose combined
weight is less than @C { min_weight } that were not already
detached) are made children of @C { low_cost_gm }.  One can
then call
@ID @C {
void KheAttachLowCostMonitors(KHE_GROUP_MONITOR low_cost_gm);
}
to reattach these monitors whenever desired.  They will no longer
be children of @C { low_cost_gm } after this is done.
@End @SubSection

@SubSection
    @Title { Changing the multipliers of cluster busy times monitors }
    @Tag { general_solvers.monitor.multiplier }
@Begin
@LP
Cluster busy times monitors have a @I { multiplier }, which is an
integer that their true costs are multiplied by
(Section {@NumberOf monitoring.clusterbusy}).  To aid
in the use of multipliers, there is an operation
@ID @C {
void KheSetMonitorMultipliers(KHE_SOLN soln, char *str, int val);
}
which finds each cluster busy times constraint @C { c } whose name
or Id contains @C { str }, and sets the multiplier of every monitor
derived from @C { c } to @C { val }.  To return the monitors to their
original state, make the same call again, but with @C { val } set to 1.
@End @SubSection

@SubSection
    @Title { Propagating unavailable times to resource monitors }
    @Tag { general_solvers.monitor.unavail }
@Begin
@LP
A resource @M { r }'s @I { unavailable times }, @M { U sub r }, is a
set of times taken from certain monitors of non-zero weight that apply
to @M { r }:  all times in avoid unavailable times monitors, all times
in limit busy times monitors with maximum limit 0, and all times
in positive time groups of cluster busy times constraints with
maximum limit 0.  In this section we do not care about the weight of
these monitors, provided it is non-zero.  We simply combine all these
times into @M { U sub r }.
@PP
Suppose that @M { r } has a cluster busy times or limit active intervals
monitor @M { m } with a time group @M { T } such that @M { T subseteq U sub r }.
Then, although @M { T } could be busy, it is not likely to be busy,
and it is reasonable to let @M { m } know this, by calling
@C { KheClusterBusyTimesMonitorSetNotBusyState }
(Section {@NumberOf monitoring.clusterbusy}) or
@C { KheLimitActiveIntervalsMonitorSetNotBusyState }
(Section {@NumberOf monitoring.limitactive}).
@PP
KHE offers a solver that implements this idea:
@ID @C {
bool KhePropagateUnavailableTimes(KHE_SOLN soln, KHE_RESOURCE_TYPE rt);
}
For each resource @M { r } of type @C { rt } in @C { soln }'s instance
(or for each resource of the instance if @C { rt } is @C { NULL }), it
calculates @M { U sub r }, and, if @M { U sub r } is non-empty, it
checks every time group @M { T } in every cluster busy times and
limit active intervals monitor for @M { r }.  For each
@M { T subseteq U sub r }, it calls the function appropriate to
the monitor, with @C { active } set to @C { false } if @M { T }
is positive, and to @C { true } if @M { T } is negative.  It
returns @C { true } if it changed anything.
@PP
There is no corresponding function to undo these settings.  As
cutoff indexes increase they become irrelevant anyway.
@End @SubSection

@EndSubSections
@End @Section

@Section
    @Title { Generating files of tables and graphs }
    @Tag { general_solvers.stats }
@Begin
@LP
KHE offers a module for generating files containing tables and
graphs.  Any number of files may be generated simultaneously,
even in parallel, although an individual file cannot be generated
in parallel.  One file may contain any number of tables and graphs,
and these can be generated simultaneously, although not in parallel.
@PP
To begin and end a file, call
@ID @C {
KHE_FILE KheFileBegin(char *file_name, KHE_FILE_FORMAT fmt);
void KheFileEnd(KHE_FILE kf);
}
This writes a file called @C { file_name } in sub-directory
@C { stats } (which the user must have created previously) of the
current directory.  The file is opened by @C { KheFileBegin } and
closed by @C { KheFileEnd }.  @C { KheFileEnd } also reclaims all
memory (taken from a specially created arena) used by all tables
and graphs of that file.  Three file formats are supported:
@ID @C {
typedef enum {
  KHE_FILE_PLAIN,
  KHE_FILE_LOUT,
  KHE_FILE_LOUT_STANDALONE,
  KHE_FILE_LATEX
} KHE_FILE_FORMAT;
}
These represent plain text, Lout, standalone Lout (i.e. ready
for converstion to Encapsulated PostScript) and LaTeX.  Only
the two Lout values support graphs.  To generate the actual
tables and graphs, see the following subsections.
@BeginSubSections

@SubSection
    @Title { Tables }
    @Tag { general_solvers.stats.tables }
@Begin
@LP
To generate tables, make matching pairs of calls to the following
functions in between the calls to @C { KheFileBegin } and
@C { KheFileEnd }:
@ID {0.98 1.0} @Scale @C {
KHE_TABLE KheTableBegin(KHE_FILE kf, int col_width, char *corner,
  bool with_average_row, bool with_total_row, bool highlight_cost_minima,
  bool highlight_time_minima, bool highlight_int_minima);
void KheTableEnd(KHE_TABLE kt);
}
The table is begun by @C { KheTableBegin }, and finished, including
being written out to file @C { kf }, by @C { KheTableEnd }.  Where
the file format permits, a label will be associated with the table:
the file name for the first table, the file name followed by an
underscore and 2 for the second table, and so on.  The value
of the table is created in between these two calls, by calling
functions to be presented shortly.  Because the entire table is
saved in memory until @C { KheTableEnd } is called, these other
calls may occur in any order.  In particular it is equally
acceptable to generate a table row by row or column by column.  
@PP
Parameter @C { col_width } determines the width in characters of
each column when the format is @C { KHE_FILE_PLAIN }; it is ignored
by the other formats.  Parameter @C { corner } is printed in the
top left-hand corner of the table.  It must be non-@C { NULL },
but it can be the empty string.
@PP
Each entry in the table has a type, which may be either @I { string },
@I { cost }, @I { time } (really just an arbitrary @C { float }), or
@I { int }.  If @C { with_average_row } is @C { true }, the table ends
with an extra row.  Each entry in this row contains the average of the
non-blank, non-string entries above it, if they all have the same type;
otherwise the entry is blank.  If @C { with_total_row } is @C { true },
the effect is the same except that totals are printed, not averages.
@PP
If @C { highlight_cost_minima } is @C { true }, the minimum values
of type @I cost in each row appear in bold font, or marked by an
asterisk in plain text.  Parameters @C { highlight_time_minima }
and @C { highlight_int_minima } are the same except that they
highlight values of type @I time or @I { int }.
@PP
A caption can be added by calling
@ID @C {
void KheTableCaptionAdd(KHE_TABLE kt, char *fmt, ...);
}
at any time between @C { KheTableBegin } and @C { KheTableEnd },
as often as desired.  This does what @C { printf } would do with the
arguments after @C { file_name }.  The results of all calls are
saved and printed as a caption by @C { KheTableEnd }.
@PP
In any given table, each row except the first (header) row must
be declared, by calling
@ID @C {
void KheTableRowAdd(KHE_TABLE kt, char *row_label, bool rule_below);
}
The rows appear in the order of the calls.  Parameter @C { row_label }
both identifies the row and appears in the first (header) column of
the table.  If @C { rule_below } is @C { true }, the row will have a
rule below it.  The header row always has a rule below it, and there is
always a rule below the last row (not counting any average or total row).
@PP
In the same way, non-header columns are declared, in order, by calls to
@ID @C {
void KheTableColAdd(KHE_TABLE kt, char *col_label, bool rule_after);
}
where @C { col_label } both identifies the column and appears in the
first (header) row of the table, and setting @C { rule_after } to
@C { true } causes a rule to be printed after the column.
@PP
To add an entry to the table, call any one of these functions:
@ID @C {
void KheTableEntryAddString(KHE_TABLE kt, char *row_label,
  char *col_label, char *str);
void KheTableEntryAddCost(KHE_TABLE kt, char *row_label,
  char *col_label, KHE_COST cost);
void KheTableEntryAddTime(KHE_TABLE kt, char *row_label,
  char *col_label, float time);
void KheTableEntryAddInt(KHE_TABLE kt, char *row_label,
  char *col_label, int val);
}
These add an entry to @C { kt } at row @C { row_label } and column
@C { col_label }, aborting if these are unknown or an entry has
already been added there.  If no entry is ever added at some
position, the table will be blank there.  The entry's format depends
on the call.  For example,
@ID {0.98 1.0} @Scale @C {
KheTableEntryAddCost(file_name, row_label, col_label, KheSolnCost(soln));
}
adds a solution cost to the table which will be formatted in
the standard way.
@PP
Strings passed to these functions are copied where required,
so mutating strings are not a concern.  There is no locking, so
calls which create and add to tables should be single-threaded.
@End @SubSection

@SubSection
    @Title { Graphs }
    @Tag { general_solvers.stats.graphs }
@Begin
@LP
To generate graphs in Lout format, make matching pairs of calls to
the following functions in between the calls to @C { KheFileBegin }
and @C { KheFileEnd }:
@ID @C {
KHE_GRAPH KheGraphBegin(KHE_FILE kf);
void KheGraphEnd(KHE_GRAPH kg);
}
To set options which control the overall appearance of the graph, call
@ID @C {
void KheGraphSetWidth(KHE_GRAPH kg, float width);
void KheGraphSetHeight(KHE_GRAPH kg, float height);
void KheGraphSetXMax(KHE_GRAPH kg, float xmax);
void KheGraphSetYMax(KHE_GRAPH kg, float ymax);
void KheGraphSetAboveCaption(KHE_GRAPH kg, char *val);
void KheGraphSetBelowCaption(KHE_GRAPH kg, char *val);
void KheGraphSetLeftCaption(KHE_GRAPH kg, char *val, char *gap);
void KheGraphSetRightCaption(KHE_GRAPH kg, char *val, char *gap);
}
These determine the width and height of the graph (in centimetres),
the maximum x and y values, and the small captions above, below,
to the left of, and to the right of the graph.  If calls to these
functions are not made, the options remain unspecified, causing
Lout's graph package to substitute default values for them in its
usual way.  The caption values must be valid Lout source.
@PP
@C { KheGraphSetLeftCaption } and @C { KheGraphSetRightCaption }
have the extra @C { gap } parameter.  This controls the gap
between the caption and the graph.  At a minimum one can write
@ID @C {
KheGraphSetLeftCaption(kg, "Caption", "0c");
}
to have the minimum gap (0 cm), but a larger value is usually
needed, to avoid unsightly overstriking.  The value of @C { gap }
can also be @C { NULL }, in which case Lout's default value is used.
@PP
Any number of @I { datasets } may be displayed on one graph; each
dataset is a sequence of points.  Often there is just one dataset.
To create a dataset, call
@ID {0.95 1.0} @Scale @C {
KHE_DATASET KheDataSetAdd(KHE_GRAPH kg,
  KHE_DATASET_POINTS_TYPE points_type, KHE_DATATSET_PAIRS_TYPE pairs_type);
}
where @C { points_type } has type
@ID @C {
typedef enum {
  KHE_DATASET_POINTS_NONE,
  KHE_DATASET_POINTS_CROSS,
  KHE_DATASET_POINTS_SQUARE,
  KHE_DATASET_POINTS_DIAMOND,
  KHE_DATASET_POINTS_CIRCLE,
  KHE_DATASET_POINTS_TRIANGLE,
  KHE_DATASET_POINTS_PLUS,
  KHE_DATASET_POINTS_FILLED_SQUARE,
  KHE_DATASET_POINTS_FILLED_DIAMOND,
  KHE_DATASET_POINTS_FILLED_CIRCLE,
  KHE_DATASET_POINTS_FILLED_TRIANGLE
} KHE_DATASET_POINTS_TYPE;
}
and says what to print at each data point (nothing, or a cross, etc.),
and @C { pairs_type } has type
@ID @C {
typedef enum {
  KHE_DATATSET_PAIRS_NONE,
  KHE_DATATSET_PAIRS_SOLID,
  KHE_DATATSET_PAIRS_DASHED,
  KHE_DATATSET_PAIRS_DOTTED,
  KHE_DATATSET_PAIRS_DOT_DASHED,
  KHE_DATATSET_PAIRS_DOT_DOT_DASHED,
  KHE_DATATSET_PAIRS_DOT_DOT_DOT_DASHED,
  KHE_DATASET_PAIRS_YHISTO,
  KHE_DATASET_PAIRS_SURFACE_YHISTO,
  KHE_DATASET_PAIRS_FILLED_YHISTO,
  KHE_DATASET_PAIRS_XHISTO,
  KHE_DATASET_PAIRS_SURFACE_XHISTO,
  KHE_DATASET_PAIRS_FILLED_XHISTO
} KHE_DATASET_PAIRS_TYPE;
}
and says what connects each successive pair of points (nothing, a solid
line, a dashed line, a histogram, etc.).  These are converted into
values of the @C { points } and @C { pairs } options of the @F "@Data"
symbol of Lout's Graph package.  The Lout User's Guide has examples of
what is produced.
@PP
Function
@ID @C {
void KhePointAdd(KHE_DATASET kd, float x, float y);
}
adds a point to a dataset.  The points are generated in the order
received, so in practice, successive calls to @C { KhePointAdd }
on the same dataset should have increasing x values.
@PP
Several datasets can be built simultaneously.  This can be useful
for recording several quantities as a solver proceeds.
@End @SubSection

@EndSubSections
@End @Section

#@Section
#    @Title { Gathering statistics (obsolete) }
#    @Tag { general_solvers.old_stats }
#@Begin
#@LP
#KHE offers a module for gathering statistics.  It can
#calculate running times and generate files containing
#tables in several formats, and graphs in Lout format.
#@BeginSubSections
#
#@SubSection
#    @Title { Running time and date }
#    @Tag { general_solvers.old_stats.runningtime }
#@Begin
#@LP
#To find out how long something takes to run, objects of
#type @C { KHE_STATS_TIMER } (the usual pointer to a private
#record) are used.  Each records one moment in time.  To create
#and delete these timer objects, the functions are
#@ID @C {
#KHE_STATS_TIMER KheStatsTimerMake(void);
#void KheStatsTimerDelete(KHE_STATS_TIMER st);
#}
#@C { KheStatsTimerMake } returns a new timer, initialized by calling
#@C { KheStatsTimerReset } on it, and @C { KheStatsTimerDelete }
#deletes @C { st }, reclaiming the memory it used.  There is also
#@ID @C {
#KHE_STATS_TIMER KheStatsTimerCopy(KHE_STATS_TIMER st);
#}
#which copies @C { st }, producing a new timer holding the same time
#as @C { st }.  The other functions are
#@ID @C {
#void KheStatsTimerReset(KHE_STATS_TIMER st);
#float KheStatsTimerNow(KHE_STATS_TIMER st);
#}
#@C { KheStatsTimerReset } resets the time held within @C { st }
#to the time when @C { KheStatsTimerReset } was called.
#@C { KheStatsTimerNow } compares the time recorded in @C { st }
#(when @C { KheStatsTimerReset } was last called) with
#the time now and reports the difference in seconds.  Both
#functions may be called any number of times on the same timer.
#Any number of timers may be used independently.
#@PP
#Because wall clock times are used, times measured within one thread
#of a parallel solve will not in general measure the time consumed
#by that thread.  However, a parallel solver can be called between
#@C { KheStatsTimerReset } and @C { KheStatsTimerNow }, and then
#they will reliably measure the elapsed time of the parallel solve.
#@PP
#Also offered is
#@ID @C {
#char *KheStatsDateToday(void);
#}
#which returns the current date as a string in static memory.
#@PP
#For the sake of compilations that do not have the Unix system functions
#called by these functions, file @C { khe.h } has a @C { KHE_USE_TIMING }
#preprocessor flag.  Its default value is 1; changing it to 0 will turn
#off all calls to Unix timing system functions.  If that is done, all
#functions will still compile and run without error, but
#@C { KheStatsTimerNow } will always return @C { -1.0 }, and
#@C { KheStatsDateToday } will return @C { "?" }.
#@End @SubSection
#
##@SubSection
##    @Title { Monitoring memory usage }
##    @Tag { general_solvers.stats.memory }
##@Begin
##@LP
##To find out how much memory the entire process is using, including
##all threads, call
##@ID @C {
##float KheStatsMemory(void);
##}
##This returns the total amount of memory consumed so far, in kilobytes.
##@PP
##For the sake of compilations that do not have the Unix system function
##(@C { getrusage }) called by @C { KheStatsMemory }, file @C { khe.h } has
##a @C { KHE_USE_MEMORY_MONITORING } preprocessor flag.  Its default value
##is 1; changing it to 0 will turn off all calls to @C { getrusage }.  If
##that is done, KHE will still compile and run without error, but
##@C { KheStatsMemory } will return @C { -1.0 }.
##@End @SubSection
#
#@SubSection
#    @Title { Files of tables and graphs }
#    @Tag { general_solvers.old_stats.files }
#@Begin
#@LP
#The main thing that the stats module does is generate files of tables
#and graphs.  Any number of files may be generated simultaneously (not
#in parallel, because the stats module has no locking, but by one
#thread).  One file may contain any number of tables and graphs,
#although only one may be generated at a time within any one file.
#@PP
#To begin and end a file, call
#@ID @C {
#void KheStatsFileBegin(char *file_name);
#void KheStatsFileEnd(char *file_name);
#}
#This writes a file called @C { file_name } in sub-directory
#@C { stats } of the current directory (which the user must have
#created previously).  The file is opened by @C { KheStatsFileBegin }
#and closed by @C { KheStatsFileEnd }.  To generate the actual
#tables and graphs, see the following subsections.
#@End @SubSection
#
#@SubSection
#    @Title { Tables }
#    @Tag { general_solvers.old_stats.tables }
#@Begin
#@LP
#To generate tables, make matching pairs of calls to the following
#functions in between the calls to @C { KheStatsFileBegin } and
#@C { KheStatsFileEnd }:
#@ID {0.98 1.0} @Scale @C {
#void KheStatsTableBegin(char *file_name, KHE_STATS_TABLE_TYPE table_type,
#  int col_width, char *corner, bool with_average_row, bool with_total_row,
#  bool highlight_cost_minima, bool highlight_time_minima,
#  bool highlight_int_minima);
#void KheStatsTableEnd(char *file_name);
#}
#Only one table at a time can be generated into a given file, so a
#table is not identified separately from its file.  The table is
#begun by @C { KheStatsTableBegin }, and finished, including being
#written out to the file, by @C { KheStatsTableEnd }.  Where the
#file format permits, a label will be associated with the table:
#the file name for the first table, the file name followed by an
#underscore and 2 for the second table, and so on.  The value
#of the table is created in between these two calls, by calling
#functions to be presented shortly.  Because the entire table is
#saved in memory until @C { KheStatsTableEnd } is called, these
#other calls may occur in any order.  In particular it is equally
#acceptable to generate the table row by row or column by column.  
#@PP
#The format of the table is specified by @C { table_type }:
#@ID @C {
#typedef enum {
#  KHE_STATS_TABLE_PLAIN,
#  KHE_STATS_TABLE_LOUT,
#  KHE_STATS_TABLE_LATEX
#} KHE_STATS_TABLE_TYPE;
#}
#The choices are plain text, Lout, or LaTeX.  Parameter @C { col_width }
#determines the width in characters of each column in plain text; it is
#ignored by the other formats.  Parameter @C { corner } is printed in
#the top left-hand corner of the table.  It must be non-@C { NULL },
#but it can be the empty string.
#@PP
#Each entry in the table has a type, which may be either @I { string },
#@I { cost }, @I { time } (really just an arbitrary @C { float }), or
#@I { int }.  If @C { with_average_row } is @C { true }, the table ends
#with an extra row.  Each entry in this row contains the average of the
#non-blank, non-string entries above it, if they all have the same type;
#otherwise the entry is blank.  If @C { with_total_row } is @C { true },
#the effect is the same except that totals are printed, not averages.
#@PP
#If @C { highlight_cost_minima } is @C { true }, the minimum values
#of type @I cost in each row appear in bold font, or marked by an
#asterisk in plain text.  Parameters @C { highlight_time_minima }
#and @C { highlight_int_minima } are the same except that they
#highlight values of type @I time or @I { int }.
#@PP
#A caption can be added by calling
#@ID @C {
#void KheStatsCaptionMake(char *file_name, char *fmt, ...);
#}
#at any time between @C { KheStatsTableBegin } and @C { KheStatsTableEnd },
#as often as desired.  This does what @C { printf } would do with the
#arguments after @C { file_name }.  The results of all calls are
#saved and printed as a caption by @C { KheStatsTableEnd }.
#@PP
#In any given table, each row except the first (header) row must
#be declared, by calling
#@ID @C {
#void KheStatsRowAdd(char *file_name, char *row_label, bool rule_below);
#}
#The rows appear in the order of the calls.  Parameter @C { row_label }
#both identifies the row and appears in the first (header) column of
#the table.  If @C { rule_below } is @C { true }, the row will have a
#rule below it.  The header row always has a rule below it, and there is
#always a rule below the last row (not counting any average or total row).
#@PP
#In the same way, non-header columns are declared, in order, by calls to
#@ID @C {
#void KheStatsColAdd(char *file_name, char *col_label, bool rule_after);
#}
#where @C { col_label } both identifies the column and appears in the
#first (header) row of the table, and setting @C { rule_after } to
#@C { true } causes a rule to be printed after the column.
#@PP
#To add an entry to the table, call any one of these functions:
#@ID @C {
#void KheStatsAddEntryString(char *file_name, char *row_label,
#  char *col_label, char *str);
#void KheStatsAddEntryCost(char *file_name, char *row_label,
#  char *col_label, KHE_COST cost);
#void KheStatsAddEntryTime(char *file_name, char *row_label,
#  char *col_label, float time);
#void KheStatsAddEntryInt(char *file_name, char *row_label,
#  char *col_label, int val);
#}
#These add an entry to @C { file_name }'s table at row @C { row_label }
#and column @C { col_label }, aborting if these are unknown or an entry
#has already been added there.  If no entry is ever added at some
#position, the table will be blank there.  The entry's format depends
#on the call.  For example,
#@ID {0.98 1.0} @Scale @C {
#KheStatsAddEntryCost(file_name, row_label, col_label, KheSolnCost(soln));
#}
#adds a solution cost to the table which will be formatted in
#the standard way.
#@PP
#All strings passed to these functions that require long-term storage 
#are copied, so mutating strings are not a concern.  On the other
#hand, there is no locking, so calls which create tables should be
#single-threaded, as should calls which modify the same table.
#@End @SubSection
#
#@SubSection
#    @Title { Graphs }
#    @Tag { general_solvers.old_stats.graphs }
#@Begin
#@LP
#To generate graphs in Lout format, make matching pairs of calls to
#the following functions in between the calls to @C { KheStatsFileBegin }
#and @C { KheStatsFileEnd }:
#@ID @C {
#void KheStatsGraphBegin(char *file_name);
#void KheStatsGraphEnd(char *file_name);
#}
#As for tables, only one graph can be generated into a given file at
#a time, and so the graph is identified by the file name.  To set
#options which control the overall appearance of the graph, call
#@ID @C {
#void KheStatsGraphSetWidth(char *file_name, float width);
#void KheStatsGraphSetHeight(char *file_name, float height);
#void KheStatsGraphSetXMax(char *file_name, float xmax);
#void KheStatsGraphSetYMax(char *file_name, float ymax);
#void KheStatsGraphSetAboveCaption(char *file_name, char *val);
#void KheStatsGraphSetBelowCaption(char *file_name, char *val);
#void KheStatsGraphSetLeftCaption(char *file_name, char *val);
#void KheStatsGraphSetRightCaption(char *file_name, char *val);
#}
#These determine the width and height of the graph (in centimetres),
#the maximum x and y values, and the small captions above, below,
#to the left of, and to the right of the graph.  If calls to these
#functions are not made, the options remain unspecified, causing
#Lout's graph package to substitute default values for them in its
#usual way.  The caption values must be valid Lout source.
#@PP
#A caption can be added by calling the same function as for tables:
#@ID @C {
#void KheStatsCaptionMake(char *file_name, char *fmt, ...);
#}
#at any time between @C { KheStatsGraphBegin } and @C { KheGraphTableEnd }.
#@PP
#Any number of @I { datasets } may be displayed on one graph; each
#dataset is a sequence of points.  Often there is just one dataset.
#To create a dataset, call
#@ID @C {
#void KheStatsDataSetAdd(char *file_name, char *dataset_label,
#  KHE_STATS_DATASET_TYPE dataset_type);
#}
#where @C { dataset_label } is used to identify the dataset, and
#@C { dataset_type } determines how the data are presented.  At
#present the stats module offers just one choice:
#@ID @C {
#typedef enum {
#  KHE_STATS_DATASET_HISTO
#} KHE_STATS_DATASET_TYPE;
#}
#but the Lout graph package offers many others, so it would not
#be difficult to expand the choices here.  @C { KHE_STATS_DATASET_HISTO }
#prints a histogram.  The x values of the dataset's points should be
#increasing integers; the y values are the frequencies.  Function
#@ID @C {
#void KheStatsPointAdd(char *file_name, char *dataset_label,
#  float x, float y);
#}
#adds a point to a dataset.  The points are generated in the order
#received, so in practice, successive calls to @C { KheStatsPointAdd }
#on the same dataset should have increasing x values.
#@End @SubSection
#
#@EndSubSections
#@End @Section

@Section
    @Title { Exponential backoff }
    @Tag { general_solvers.backoff }
@Begin
@LP
One strategy for making solvers faster is to do a lot of what is
useful, and not much of what isn't useful.  When something is
always useful, it is best to simply do it.  When something might
be useful but wastes a lot of time when it isn't, it is best to
try it, observe whether it is useful, and do more or less of it
accordingly.  Solvers that do this are said to be @I { adaptive }.
@PP
For example, suppose there is a choice of two or more methods of
doing something.  In that case, information can be kept about how
successful each method has been recently, and the choice can be
weighted towards recently successful methods.
@PP
However, this section is concerned with a different situation,
involving just one method.  Suppose there is a sequence of
@I opportunities to apply this method, and that as each opportunity
arrives, the solver can choose to apply the method or not.  Typically,
the method will be a repair method:  repair is optional.  If the
solver @I accepts the opportunity, the method is then run and either
@I succeeds (does something useful) or @I fails (does nothing useful).
Otherwise, the solver @I declines the opportunity.  So opportunities
are classified as successful, failed, or declined.
@PP
@I { Exponential backoff } from computer network implementation is
a form of adaptation suited to this situation.  It works as follows.
If the solver applies the method and it is successful, then it forgets
all history and will accept the next opportunity.  But if the solver
applies the method and it fails, then it remembers the total number
of failed opportunities @M { F } (including this one) since the
last successful opportunity, and does not accept another
opportunity until after it has declined @M { 2 sup {F-1} }
opportunities.  Declined opportunities do not count as failures.
@PP
Here are some examples.  Each character is one opportunity; @C { S }
is a successful opportunity (or the start of the sequence), @C { F }
is a failed one, and @C { . } is a declined one.  Each successful
opportunity makes a fresh start, so the examples all begin with
@C { S } and contain only @C { F } and @C { . } thereafter:
@ID @C {
S
SF.
SF.F..
SF.F..F....
SF.F..F....F........
}
and so on.  Every complete trace of exponential backoff can be broken
at each @C { S } into sub-traces like these.  Methods that always
succeed are tried at every opportunity.  Methods that always fail
are tried only about @M { log sub 2 n } times, where @M { n } is
the total number of opportunities.
@PP
Other rules for which opportunities to accept could be used, rather
than waiting until @M { 2 sup {F-1} } opportunities have been declined.
For example, every opportunity could be accepted, which amounts to
having no backoff at all.  The principles are the same, only the
rule changes.
@PP
KHE offers three operations which together implement exponential backoff:
@ID @C {
KHE_BACKOFF KheBackoffBegin(KHE_BACKOFF_TYPE backoff_type, HA_ARENA a);
bool KheBackoffAcceptOpportunity(KHE_BACKOFF bk);
void KheBackoffResult(KHE_BACKOFF bk, bool success);
}
@C { KheBackoffBegin } creates a new backoff object in arena
@C { a }, passing a @C { backoff_type } value of type
@ID @C {
typedef enum {
  KHE_BACKOFF_NONE,
  KHE_BACKOFF_EXPONENTIAL
} KHE_BACKOFF_TYPE;
}
which determines which rule is used:  none or exponential.
@C { KheBackoffAcceptOpportunity } is called when
an opportunity arises, and returns @C { true } if that opportunity
should be accepted.  In that case, the next call must be to
@C { KheBackoffResult }, reporting whether or not the method was
successful.  As usual, the backoff object's memory is reclaimed
when the arena is deleted.
@PP
Suppose that the program pattern without exponential backoff is
@ID -0.5px @Break @C {
while( ... )
{
  ...
  if( opportunity_has_arisen )
    success = try_repair_method(soln);
  ...
}
}
Then the modified pattern for including exponential backoff is
@ID -0.5px @Break @C {
bk = KheBackoffBegin(KHE_BACKOFF_EXPONENTIAL);
while( ... )
{
  ...
  if( opportunity_has_arisen && KheBackoffAcceptOpportunity(bk) )
  {
    success = try_repair_method(soln);
    KheBackoffResult(bk, success);
  }
  ...
}
}
Each successful {0.95 1.0} @Scale @C { KheBackoffAcceptOpportunity }
is followed by a call to {0.95 1.0} @Scale @C { KheBackoffResult }.
@PP
All backoff objects hold a few statistics, kept only for printing by
@C { KheBackoffDebug } below, and a boolean flag which is @C { true }
if the next call must be to @C { KheBackoffResult }.  When
exponential backoff is requested, a backoff object also maintains
two integers, @M { C } and @M { M }.  @M { C } is the number of
declines since the last accept (or since the backoff object was
created).  @M { M } is the maximum number of opprtunities that
may be declined, defined by
@ID @Math { M ``` = ```
matrix atleft { blbrace }
{
  row col 0               col { @R "    if " F = 0 } 
  row col { 2 sup {F-1} } col { @R "    if " F >= 1 }
}
}
where @M { F } is the number of failures since the last success
(or since the backoff object was created).  The next call to
@C { KheBackoffAcceptOpportunity } will return @C { true } if
@M { C >= M }.  The implementation will not increase @M { M } if
that would cause an overflow.  Overflow is very unlikely, since
an enormous number of opportunities would have to occur first.
@PP
Function
@ID @C {
char *KheBackoffShowNextDecision(KHE_BACKOFF bk);
}
returns @C { "ACCEPT" } when the next call to
@C { KheBackoffAcceptOpportunity } will return @C { true },
and @C { "DECLINE" } when it will return @C { false }.  There is also
@ID {0.95 1.0} @Scale @C {
void KheBackoffDebug(KHE_BACKOFF bk, int verbosity, int indent, FILE *fp);
}
Verbosity 1 prints the current state, including a `@C { ! }' when
the flag is set, on one line.  Verbosity 2 prints some statistics:
the number of opportunities so far, and how many are successful,
failed, and declined, in a multi-line format.
# A function for testing this module appears in @C { khe.h }.
@End @Section

@EndSections
@End @Chapter
