Remaining questions after Guido's notes
This commit is contained in:
parent
b63274965f
commit
91d264597c
@ -4,7 +4,7 @@ var int: cost; % objective function
|
||||
% --- some constraints defining the problem ---
|
||||
|
||||
% The user-defined LNS strategy
|
||||
predicate my_lns() = basic_lns(uniform_neighbourhood(x, 0.2));
|
||||
predicate my_lns() = basic_lns(uniform_neighbourhood(x, 0.2));@\Vlabel{line:inc:blns:strat}@
|
||||
|
||||
% Solve using my_lns, restart every 500 nodes, overall timeout 120 seconds
|
||||
solve ::on_restart("my_lns") ::restart_constant(500) ::timeout(120)
|
||||
|
@ -3,6 +3,8 @@ predicate simulated_annealing(float: init_temp, float: cooling_rate) =
|
||||
var float: temp;
|
||||
} in if status() = START then
|
||||
temp = init_temp
|
||||
elseif status() = UNSAT then
|
||||
complete()
|
||||
else
|
||||
temp = last_val(temp) * (1 - cooling_rate) % cool down
|
||||
/\ _objective < sol(_objective) - ceil(log(uniform(0.0, 1.0)) * temp)
|
||||
|
@ -168,28 +168,42 @@ Function \mzninline{status} returns the status of the previous restart, namely:
|
||||
\item[\mzninline{UNKNOWN}] the last \gls{restart} did not find a \gls{sol}, but did not exhaust its \gls{search-space}.
|
||||
\end{description}
|
||||
|
||||
\noindent{}Function \mzninline{last_val} allows modellers to access the last value assigned to a \variable{}.
|
||||
If \mzninline{status()=SAT \/ status{}=OPT}, then \mzninline{last_val(x)} and \mzninline{sol(x)} return the same value.
|
||||
However, if \mzninline{status()=UNSAT \/ status{}=UNKNOWN}, but \mzninline{x} was still (at some point) assigned during search, then that value will be returned by \mzninline{last_val(x)}, whereas \mzninline{sol(x)} will still return the value from the last \gls{sol}.
|
||||
When \mzninline{x} is assigned at the root node of the search, it has the same value until the next restart.
|
||||
This mechanism allows us to use \variables{} as state variables.
|
||||
|
||||
The value is undefined when \mzninline{status()=START}.
|
||||
Like \mzninline{sol}, it has versions for all basic \variable{} types.
|
||||
|
||||
\begin{listing}
|
||||
\mznfile{assets/listing/inc_state_access.mzn}
|
||||
\caption{\label{lst:inc-state-access} Functions for accessing previous \solver{} states.}
|
||||
\end{listing}
|
||||
|
||||
The function \mzninline{last_val} allows modellers to access the last value assigned to a \variable{}.
|
||||
Like \mzninline{sol}, it has versions for all basic \variable{} types.
|
||||
If \mzninline{status()=SAT \/ status{}=OPT}, then \mzninline{last_val(x)} and \mzninline{sol(x)} return the same value.
|
||||
However, if \mzninline{status()=UNSAT \/ status{}=UNKNOWN}, but \mzninline{x} was still (at some point) assigned during search, then that value will be returned by \mzninline{last_val(x)}, whereas \mzninline{sol(x)} will still return the value from the last \gls{sol}.
|
||||
The value is undefined when \mzninline{status()=START} or when \mzninline{x} was not present during the last \gls{restart}.
|
||||
|
||||
If a \variable{} \mzninline{x} is assigned immediately after each restart (before any \glspl{search-decision}), then it is guaranteed that \mzninline{last_val(x)} will return this value after the next restart.
|
||||
This mechanism allows us to use \variables{} similar fashion to \minisearch{}'s state variables.
|
||||
|
||||
In order to be able to initialize the \variables{} used for state access, we interpret \mzninline{on_restart} also before the initial search using the same semantics.
|
||||
As such, the predicate is also called before the first ``real'' \gls{restart}, but any \constraint{} posted by the predicate will be retracted for the next \gls{restart}.
|
||||
|
||||
Of particular interest for the behaviour of \mzninline{last_val} and \mzninline{sol} is the manner in which we identify a \variable{}.
|
||||
We have defined the behaviour of the \mzninline{on_restart} \gls{annotation} as repeatedly calling the function argument on every restart.
|
||||
This means that if the call introduces and \glspl{avar}, then it will create unique \glspl{avar} for every restart.
|
||||
It could thus be said that \mzninline{last_val} and \mzninline{sol} cannot be used on these \glspl{avar}.
|
||||
However, it is often natural to introduce state variables used within our restart strategy, as we shall soon see.
|
||||
Therefore, we identify each \variable{} using their variable path \autocite{leo-2015-multipass}.
|
||||
The path of a \variable{} is designed to give a unique identifier to a \variable{} when an \instance{} is rewritten multiple times.
|
||||
The \variable{} path is mostly dependent on the \parameter{} arguments to the calls and the value of iterators for which the \variable{} is introduced.
|
||||
As such, this mechanism will between \glspl{restart} correctly identify the \variables{} for each call to the nullary function in the \mzninline{on_restart} \gls{annotation}.
|
||||
However, it might lead to slightly unexpected behaviour when using \parameter{} function arguments that do not relate to the \mzninline{avar} introduced in the function.
|
||||
|
||||
\paragraph{Parametric neighbourhood selection predicates}
|
||||
|
||||
We define standard \gls{neighbourhood} selection strategies as predicates that are parametric over the \glspl{neighbourhood} they should apply.
|
||||
Since there are many well-known ways to makes a selection of a set of \glspl{neighbourhood}, we define standard \gls{neighbourhood} selection strategies as predicates that are parametric over the \glspl{neighbourhood} they should apply.
|
||||
For example, we can define a strategy \mzninline{basic_lns} that applies a \gls{lns} \gls{neighbourhood}.
|
||||
Since \mzninline{on_restart} now also includes the initial search, we apply the \gls{neighbourhood} only if the current status is not \mzninline{START}, as shown in the following predicate.
|
||||
Since \mzninline{on_restart} now also includes the initial search, we apply the \gls{neighbourhood} only if the current status is not \mzninline{START}.
|
||||
However, we again face the problem that in \minizinc{} we cannot pass functions or predicates as arguments.
|
||||
We can however define these predicates to take Boolean \variables{}, the result of evaluating a predicate, as their arguments.
|
||||
Using this technique, the \mzninline{basic_lns} predicate can be defined as follows.
|
||||
|
||||
\begin{mzn}
|
||||
predicate basic_lns(var bool: nbh) =
|
||||
@ -198,40 +212,39 @@ Since \mzninline{on_restart} now also includes the initial search, we apply the
|
||||
endif;
|
||||
\end{mzn}
|
||||
|
||||
In order to use this predicate with the \mzninline{on_restart} \gls{annotation}, we cannot simply call a \gls{neighbourhood} predicate as follows.
|
||||
Still, in order to use this predicate with the \mzninline{on_restart} \gls{annotation}, we cannot simply call a \gls{neighbourhood} predicate as follows.
|
||||
|
||||
\begin{mzn}
|
||||
basic_lns(uniform_neighbourhood(x, 0.2))
|
||||
\end{mzn}
|
||||
|
||||
\noindent{}Calling \mzninline{uniform_neighbourhood} like this would result in a single evaluation of the predicate, since \minizinc{} employs a call-by-value evaluation strategy.
|
||||
Furthermore, the \mzninline{on_restart} \gls{annotation} only accepts the name of a nullary predicate.
|
||||
Therefore, users have to define their overall strategy in a new predicate.
|
||||
\noindent{}Calling \mzninline{uniform_neighbourhood} like this would result in a single evaluation of the predicate.
|
||||
Rather, the modeller has to define their overall strategy in a new nullary predicate.
|
||||
This ensures that both the selection strategy and the \glspl{neighbourhood} are evaluated on every restart.
|
||||
\Cref{lst:inc-basic-complete} shows a complete example of a basic \gls{lns} model.
|
||||
\Lref{line:inc:blns:strat} shows the predicate defined to capture the overall strategy.
|
||||
|
||||
\begin{listing}
|
||||
\begin{listing}[p]
|
||||
\mznfile{assets/listing/inc_basic_complete.mzn}
|
||||
\caption{\label{lst:inc-basic-complete} A complete \gls{lns} example.}
|
||||
\end{listing}
|
||||
|
||||
We can also define round-robin and adaptive strategies using these primitives.
|
||||
\Cref{lst:inc-round-robin} defines a round-robin \gls{lns} meta-heuristic, which cycles through a list of \mzninline{N} neighbourhoods \mzninline{nbhs}.
|
||||
\Cref{lst:inc-round-robin} defines a round-robin \gls{lns} selection strategy, which cycles through a list of \mzninline{N} neighbourhoods \mzninline{nbhs}.
|
||||
To do this, it uses the \variable{} \mzninline{select}.
|
||||
In the initialization phase (\mzninline{status()=START}), \mzninline{select} is set to \mzninline{-1}, which means none of the \glspl{neighbourhood} is activated.
|
||||
In any following \gls{restart}, \mzninline{select} is incremented modulo \mzninline{N}, by accessing the last value assigned in a previous \gls{restart}.
|
||||
This will activate a different \gls{neighbourhood} for each subsequent \gls{restart} (\lref{line:6:roundrobin:post}).
|
||||
|
||||
\begin{listing}
|
||||
\begin{listing}[p]
|
||||
\mznfile[l]{assets/listing/inc_round_robin.mzn}
|
||||
\caption{\label{lst:inc-round-robin} A predicate providing the round-robin meta-heuristic.}
|
||||
\caption{\label{lst:inc-round-robin} A predicate providing the round-robin selection strategy.}
|
||||
\end{listing}
|
||||
|
||||
%\paragraph{Adaptive \gls{lns}}
|
||||
|
||||
For adaptive \gls{lns}, a simple strategy is to change the size of the \gls{neighbourhood} depending on whether the previous size was successful or not.
|
||||
\Cref{lst:inc-adaptive} shows an adaptive version of the \mzninline{uniform_neighbourhood} that increases the number of free \variables{} when the previous \gls{restart} failed, and decreases it when it succeeded, within the range \([0.6,0.95]\).
|
||||
|
||||
\begin{listing}
|
||||
\begin{listing}[p]
|
||||
\mznfile{assets/listing/inc_adaptive.mzn}
|
||||
\caption{\label{lst:inc-adaptive}A simple adaptive neighbourhood.}
|
||||
\end{listing}
|
||||
@ -239,36 +252,16 @@ For adaptive \gls{lns}, a simple strategy is to change the size of the \gls{neig
|
||||
\subsection{Optimization strategies}
|
||||
|
||||
The \gls{meta-optimization} algorithms we have seen so far rely on the default behaviour of \minizinc{} \solvers{} to use \gls{bnb} for optimization: when a new \gls{sol} is found, the \solver{} adds a \constraint{} to the remainder of the search to only accept better \glspl{sol}, as defined by the \gls{objective} in the \mzninline{minimize} or \mzninline{maximize} clause of the \mzninline{solve} item.
|
||||
When combined with \glspl{restart} and \gls{lns}, this is equivalent to a simple hill-climbing meta-heuristic.
|
||||
When combined with \glspl{restart} and \gls{lns}, this is equivalent to a simple hill-climbing search method.
|
||||
|
||||
We can use the constructs introduced above to implement alternative meta-heuristics such as simulated annealing.
|
||||
We can use the constructs introduced above to implement alternative search method such as simulated annealing.
|
||||
We use the \mzninline{restart_without_objective} \gls{annotation}, in particular, to tell the solver not to add the \gls{bnb} \constraint{} on \gls{restart}.
|
||||
It will still use the declared \gls{objective} to decide whether a new \gls{sol} is globally the best one seen so far, and only output those.
|
||||
This maintains the convention of \minizinc{} \solvers{} that the last \gls{sol} printed at any point in time is the currently best known one.
|
||||
|
||||
With \mzninline{restart_without_objective}, the \gls{restart} predicate is now responsible for constraining the \gls{objective}.
|
||||
Note that a simple hill-climbing (for minimization) can still be defined easily in this context as follows.
|
||||
|
||||
\begin{mzn}
|
||||
predicate hill_climbing() = status() != START -> _objective < sol(_objective);
|
||||
\end{mzn}
|
||||
|
||||
It takes advantage of the fact that the declared \gls{objective} is available through the built-in \variable{} \mzninline{_objective}.
|
||||
A more interesting example is a simulated annealing strategy.
|
||||
When using this strategy, the sequence of \glspl{sol} that the \solver{} finds are not required to steadily improve in quality.
|
||||
Instead, we ask the \solver{} to find a \gls{sol} that is a significant improvement over the previous \gls{sol}.
|
||||
Over time, we decrease the amount by which we require the \gls{sol} needs to improve until we are just looking for any improvements.
|
||||
This \gls{meta-optimization} can help improve the qualities of \gls{sol} quickly and thereby reaching the \gls{opt-sol} quicker.
|
||||
\Cref{lst:inc-sim-ann} show how this strategy is also easy to express using \gls{rbmo}.
|
||||
|
||||
\begin{listing}
|
||||
\mznfile{assets/listing/inc_sim_ann.mzn}
|
||||
\caption{\label{lst:inc-sim-ann}A predicate implementing a simulated annealing search.}
|
||||
\end{listing}
|
||||
|
||||
So far, the algorithms used have been for versions of incomplete search, or we have trusted the \solver{} to know when to stop searching.
|
||||
However, for the following algorithms the \solver{} will (or should) not be able to determine whether the search is complete.
|
||||
Instead, we introduce the following function that can be used to signal to the solver that the search is complete.
|
||||
When using \mzninline{restart_without_objective} the \solver{} is no longer able to decide when the search process is finished.
|
||||
However, when \gls{rbmo} is used to describe a complete search, we still want to stop the \solver{} when the process is known to be finished.
|
||||
Therefore, we introduce the following function that can be used to signal to the solver that the search is complete.
|
||||
|
||||
\begin{mzn}
|
||||
function var bool: complete();
|
||||
@ -277,6 +270,31 @@ Instead, we introduce the following function that can be used to signal to the s
|
||||
\noindent{}When the \variable{} returned by this function \gls{fixed} to \true{}, then the search is complete.
|
||||
If any \gls{sol} was found, it is declared an \gls{opt-sol}.
|
||||
|
||||
With \mzninline{restart_without_objective}, the \gls{restart} predicate is now responsible for constraining the \gls{objective}.
|
||||
Note that a simple hill-climbing (for minimization) can still be defined easily in this context as follows.
|
||||
|
||||
\begin{mzn}
|
||||
predicate hill_climbing() =
|
||||
if status() != START then
|
||||
_objective < sol(_objective);
|
||||
elseif status() = UNSAT then
|
||||
complete();
|
||||
endif;
|
||||
\end{mzn}
|
||||
|
||||
It takes advantage of the fact that the declared \gls{objective} is available through the built-in \variable{} \mzninline{_objective}.
|
||||
A more interesting example is a simulated annealing strategy.
|
||||
When using this strategy, the sequence of \glspl{sol} that the \solver{} finds are not required to steadily improve in quality.
|
||||
Instead, the \solver{} can produce \glspl{sol} that potentially with a potentially reduced objective value.
|
||||
Over time, we decrease the amount by which a \gls{sol} might decrease objective value until we are only looking for improvements.
|
||||
This \gls{meta-optimization} can help the \solver{} to quickly approximate the \gls{opt-sol}.
|
||||
\Cref{lst:inc-sim-ann} show how this strategy is also easy to express using \gls{rbmo}.
|
||||
|
||||
\begin{listing}
|
||||
\mznfile{assets/listing/inc_sim_ann.mzn}
|
||||
\caption{\label{lst:inc-sim-ann}A predicate implementing a simulated annealing search.}
|
||||
\end{listing}
|
||||
|
||||
Using the same methods it is also possible to describe optimization strategies with multiple \glspl{objective}.
|
||||
An example of such a strategy is lexicographic search.
|
||||
Lexicographic search can be employed when there is a strict order between the importance of different \glspl{objective}.
|
||||
@ -322,7 +340,6 @@ It should be noted that the \glspl{sol} found by this search will still contain
|
||||
|
||||
The \glspl{neighbourhood} defined in the previous section can be executed with \minisearch{} by adding support for the \mzninline{status} and \mzninline{last_val} built-in functions, and by defining the main \gls{restart} loop.
|
||||
The \minisearch{} evaluator will then call a \solver{} to produce a \gls{sol}, and evaluate the \gls{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 \gls{rewriting} and solving separate, by embedding the entire \gls{meta-optimization} algorithm into the \gls{slv-mod}.
|
||||
|
||||
This section introduces such a \gls{rewriting} approach.
|
||||
|
Reference in New Issue
Block a user