530 lines
26 KiB
TeX
530 lines
26 KiB
TeX
\chapter{Incremental Solving}\label{ch:incremental}
|
|
%************************************************
|
|
|
|
\section{Modelling of Neighbourhoods and Meta-heuristics}
|
|
\label{section:2-modelling-nbhs}
|
|
%
|
|
% Start with a brief review of most common neighbourhoods and then explain:
|
|
% \begin{itemize}
|
|
% \item Random built in with integers
|
|
% \begin{itemize}
|
|
% \item Explain the built in
|
|
% \item Give an example of use (use model)
|
|
% \item Limitations if any
|
|
% \end{itemize}
|
|
|
|
% \item Solution based one
|
|
% \begin{itemize}
|
|
% \item Explain the built in
|
|
% \item Give an example of use (use model)
|
|
% \item Limitations if any
|
|
% \end{itemize}
|
|
% \end{itemize}
|
|
|
|
% End with future work for other built ins (hint which ones would be useful).
|
|
|
|
Most LNS literature discusses neighbourhoods in terms of ``destroying'' part of
|
|
a solution that is later repaired. However, from a declarative modelling point
|
|
of view, it is more natural to see neighbourhoods as adding new constraints and
|
|
variables that need to be applied to the base model, \eg forcing variables to
|
|
take the same value as in the previous solution.
|
|
|
|
This section introduces a \minizinc\ extension that enables modellers to define
|
|
neighbourhoods using the $\mathit{nbh(a)}$ approach described above. This
|
|
extension is based on the constructs introduced in
|
|
\minisearch\~\autocite{rendl-2015-minisearch}, as summarised below.
|
|
|
|
\subsection{LNS in \glsentrytext{minisearch}}
|
|
|
|
\minisearch\ introduced a \minizinc\ extension that enables modellers to express
|
|
meta-searches inside a \minizinc\ model. A meta-search in \minisearch\ typically
|
|
solves a given \minizinc\ model, performs some calculations on the solution, adds
|
|
new constraints and then solves again.
|
|
|
|
An LNS definition in \minisearch\ consists of two parts. The first part is a
|
|
declarative definition of a neighbourhood as a \minizinc\ predicate that posts
|
|
the constraints that should be added with respect to a previous solution. This
|
|
makes use of the \minisearch\ function: \mzninline{function int: sol(var int:
|
|
x)}, which returns the value that variable \mzninline{x} was assigned to in
|
|
the previous solution (similar functions are defined for Boolean, float and set
|
|
variables). In addition, a neighbourhood predicate will typically make use of
|
|
the random number generators available in the \minizinc\ standard library.
|
|
\Cref{lst:6-lns-minisearch-pred} shows a simple random neighbourhood. For each
|
|
decision variable \mzninline{x[i]}, it draws a random number from a uniform
|
|
distribution and, if it exceeds threshold \mzninline{destrRate}, posts
|
|
constraints forcing \mzninline{x[i]} to take the same value as in the previous
|
|
solution. For example, \mzninline{uniformNeighbourhood(x, 0.2)} would result in
|
|
each variable in the array \mzninline{x} having a 20\% chance of being
|
|
unconstrained, and an 80\% chance of being assigned to the value it had in the
|
|
previous solution.
|
|
|
|
\begin{listing}
|
|
\highlightfile{assets/mzn/6_lns_minisearch_pred.mzn}
|
|
\caption{\label{lst:6-lns-minisearch-pred} A simple random LNS predicate
|
|
implemented in \minisearch{}}
|
|
\end{listing}
|
|
|
|
\begin{listing}
|
|
\highlightfile{assets/mzn/6_lns_minisearch.mzn}
|
|
\caption{\label{lst:6-lns-minisearch} A simple LNS metaheuristic implemented
|
|
in \minisearch{}}
|
|
\end{listing}
|
|
|
|
The second part of a \minisearch\ LNS is the meta-search itself. The most basic
|
|
example is that of function \mzninline{lns} in \cref{lst:6-lns-minisearch}. It
|
|
performs a fixed number of iterations, each invoking the neighbourhood predicate
|
|
\mzninline{uniformNeighbourhood} in a fresh scope (so that the constraints only
|
|
affect the current loop iteration). It then searches for a solution
|
|
(\mzninline{minimize_bab}) with a given timeout, and if the search does return a
|
|
new solution, it commits to that solution (so that it becomes available to the
|
|
\mzninline{sol} function in subsequent iterations). The \texttt{lns} function
|
|
also posts the constraint \mzninline{obj < sol(obj)}, ensuring the objective
|
|
value in the next iteration is strictly better than that of the current
|
|
solution.
|
|
|
|
\paragraph{Limitations of the \minisearch\ approach.}
|
|
|
|
Although \minisearch\ enables the modeller to express \emph{neighbourhoods} in a
|
|
declarative way, the definition of the \emph{meta-search} is rather unintuitive
|
|
and difficult to debug, leading to unwieldy code for defining simple restarting
|
|
strategies. Furthermore, the \minisearch\ implementation requires either a close
|
|
integration of the backend solver into the \minisearch\ system, or it drives the
|
|
solver through the regular text-file based \flatzinc\ interface, leading to a
|
|
significant communication overhead.
|
|
|
|
To address these two issues for LNS, we propose to keep modelling neighbourhoods
|
|
as predicates, but define a small number of additional \minizinc\ built-in
|
|
annotations and functions that (a) allow us to express important aspects of the
|
|
meta-search in a more convenient way, and (b) enable a simple compilation scheme
|
|
that requires no additional communication with and only small, simple extensions
|
|
of the backend solver.
|
|
|
|
The approach we follow here is therefore to \textbf{extend \flatzinc}, such that
|
|
the definition of neighbourhoods can be communicated to the solver together with
|
|
the problem instance. This maintains the loose coupling of \minizinc\ and
|
|
solver, while avoiding the costly communication and cold-starting of the
|
|
black-box approach.
|
|
|
|
\subsection{Restart annotations}
|
|
|
|
Instead of the complex \minisearch\ definitions, we propose to add support for
|
|
simple meta-searches that are purely based on the notion of \emph{restarts}. A
|
|
restart happens when a solver abandons its current search efforts, returns to
|
|
the root node of the search tree, and begins a new exploration. Many CP solvers
|
|
already provide support for controlling their restarting behaviour, e.g.\ they
|
|
can periodically restart after a certain number of nodes, or restart for every
|
|
solution. Typically, solvers also support posting additional constraints upon
|
|
restarting (e.g Comet~\autocite{michel-2005-comet}) that are only valid for the
|
|
particular restart (i.e., they are ``retracted'' for the next restart).
|
|
|
|
In its simplest form, we can therefore implement LNS by specifying a
|
|
neighbourhood predicate, and annotating the \mzninline{solve} item to indicate
|
|
the predicate should be invoked upon each restart:
|
|
|
|
\mzninline{solve ::on_restart(myNeighbourhood) minimize cost;}
|
|
|
|
Note that \minizinc\ currently does not support passing functions or predicates
|
|
as arguments. Calling the predicate, as in
|
|
\mzninline{::on_restart(myNeighbourhood())}, would not have the correct
|
|
semantics, since the predicate needs to be called for \emph{each} restart. As a
|
|
workaround, we currently pass the name of the predicate to be called for each
|
|
restart as a string (see the definition of the new \mzninline{on_restart}
|
|
annotation in \cref{lst:6-restart-ann}).
|
|
|
|
The second component of our LNS definition is the \emph{restarting strategy},
|
|
defining how much effort the solver should put into each neighbourhood (i.e.,
|
|
restart), and when to stop the overall search.
|
|
|
|
We propose adding new search annotations to \minizinc\ to control this behaviour
|
|
(see \cref{lst:6-restart-ann}). The \mzninline{restart_on_solution} annotation
|
|
tells the solver to restart immediately for each solution, rather than looking
|
|
for the best one in each restart, while \mzninline{restart_without_objective}
|
|
tells it not to add branch-and-bound constraints on the objective. The other
|
|
\mzninline{restart_X} annotations define different strategies for restarting the
|
|
search when no solution is found. The \mzninline{timeout} annotation gives an
|
|
overall time limit for the search, whereas \mzninline{restart_limit} stops the
|
|
search after a fixed number of restarts.
|
|
|
|
\begin{listing}[t]
|
|
\highlightfile{assets/mzn/6_restart_ann.mzn}
|
|
\caption{\label{lst:6-restart-ann} New annotations to control the restarting
|
|
behaviour}
|
|
\end{listing}
|
|
|
|
\subsection{Neighbourhood selection}
|
|
|
|
It is often beneficial to use several neighbourhood definitions for a problem.
|
|
Different neighbourhoods may be able to improve different aspects of a solution,
|
|
at different phases of the search. Adaptive LNS \autocite{ropke-2006-adaptive,
|
|
pisinger-2007-heuristic}, which keeps track of the neighbourhoods that led to
|
|
improvements and favours them for future iterations, is the prime example for
|
|
this approach. A simpler scheme may apply several neighbourhoods in a
|
|
round-robin fashion.
|
|
|
|
In \minisearch\, adaptive or round-robin approaches can be implemented using
|
|
\emph{state variables}, which support destructive update (overwriting the value
|
|
they store). In this way, the \minisearch\ strategy can store values to be used
|
|
in later iterations. We use the \emph{solver state} instead, i.e., normal
|
|
decision variables, and define two simple built-in functions to access the
|
|
solver state \emph{of the previous restart}. This approach is sufficient for
|
|
expressing neighbourhood selection strategies, and its implementation is much
|
|
simpler.
|
|
|
|
\paragraph{State access and initialisation}
|
|
|
|
The state access functions are defined in \cref{lst:6-state-access}. Function
|
|
\mzninline{status} returns the status of the previous restart, namely:
|
|
\mzninline{START} (there has been no restart yet); \mzninline{UNSAT} (the
|
|
restart failed); \mzninline{SAT} (the restart found a solution); \mzninline{OPT}
|
|
(the restart found and proved an optimal solution); and \mzninline{UNKNOWN} (the
|
|
restart did not fail or find a solution). Function \mzninline{lastval} (which,
|
|
like \mzninline{sol}, has versions for all basic variable types) allows
|
|
modellers to access the last value assigned to a variable (the value is
|
|
undefined if \mzninline{status()=START}).
|
|
|
|
\begin{listing}[t]
|
|
\highlightfile{assets/mzn/6_state_access.mzn}
|
|
\caption{\label{lst:6-state-access} Functions for accessing previous solver
|
|
states}
|
|
\end{listing}
|
|
|
|
In order to be able to initialise the variables used for state access, we
|
|
reinterpret \mzninline{on_restart} so that the predicate is also called for the
|
|
initial search (i.e., before the first ``real'' restart) with the same
|
|
semantics, that is, any constraint posted by the predicate will be retracted for
|
|
the next restart.
|
|
|
|
\paragraph{Parametric neighbourhood selection predicates}
|
|
|
|
We define standard neighbourhood selection strategies as predicates that are
|
|
parametric over the neighbourhoods they should apply. For example, since
|
|
\mzninline{on_restart} now also includes the initial search, we can define a
|
|
strategy \mzninline{basic_lns} that applies a neighbourhood only if the current
|
|
status is not \mzninline{START}:
|
|
|
|
\mzninline{predicate basic_lns(var bool: nbh) = (status()!=START -> nbh);}
|
|
|
|
In order to use this predicate with the \mzninline{on_restart} annotation, we
|
|
cannot simply pass \mzninline{basic_lns(uniformNeighbourhood(x,0.2))}. First of
|
|
all, calling \mzninline{uniformNeighbourhood} like that would result in a
|
|
\emph{single} evaluation of the predicate, since \minizinc\ employs a
|
|
call-by-value evaluation strategy. Furthermore, the \mzninline{on_restart}
|
|
annotation only accepts the name of a nullary predicate. Therefore, users have
|
|
to define their overall strategy in a new predicate. \Cref{lst:6-basic-complete}
|
|
shows a complete example of a basic LNS model.
|
|
|
|
\begin{listing}[t]
|
|
\highlightfile{assets/mzn/6_basic_complete.mzn}
|
|
\caption{\label{lst:6-basic-complete} Complete LNS example}
|
|
\end{listing}
|
|
|
|
We can also define round-robin and adaptive strategies using these primitives.
|
|
%\paragraph{Round-robin LNS}
|
|
\Cref{lst:6-round-robin} defines a round-robin LNS meta-heuristic, which cycles
|
|
through a list of \mzninline{N} neighbourhoods \mzninline{nbhs}. To do this, it
|
|
uses the decision variable \mzninline{select}. In the initialisation phase
|
|
(\mzninline{status()=START}), \mzninline{select} is set to \mzninline{-1}, which
|
|
means none of the neighbourhoods is activated. In any following restart,
|
|
\mzninline{select} is incremented modulo \mzninline{N}, by accessing the last
|
|
value assigned in a previous restart (\mzninline{lastval(select)}). This will
|
|
activate a different neighbourhood for each restart
|
|
(\lref{line:6:roundrobin:post}).
|
|
|
|
\begin{listing}[t]
|
|
\highlightfile{assets/mzn/6_round_robin.mzn}
|
|
\caption{\label{lst:6-round-robin} A predicate providing the round robin
|
|
meta-heuristic}
|
|
\end{listing}
|
|
|
|
%\paragraph{Adaptive LNS}
|
|
For adaptive LNS, a simple strategy is to change the size of the neighbourhood
|
|
depending on whether the previous size was successful or not.
|
|
\Cref{lst:6-adaptive} shows an adaptive version of the
|
|
\mzninline{uniformNeighbourhood} that increases the number of free variables
|
|
when the previous restart failed, and decreases it when it succeeded, within the
|
|
bounds $[0.6,0.95]$.
|
|
|
|
\begin{listing}[t]
|
|
\highlightfile{assets/mzn/6_adaptive.mzn}
|
|
\caption{\label{lst:6-adaptive} A simple adaptive neighbourhood}
|
|
\end{listing}
|
|
|
|
\subsection{Meta-heuristics}
|
|
|
|
The LNS strategies we have seen so far rely on the default behaviour of
|
|
\minizinc\ solvers to use branch-and-bound for optimisation: when a new solution
|
|
is found, the solver adds a constraint to the remainder of the search to only
|
|
accept better solutions, as defined by the objective function in the
|
|
\mzninline{minimize} or \mzninline{maximize} clause of the \mzninline{solve}
|
|
item. When combined with restarts and LNS, this is equivalent to a simple
|
|
hill-climbing meta-heuristic.
|
|
|
|
We can use the constructs introduced above to implement alternative
|
|
meta-heuristics such as simulated annealing. In particular, we use
|
|
\mzninline{restart_without_objective} to tell the solver not to add the
|
|
branch-and-bound constraint on restart. It will still use the declared objective
|
|
to decide whether a new solution is the globally best one seen so far, and only
|
|
output those (to maintain the convention of \minizinc\ solvers that the last
|
|
solution printed at any point in time is the currently best known one).
|
|
%
|
|
With \mzninline{restart_without_objective}, the restart predicate is now
|
|
responsible for constraining the objective function. Note that a simple
|
|
hill-climbing (for minimisation) can still be defined easily in this context as:
|
|
|
|
{
|
|
\centering
|
|
\scriptsize
|
|
\highlightfile{assets/mzn/6_hill_climbing.mzn}
|
|
}
|
|
|
|
It takes advantage of the fact that the declared objective function is available
|
|
through the built-in variable \mzninline{_objective}.
|
|
%
|
|
A simulated annealing strategy is also easy to
|
|
express:
|
|
|
|
{
|
|
\centering
|
|
\scriptsize
|
|
\highlightfile{assets/mzn/6_simulated_annealing.mzn}
|
|
}
|
|
|
|
\section{Compilation of Neighbourhoods} \label{section:compilation}
|
|
|
|
The neighbourhoods defined in the previous section can be executed with
|
|
\minisearch\ by adding support for the \mzninline{status} and
|
|
\mzninline{lastval} built-in functions, and by defining the main restart loop.
|
|
The \minisearch{} evaluator will then call a solver to produce a solution, and
|
|
evaluate the neighbourhood predicate, incrementally producing new \flatzinc\ to
|
|
be added to the next round of solving.
|
|
%
|
|
While this is a viable approach, our goal is to keep the compiler and solver
|
|
separate, by embedding the entire LNS specification into the \flatzinc\ that is
|
|
passed to the solver.
|
|
%
|
|
This section introduces such a compilation approach. It only requires simple
|
|
modifications of the \minizinc\ compiler, and the compiled \flatzinc\ can be
|
|
executed by standard CP solvers with a small set of simple extensions.
|
|
|
|
\subsection{Compilation overview}
|
|
|
|
The neighbourhood definitions from the previous section have an important
|
|
property that makes them easy to compile to standard \flatzinc: they are defined
|
|
in terms of standard \minizinc\ expressions, with the exception of a few new
|
|
built-in functions. When the neighbourhood predicates are evaluated in the
|
|
\minisearch\ way, the \minisearch\ runtime implements those built-in functions,
|
|
computing the correct value whenever a predicate is evaluated.
|
|
%
|
|
Instead, the compilation scheme presented below uses a limited form of
|
|
\emph{partial evaluation}: parameters known at compile time will be fully
|
|
evaluated; those only known during the solving, such as the result of a call to
|
|
any of the new functions (\mzninline{sol}, \mzninline{status}, etc.), are
|
|
replaced by decision variables. This essentially \textbf{turns the new built-in
|
|
functions into constraints} that have to be supported by the target solver.
|
|
The neighbourhood predicate can then be added as a constraint to the model. The
|
|
evaluation is performed by hijacking the solver's own capabilities: It will
|
|
automatically perform the evaluation of the new functions by propagating the new
|
|
constraints.
|
|
|
|
To compile an LNS specification to standard \flatzinc, the \minizinc\ compiler
|
|
performs four simple steps:
|
|
|
|
\begin{enumerate}
|
|
\item Replace the annotation \mzninline{::on_restart("X")} with a call to
|
|
predicate \mzninline{X}.
|
|
\item Inside predicate \mzninline{X} and any other predicate called
|
|
recursively from \mzninline{X}: treat any call to built-in functions
|
|
\mzninline{sol}, \mzninline{status}, and \mzninline{lastval} as
|
|
returning a \mzninline{var} instead of a \mzninline{par} value; and
|
|
rename calls to random functions, e.g., \mzninline{uniform} to
|
|
\mzninline{uniform_nbh}, in order to distinguish them from their
|
|
standard library versions.
|
|
\item Convert any expression containing a call from step 2 to \mzninline{var}
|
|
to ensure the functions are compiled as constraints, rather than
|
|
statically evaluated by the \minizinc\ compiler.
|
|
\item Compile the resulting model using an extension of the \minizinc\
|
|
standard library that provides declarations for these built-in
|
|
functions, as defined below.
|
|
\end{enumerate}
|
|
|
|
These transformations will not change the code of many neighbourhood
|
|
definitions, since the built-in functions are often used in positions that
|
|
accept both parameters and variables. For example, the
|
|
\mzninline{uniformNeighbourhood} predicate from \cref{lst:6-lns-minisearch-pred}
|
|
uses \mzninline{uniform(0.0,1.0)} in an \mzninline{if} expression, and
|
|
\mzninline{sol(x[i])} in an equality constraint. Both expressions can be
|
|
translated to \flatzinc\ when the functions return a \mzninline{var}.
|
|
|
|
\subsection{Compiling the new built-ins}
|
|
|
|
We can compile models that contain the new built-ins by extending the \minizinc\
|
|
standard library as follows.
|
|
|
|
\paragraph{\mzninline{status}}
|
|
|
|
\Cref{lst:6-status} shows the definition of the \mzninline{status} function. It
|
|
simply replaces the functional form by a predicate \mzninline{status} (declared
|
|
in \lref{line:6:status}), which constrains its local variable argument
|
|
\mzninline{stat} to take the status value.
|
|
|
|
\begin{listing}[t]
|
|
\highlightfile{assets/mzn/6_status.mzn}
|
|
\caption{\label{lst:6-status} MiniZinc definition of the \mzninline{status} function}
|
|
\end{listing}
|
|
|
|
|
|
\paragraph{\mzninline{sol} and \mzninline{lastval}}
|
|
|
|
Since \mzninline{sol} is overloaded for different variable types and \flatzinc\
|
|
does not support overloading, we produce type-specific built-ins for every type
|
|
of solver variable (\mzninline{int_sol(x, xi)}, \mzninline{bool_sol(x, xi)},
|
|
etc.). The resolving of the \mzninline{sol} function into these specific
|
|
built-ins is done using an overloaded definition like the one shown
|
|
in~\Cref{lst:6-int-sol} for integer variables. If the value of the variable in
|
|
question becomes known at compile time, we use that value instead. Otherwise, we
|
|
replace the function call with a type specific \mzninline{int_sol} predicate,
|
|
which is the constraint that will be executed by the solver.
|
|
%
|
|
\begin{listing}[t]
|
|
\centering
|
|
% \begin{mzn}
|
|
% predicate int_sol(var int: x, var int: xi);
|
|
% function int: sol(var int: x) = if is_fixed(x) then fix(x)
|
|
% else let { var lb(x)..ub(x): xi;
|
|
% constraint int_sol(x,xi);
|
|
% } in xi;
|
|
% endif;
|
|
% \end{mzn}
|
|
\caption{\label{lst:6-int-sol} MiniZinc definition of the \mzninline{sol}
|
|
function for integer variables}
|
|
\end{listing}
|
|
%
|
|
To improve the compilation of the model further, we use the declared bounds of
|
|
the argument (\mzninline{lb(x)..ub(x)}) to constrain the variable returned by
|
|
\mzninline{sol}. This bounds information is important for the compiler to be
|
|
able to generate the most efficient \flatzinc\ code for expressions involving
|
|
\mzninline{sol}. The compilation of \mzninline{lastval} is similar to that for
|
|
\mzninline{sol}.
|
|
|
|
\paragraph{Random number functions}
|
|
|
|
Calls to the random number functions have been renamed by appending
|
|
\texttt{\_nbh}, so that the compiler does not simply evaluate them statically.
|
|
The definition of these new functions follows the same pattern as for
|
|
\mzninline{sol}, \mzninline{status}, and \mzninline{lastval}. The MiniZinc
|
|
definition of the \mzninline{uniform_nbh} function is shown in
|
|
\Cref{lst:6-int-rnd}.%
|
|
\footnote{Random number functions need to be marked as \mzninline{::impure} for
|
|
the compiler not to apply Common Subexpression Elimination
|
|
(CSE)~\autocite{stuckey-2013-functions} if they are called multiple times with
|
|
the same arguments.}%
|
|
Note that the function accepts variable arguments \mzninline{l} and
|
|
\mzninline{u}, so that it can be used in combination with other functions, such
|
|
as \mzninline{sol}.
|
|
|
|
\begin{listing}[t]
|
|
\centering
|
|
% \begin{mzn}
|
|
% predicate float_uniform(var float:l, var float: u, var float: r);
|
|
% function var float: uniform_nbh(var float: l, var float: u) :: impure =
|
|
% let { var lb(l)..ub(u): rnd;
|
|
% constraint float_uniform(l,u,rnd):
|
|
% } in rnd;
|
|
% \end{mzn}
|
|
\caption{\label{lst:6-int-rnd} MiniZinc definition of the
|
|
\mzninline{uniform_nbh} function for floats}
|
|
\end{listing}
|
|
|
|
|
|
\subsection{Solver support for LNS \glsentrytext{flatzinc}}
|
|
|
|
We will now show the minimal extensions required from a solver to interpret the
|
|
new \flatzinc\ constraints and, consequently, to execute LNS definitions
|
|
expressed in \minizinc.
|
|
|
|
First, the solver needs to parse and support the restart annotations
|
|
of~\cref{lst:6-restart-ann}. Many solvers already support all this
|
|
functionality. Second, the solver needs to be able to parse the new constraints
|
|
\mzninline{status}, and all versions of \mzninline{sol}, \mzninline{lastval},
|
|
and random number functions like \mzninline{float_uniform}. In addition, for the
|
|
new constraints the solver needs to:
|
|
\begin{itemize}
|
|
\item \mzninline{status(s)}: record the status of the previous restart, and
|
|
fix \mzninline{s} to the recorded status.
|
|
\item \mzninline{sol(x,sx)} (variants): constrain \mzninline{sx} to be equal
|
|
to the value of \mzninline{x} in the incumbent solution. If there is no
|
|
incumbent solution, it has no effect.
|
|
\item \mzninline{lastval(x,lx)} (variants): constrain \mzninline{lx} to take
|
|
the last value assigned to \mzninline{x} during search. If no value was
|
|
ever assigned, it has no effect. Note that many solvers (in particular
|
|
SAT and LCG solvers) already track \mzninline{lastval} for their
|
|
variables for use in search. To support LNS a solver must at least track
|
|
the \emph{lastval} of each of the variables involved in such a
|
|
constraint. This is straightforward by using the \mzninline{lastval}
|
|
propagator itself. It wakes up whenever the first argument is fixed, and
|
|
updates the last value (a non-backtrackable value).
|
|
\item random number functions: fix their variable argument to a random number
|
|
in the appropriate probability distribution.
|
|
\end{itemize}
|
|
|
|
Importantly, these constraints need to be propagated in a way that their effects
|
|
can be undone for the next restart. Typically, this means the solver must not
|
|
propagate these constraints in the root node of the search.
|
|
|
|
Modifying a solver to support this functionality is straightforward if it
|
|
already has a mechanism for posting constraints during restarts. We have
|
|
implemented these extensions for both Gecode (110 new lines of code) and Chuffed
|
|
(126 new lines of code).
|
|
|
|
For example, consider the model from \cref{lst:6-basic-complete} again.
|
|
\Cref{lst:6-flat-pred} shows a part of the \flatzinc\ that arises from compiling
|
|
\mzninline{basic_lns(uniformNeighbourhood(x, 0.2))}, assuming that
|
|
\mzninline{index_set(x) = 1..n}.
|
|
|
|
\Lrefrange{line:6:status:start}{line:6:status:end} define a Boolean variable
|
|
\mzninline{b1} that is true iff the status is not \mzninline{START}. The second
|
|
block of code (\lrefrange{line:6:x1:start}{line:6:x1:end}) represents the
|
|
decomposition of the expression
|
|
|
|
\mzninline{(status() != START /\ uniform(0.0,1.0) > 0.2) -> x[1] = sol(x[1])}
|
|
%
|
|
which is the result of merging the implication from the \mzninline{basic_lns}
|
|
predicate with the \mzninline{if} expression from
|
|
\mzninline{uniformNeighbourhood}. The code first introduces and constrains a
|
|
variable for the random number, then adds two Boolean variables: \mzninline{b2}
|
|
is constrained to be true iff the random number is greater than 0.2; while
|
|
\mzninline{b3} is constrained to be the conjunction \mzninline{status()!=START
|
|
/\ uniform(0.0,1.0)>0.2}. \lref{line:6:x1} constrains \mzninline{x1} to be the
|
|
value of \mzninline{x[1]} in the previous solution. Finally, the half-reified
|
|
constraint in \lref{line:6:x1:end} implements \mzninline{b3 -> x[1]=sol(x[1])}.
|
|
We have omitted the similar code generated for \mzninline{x[2]} to
|
|
\mzninline{x[n]}. Note that the \flatzinc\ shown here has been simplified for
|
|
presentation.
|
|
|
|
\begin{listing}[t]
|
|
\highlightfile{assets/mzn/6_basic_complete_transformed.mzn}
|
|
\caption{\label{lst:6-flat-pred} \flatzinc\ that results from compiling \\
|
|
\mzninline{basic_lns(uniformNeighbourhood(x,0.2))}.}
|
|
\end{listing}
|
|
|
|
The first time the solver is invoked, it sets \mzninline{s} to 1
|
|
(\mzninline{START}). Propagation will fix \mzninline{b1} to \mzninline{false}
|
|
and \mzninline{b3} to \mzninline{false}. Therefore, the implication in
|
|
\lref{line:6:x1:end} is not activated, leaving \mzninline{x[1]} unconstrained.
|
|
The neighbourhood constraints are effectively switched off.
|
|
|
|
When the solver restarts, all of the special propagators are re-executed.
|
|
Now \mzninline{s} is not 1, and \mzninline{b1} will be set to
|
|
\mzninline{true}. The \mzninline{float_random} propagator assigns
|
|
\mzninline{rnd1} a new random value and, depending on whether it is greater
|
|
than \mzninline{0.2}, the Boolean variables \mzninline{b2}, and consequently
|
|
\mzninline{b3} will be assigned. If it is \mzninline{true}, the constraint
|
|
in line \lref{line:6:x1:end} will become active and assign \mzninline{x[1]}
|
|
to its value in the previous solution.
|
|
|
|
Furthermore, it is not strictly necessary to guard \mzninline{int_uniform}
|
|
against being invoked before \mzninline{status()!=START}, since the
|
|
\mzninline{sol} constraints will simply not propagate anything in case no
|
|
solution has been recorded yet, but we use this simple example to illustrate
|
|
how these Boolean conditions are compiled and evaluated.
|