Small introduction and spell checking

This commit is contained in:
Jip J. Dekker 2021-04-26 18:06:13 +10:00
parent 87ff8426f3
commit 1ab2442c8d
No known key found for this signature in database
GPG Key ID: 517DF4A00618C9C3
5 changed files with 201 additions and 146 deletions

View File

@ -4,6 +4,7 @@
\newacronym[see={[Glossary:]{gls-cse}}]{cse}{CSE\glsadd{gls-cse}}{Common Subexpression Elimination}
\newacronym[see={[Glossary:]{gls-csp}}]{csp}{CSP\glsadd{gls-csp}}{Constraint Satisfaction Problem}
\newacronym[see={[Glossary:]{gls-cop}}]{cop}{COP\glsadd{gls-cop}}{Constraint Optimisation Problem}
\newacronym[see={[Glossary:]{gls-lcg}}]{lcg}{LCG\glsadd{gls-lcg}}{Lazy Clause Generation}
\newacronym[see={[Glossary:]{gls-lns}}]{lns}{LNS\glsadd{gls-lns}}{Large Neighbourhood Search}
\newacronym[see={[Glossary:]{gls-mip}}]{mip}{MIP\glsadd{gls-mip}}{Mixed Integer Programming}
\newacronym[see={[Glossary:]{gls-sat}}]{sat}{SAT\glsadd{gls-sat}}{Boolean Satisfiability}

View File

@ -24,6 +24,11 @@
description={},
}
\newglossaryentry{chuffed}{
name={Chuffed},
description={},
}
\newglossaryentry{comprehension}{
name={comprehension},
description={},
@ -79,6 +84,10 @@
description={},
}
\newglossaryentry{gecode}{
name={Gecode},
description={},
}
\newglossaryentry{generator}{
name={generator},
description={},
@ -104,6 +113,11 @@
description={},
}
\newglossaryentry{gls-lcg}{
name={lazy clause generation},
description={},
}
\newglossaryentry{gls-lns}{
name={large neighbourhood search},
description={},

View File

@ -555,9 +555,48 @@ expressions in the \gls{solver} model.
\section{The Current \glsentrytext{minizinc} Interpreter}%
\label{sec:back-mzn-interpreter}
For version 2.5.5 of the \minizinc\ bundle, the \texttt{minizinc} executable is
officially provided tool to solve \minizinc\ instances. A modeller provides the
\texttt{minizinc} executable with a \minizinc\ model, the ground data required
to instantiate the model, and a \gls{solver} definition. Primarily the
\gls{solver} definition defines the \minizinc\ library used to flatten the
\minizinc\ instance and the way in which the \gls{solver} is to be executed. The
process of the \texttt{minizinc} executable can be categorised into the
following stages:
\begin{description}
\item[Parsing] \texttt{minizinc} parses the input data, the \minizinc\ model,
and the \gls{solver} library.
\item[Type checking] \texttt{minizinc} ensures the type consistency of the
\minizinc\ model, making sure that the types of all expressions match their
declarations and is allowed in the locations where they are used. \\ In the
process of type checking the model, all identifiers and calls are connected to
the declaration that they refer to.
\item[Flattening] \jip{TODO: This should have something}
\item[Optimisation] Given the generated \flatzinc{} model, \texttt{minizinc}
will try optimise this model to try and reduce the number of
\glspl{constraint} and size of the \glspl{domain} of \glspl{variable} in the
\flatzinc\ model.
\item[Solving] The optimised \flatzinc\ model is given to the \gls{solver}.
Any solutions found by the \gls{solver} are communicated back to the user.
\end{description}
\jip{TODO: Description of the flattening process}
\jip{TODO: Description of the techniques used during the optimisation phase}
\section{Other Constraint Modelling Languages}%
\label{sec:back-other-languages}
\subsection{AMPL}%
\label{sub:back-ampl}
\subsection{OPL}%
\label{sub:back-}
\subsection{Essence}%
\label{sub:back-essence}
\section{ACD Term Rewriting}%
\label{sec:back-term}

View File

@ -3,7 +3,7 @@
%************************************************
Rewriting a high-level constraint model down into an equivalent solver-level
constraint model might often seems like a simple term rewriting system. In
constraint model might often seem like a simple term rewriting system. In
reality, however, simple rewriting of the model will often result in sub-optimal
solver-level model and this might result in exponentially more work for the
solver. To combat this problem many techniques have been developed to create
@ -35,7 +35,7 @@ tool chain allows us to:
\end{itemize}
The new architecture is shown in \Cref{sfig:4-newarch}. A constraint model is
first compiled to byte code (called \microzinc), independent of the data. The
first compiled to byte code (called \microzinc{}), independent of the data. The
byte code is interpreted with the data to produce \nanozinc\ code, an extension
of the existing \flatzinc\ format. The interpreter can even be made incremental:
in \cref{ch:incremental} we discuss how in meta optimisation, no recompilation
@ -78,15 +78,15 @@ presents our conclusions.
This section introduces the two sub-languages of \minizinc\ at the core of the
new abstract machine model we have developed.
\microzinc\ is a simplified subset of \minizinc\ that only contains function
\microzinc{} is a simplified subset of \minizinc\ that only contains function
declarations and a restricted expression language, but it extends \minizinc{}'s
type system with tuples. Models written in \minizinc\ can be translated into
\microzinc.
\microzinc{}.
\nanozinc\ is similar to \flatzinc\ in that it contains only simple variable
\nanozinc{} is similar to \flatzinc\ in that it contains only simple variable
declarations and flat constraints. However, while all function calls in
\flatzinc\ need to be solver-level primitives, \nanozinc\ calls can refer to
functions implemented in \microzinc. Furthermore, \nanozinc\ allows for
functions implemented in \microzinc{}. Furthermore, \nanozinc\ allows for
constraints to be bound to a specific variable and constraints and variables
declarations can be interleaved.
@ -124,7 +124,7 @@ and where expressions have \mzninline{par} type.
<gen-exp> ::= <ident> "in" <exp> ["," <ident> "in" <exp> ]*
\end{grammar}
\caption{\label{fig:4-uzn-syntax}Syntax of \microzinc.}
\caption{\label{fig:4-uzn-syntax}Syntax of \microzinc{}.}
\end{figure}
\newcommand{\sep}{\rhd}
@ -149,17 +149,17 @@ explained in detail in \cref{sec:4-nanozinc}, for now you can ignore them).
<nzn-bind> ::= "└──" <nzn-con>
\end{grammar}
\caption{\label{fig:4-nzn-syntax}Syntax of \nanozinc.}
\caption{\label{fig:4-nzn-syntax}Syntax of \nanozinc{}.}
\end{figure}
\subsection{\glsentrytext{minizinc} to
\glsentrytext{microzinc}/\glsentrytext{nanozinc}}
The translation from \minizinc\ to \microzinc\ involves the following steps:
The translation from \minizinc\ to \microzinc{} involves the following steps:
\begin{enumerate}
\item Transform all expressions that are valid in \minizinc\ but not in
\microzinc. This includes simple cases such as replacing operator
\microzinc{}. This includes simple cases such as replacing operator
expressions like \mzninline{x+y} by function calls
\mzninline{int_plus(x,y)}. As mentioned above, more complex rules for
rewriting conditionals with variables, such as \mzninline{if x then A
@ -180,17 +180,17 @@ The translation from \minizinc\ to \microzinc\ involves the following steps:
if boolean variable exists.
\item Lift the partiality in expressions into the surrounding context to
implement \minizinc{}'s relational semantics. In contrast to \minizinc,
the evaluation of \microzinc\ will implement strict semantics for
partial expressions, whereas \minizinc\ uses relational semantics.
Stuckey and Tack have extensively examined this problems and describe
how to transform any partial function into a total function.
implement \minizinc{}'s relational semantics. In contrast to
\minizinc{}, the evaluation of \microzinc\ will implement strict
semantics for partial expressions, whereas \minizinc\ uses relational
semantics. Stuckey and Tack have extensively examined these problems and
describe how to transform any partial function into a total function.
\autocite*{stuckey-2013-functions}. A general approach for describing
the partially in \minizinc\ functions in \microzinc\ functions is to
make the function return both a boolean variable that indicates the
totality of the input variables and the resulting value of the function
that is adjusted that is adjusted to return a predefined value when it
would normally be undefined. For example, The function
the partially in \microzinc\ functions is to make the function return
both a boolean variable that indicates the totality of the input
variables and the resulting value of the function that is adjusted that
is adjusted to return a predefined value when it would normally be
undefined. For example, The function
\begin{mzn}
function var int: element(array of int: a, var int: x);
@ -230,14 +230,14 @@ The translation from \minizinc\ to \microzinc\ involves the following steps:
every call, the function dispatch in \microzinc\ is determined
statically.
It is important, however, that the correct version of functions that
allow for both variable and parameter arguments is chosen. And, as we
will later discuss in more detail, is possible for variables to be fixed
to a single value, at which point they can be treated as parameters. The
ensure the correct version of the function is used, functions can be
defined to, at runtime, check if a variable is fixed and, if so,
dispatch to the parameter version of the function. If we for example
have a \minizinc\ function \mzninline{f} that is overloaded on
It is important, however, that the correct version of a function is
chosen when it allows for both variable and parameter parameters. And,
as we will later discuss in more detail, is possible for variables to be
fixed to a single value, at which point they can be treated as
parameters. To ensure the correct version of the function is used,
functions are changed to, at runtime, check if a variable is fixed and,
if so, dispatch to the parameter version of the function. If we for
example have a \minizinc\ function \mzninline{f} that is overloaded on
\mzninline{var int} and \mzninline{int}, then the \microzinc\
transformation might look like:
@ -315,8 +315,9 @@ The translation from \minizinc\ to \microzinc\ involves the following steps:
Each call in a \nanozinc\ program is either a call to a solver-level predicate,
or it has a corresponding \microzinc\ function definition. The goal of partial
evaluation is to transform the \nanozinc\ program into (essentially) \flatzinc,
\ie\ a program where all calls are calls to solver-level predicates.
evaluation is to transform the \nanozinc\ program into (essentially)
\flatzinc{}, \ie\ a program where all calls are calls to solver-level
predicates.
To achieve this, we define the semantics of \microzinc\ as a rewriting system
that produces \nanozinc\ calls. Each non-primitive call in a \nanozinc\ program
@ -435,7 +436,7 @@ suitable alpha renaming.
\tuple{\ensuremath{\texttt{constraint}~\ptinline{F}(a_1, \ldots, a_k), \Env}}}
\end{prooftree}
\caption{\label{fig:4-rewrite-to-nzn-calls} Rewriting rules for partial evaluation
of \microzinc\ calls to \nanozinc.}
of \microzinc\ calls to \nanozinc{}.}
\end{figure*}
The rules in \cref{fig:4-rewrite-to-nzn-calls} handle function calls. The first
@ -497,7 +498,7 @@ considered primitives, and as such simply need to be transferred into the
\infer2[(ItemC)]{\Sem{\(\mbox{constraint}~C\texttt{;} \mathbf{I} \mid{} \mathbf{X}\)}{\Prog, \Env, \Ctx} \Rightarrow\ \tuple{x, \Env''}}
\end{prooftree}
\caption{\label{fig:4-rewrite-to-nzn-let} Rewriting rules for partial evaluation
of \microzinc\ let expressions to \nanozinc.}
of \microzinc\ let expressions to \nanozinc{}.}
\end{figure*}
The rules in \cref{fig:4-rewrite-to-nzn-let} considers let expressions. Starting
@ -561,7 +562,7 @@ notation.
\tuple{\mbox{concat} [ A_v~|~v \in{} S ], \Env{} \cup{} \bigcup{}_{v \in{} S} \Env{}_{v}}}
\end{prooftree}
\caption{\label{fig:4-rewrite-to-nzn-other} Rewriting rules for partial evaluation
of other \microzinc\ expressions to \nanozinc.}
of other \microzinc\ expressions to \nanozinc{}.}
\end{figure*}
Finally, the rules in \cref{fig:4-rewrite-to-nzn-other} handle the evaluation of
@ -585,23 +586,23 @@ concatenation of the resulting lists with all the additional \nanozinc\ items.
Our prototype implementation of the \microzinc/\nanozinc\ framework consists of
the following components.
The \textbf{compiler} translates \minizinc\ into a \textbf{bytecode} encoding of
\microzinc. The compiler currently supports a significant subset of the full
The \textbf{compiler} translates \minizinc\ into a \textbf{byte code} encoding of
\microzinc{}. The compiler currently supports a significant subset of the full
\minizinc\ language, with the missing features (such as multi-dimensional arrays
and complex output statements) requiring additional engineering effort but no
new technology.
The \textbf{interpreter} evaluates \microzinc\ bytecode and \nanozinc\ programs
The \textbf{interpreter} evaluates \microzinc\ byte code and \nanozinc\ programs
based on the rewrite system introduced in this section. It can read parameter
data from a file or via an API\@. The interpreter constructs the call to the
\mzninline{main} function, and then performs the rewriting into flat \nanozinc.
\mzninline{main} function, and then performs the rewriting into flat \nanozinc{}.
Memory management is implemented using reference counting, which lends itself
well to the optimisations discussed in the next section. The interpreter is
written in around 3.5kLOC of C++ to enable easy embedding into applications and
other programming languages.
The framework includes \textbf{interfaces} to multiple solvers. It can
pretty-print the \nanozinc\ code to standard \flatzinc, so that any solver
pretty-print the \nanozinc\ code to standard \flatzinc{}, so that any solver
currently compatible with \minizinc\ can be used. Additionally, a direct C++ API
can be used to connect solvers directly, in order to enable incremental
solving. This topic is revisited in \cref{ch:incremental}.
@ -667,11 +668,11 @@ prepended by \texttt{└── }).
independent of the value of \mzninline{b}. The constraint \mzninline{abs(x) >
y} is therefore not required. However, the rewriting has to choose a
particular order in which arguments are evaluated, so it is always possible
that it already evaluated the left hand side before ``noticing'' that the
that it already evaluated the left-hand side before ``noticing'' that the
disjunction is true.
If the system now simplifies the constraint \mzninline{bool_or(b, c)} to
\mzninline{true}, the identifier \mzninline{b} will become unused outside of
\mzninline{true}, the identifier \mzninline{b} will become unused outside
its auxiliary definitions, since it was only referenced from the
\mzninline{bool_or} call. Removing \mzninline{b} leads will also its defining
constraint, \mzninline{int_gt_reif}, being removed. This in turn means that
@ -689,10 +690,10 @@ prepended by \texttt{└── }).
other parts of the model not shown here and therefore not removed.
\end{example}
Note how how counting usage of variables outside its auxiliary definitions
allows us to remove the \mzninline{z} variable --- we could not simply have
counted the usage of \mzninline{z} in all constraints since the constraint used
to define it, \mzninline{int_abs(x, z)}, would have kept it alive.
Note how counting usage of variables outside its auxiliary definitions allows us
to remove the \mzninline{z} variable --- we could not simply have counted the
usage of \mzninline{z} in all constraints since the constraint used to define
it, \mzninline{int_abs(x, z)}, would have kept it alive.
It is important to notice the difference between constraints that appear at the
top level and constraints that appear as part of the auxiliary definitions of a
@ -855,7 +856,7 @@ arguments). These links do not take part in the reference counting.
Adding \gls{propagation} and simplification into the rewriting system means
that the system becomes non-confluent, and some orders of execution may produce
``better'', \ie\ more compact or more efficient, \nanozinc.
``better'', \ie\ more compact or more efficient, \nanozinc{}.
\begin{example}
The following example is similar to code found in the \minizinc\ libraries of
@ -901,7 +902,7 @@ can then be fully simplified by \gls{propagation}, before the Boolean and
reified constraints are rewritten into lower-level linear primitives suitable
for MIP\@.
\subsection{Common Subexpression Elimination}\label{sec:4-cse}
\subsection{Common Sub-expression Elimination}\label{sec:4-cse}
The simplifications introduced above remove definitions from the model when we
can detect that they are no longer needed. In some cases, it is even possible to
@ -946,9 +947,9 @@ in the following example.
straightforward approach to ensure that the same instantiation of a function
is only evaluated once is through memorisation. After the evaluation of a
\microzinc\ call, the instantiated call and its result are stored in a lookup
table, the \gls{cse} table. Before a call is evaluated the \gls{cse} table is
consulted. If the call is found, then the accompanying result is used;
otherwise, the evaluation proceeds as normal.
table, the \gls{cse} table. Then before any consequent call is evaluated the
\gls{cse} table is consulted. If the call is found, then the accompanying
result is used; otherwise, the evaluation proceeds as normal.
In our example, the evaluation of \mzninline{pythagoras(i, i)} would proceed
as normal to evaluate \mzninline{x = pow(i, 2)}. However, the expression
@ -958,19 +959,19 @@ in the following example.
It is worth noting that the \gls{cse} approach based on memorisation also covers
the static example above, where the compiler can enforce sharing of common
subexpressions before evaluation begins. However, since the static analysis is
cheap and can potentially avoid many dynamic table lookups, it is valuable to
sub-expressions before evaluation begins. However, since the static analysis is
cheap and can potentially avoid many dynamic table look-ups, it is valuable to
combine both approaches. The static approach can be further improved by
\emph{inlining} function calls, since that may expose more calls as being
syntactically equal.
\jip{TODO: \gls{cse} will be discussed in the background. How can we make this
more specific to how it works in \nanozinc}
more specific to how it works in \nanozinc{}.}
\paragraph{Reification}
Many constraint modelling languages, including \minizinc, not only enable
Many constraint modelling languages, including \minizinc{}, not only enable
constraints to be globally enforced, but typically also support constraints to
be \emph{reified} into a Boolean variable. Reification means that a variable
\mzninline{b} is constrained to be true if and only if a corresponding
@ -978,7 +979,7 @@ constraint \mzninline{c(...)} holds. We have already seen reification in
\cref{ex:4-absreif}: the truth of constraint \mzninline{abs(x) > y} was bound to
a Boolean variable \mzninline{b1}, which was then used in a disjunction. We say
that the same constraint can be used in \emph{root context} as well as in a
\emph{reified context}. In \minizinc, almost all constraints can be used in both
\emph{reified context}. In \minizinc{}, almost all constraints can be used in both
contexts. However, reified constraints are often defined in the library in terms
of complicated decompositions into simpler constraints, or require specialised
algorithms in the target solvers. In either case, it can be very beneficial for
@ -992,13 +993,14 @@ reification (or in fact any evaluation). We can harness \gls{cse} to store the
evaluation context when a constraint is added, and detect when the same
constraint is used in both contexts. Whenever a lookup in the \gls{cse} table is
successful, action can be taken depending on both the current and stored
evaluation context. If the stored expression was in root context, the constant
\mzninline{true} can be used, independent of the current context. If the stored
expression was in reified context and the current context is reified, the stored
result variable can be used. If the stored expression was in reified context and
the current context is root context, then the previous result can be replaced by
the constant \mzninline{true} (and all its dependencies removed) and the
evaluation will proceed as normal with the root context constraint.
evaluation context. If the stored expression was in root context, then the
constant \mzninline{true} can be used, independent of the current context.
Otherwise, if the stored expression was in reified context and the current
context is reified, then the stored result variable can be used. Finally, if the
stored expression was in reified context and the current context is root
context, then the previous result can be replaced by the constant
\mzninline{true} (and all its dependencies removed) and the evaluation will
proceed as normal with the root context constraint.
\begin{example}
Consider the fragment:
@ -1011,19 +1013,19 @@ evaluation will proceed as normal with the root context constraint.
If we process the top-level constraints in order we create a reified call to
\mzninline{q(x)} for the original call. Suppose processing the second
constraint we discover \mzninline{t(x,y)} is \mzninline{true}, setting
\mzninline{b1}. When we process \mzninline{q(x)} in the call
\mzninline{t(x,y)} we find it is the root context. So \gls{cse} needs to set
\mzninline{b0} to \mzninline{true}.
constraint we discover \mzninline{t(x,y)} is \mzninline{true}, fixing
\mzninline{b1}. When we then process \mzninline{q(x)} in instantiation of the
call \mzninline{p(x,y)}, we find it is the root context. So \gls{cse} needs to
set \mzninline{b0} to \mzninline{true}.
\end{example}
\minizinc\ distinguishes not only between root and reified contexts, but in
\minizinc{} distinguishes not only between root and reified contexts, but in
addition recognises constraints in \emph{positive} contexts, also known as
\emph{half-reified} \autocite{feydy-2011-half-reif}. A full explanation and
discussion of half-reification can be found in \cref{ch:half_reif}.
\jip{TODO: Reification should also be discussed in the background. How can we
make this more specific to how it works in \nanozinc}
make this more specific to how it works in \nanozinc{}.}
\subsection{Constraint Aggregation}%
\label{subsec:rew-aggregation}
@ -1047,11 +1049,11 @@ operators. For example the evaluation of the linear constraint \mzninline{x +
These \nanozinc\ definitions are correct, but, at least for \gls{mip} solvers,
the existence of the intermediate variables is likely to have a negative impact
on the solvers performance. These solves would likely performed better had they
received the linear constraint \mzninline{int_lin_le([1,2,-1], [x,y,z], 0)}
directly. Since many solvers support linear constraints, it is often an
additional burden to have intermediate values that have to be given a value in
the solution.
on the \gls{solver}'s performance. These \glspl{solver} would likely perform
better had they received the linear constraint \mzninline{int_lin_le([1,2,-1],
[x,y,z], 0)} directly. Since many solvers support linear constraints, it is
often an additional burden to have intermediate values that have to be given a
value in the solution.
This can be resolved using the \gls{aggregation} of constraints. When we
aggregate constraints we combine constraints connected through functional
@ -1060,25 +1062,25 @@ intermediate variables. For example, the arithmetic definitions can be combined
into linear constraints, Boolean logic can be combined into clauses, and
counting constraints can be combined into global cardinality constraints.
In \nanozinc, we are able to aggregate constraints during partial evaluation. To
In \nanozinc{}, we are able to aggregate constraints during partial evaluation. To
aggregate a certain kind of constraint, the solver must the constraint as a
solver-level primitive. These constraints will now be kept as temporary
functional definitions in the \nanozinc\ program. Once a top-level (relational)
constraint is posted that uses the temporary functional definitions as one of
\gls{constraint} is posted that uses the temporary functional definitions as one of
its arguments, the interpreter will employ dedicated \gls{aggregation} logic to
visit the functional definitions and combine their constraints. The top-level
constraint constraint is then replaced by the combined constraint. When the
\gls{constraint} is then replaced by the combined \gls{constraint}. When the
intermediate variables become unused, they will be removed using the normal
mechanisms.
\jip{TODO: Aggregation is also part of the background. How can we make this more
specific to how it works in \nanozinc}
specific to how it works in \nanozinc{}.}
\section{Experiments}\label{sec:4-experiments}
We have created a prototype implementation of the architecture presented in the
preceding sections. It consists of a compiler from \minizinc\ to \microzinc, and
an incremental \microzinc\ interpreter producing \nanozinc. The system supports
preceding sections. It consists of a compiler from \minizinc\ to \microzinc{}, and
an incremental \microzinc\ interpreter producing \nanozinc{}. The system supports
a significant subset of the full \minizinc\ language; notable features that are
missing are support for set and float variables, option types, and compilation
of model output expressions and annotations. We will release our implementation
@ -1093,11 +1095,11 @@ well as incremental flattening and solving that demonstrate the efficiency
gains that are possible thanks to the new architecture.
We selected 20 models from the annual \minizinc\ challenge and compiled 5
instances of each model to \flatzinc, using the current \minizinc\ release
instances of each model to \flatzinc{}, using the current \minizinc\ release
version 2.4.3 and the new prototype system. In both cases we use the standard
\minizinc\ library of global constraints (\ie\ we decompose those constraints
rather than using solver built-ins, in order to stress-test the flattening). We
measured pure flattening time, \ie\ without time required to parse and typecheck
measured pure flattening time, \ie\ without time required to parse and type check
in version 2.4.3, and without time required for compilation to \microzinc\ in
the new system (compilation is usually very fast). Times are averages of 10
runs.\footnote{All models obtained from
@ -1123,13 +1125,13 @@ unoptimised prototype to a mature piece of software.
\begin{subfigure}[b]{.48\columnwidth}
\centering
\includegraphics[width=\columnwidth]{assets/img/4_compare_runtime}
\caption{\label{sfig:4-compareruntime}flattening run time (ms)}
\caption{\label{sfig:4-compareruntime}Flattening run time (ms)}
\end{subfigure}%
\hspace{0.04\columnwidth}%
\begin{subfigure}[b]{.48\columnwidth}
\centering
\includegraphics[width=\columnwidth]{assets/img/4_compare_memory}
\caption{\label{sfig:4-comparemem}flattening memory (MB)}
\caption{\label{sfig:4-comparemem}Flattening memory (MB)}
\end{subfigure}
\caption{\label{fig:4-runtime}Performance on flattening 100 MiniZinc Challenge
instances. \minizinc\ 2.4.3 (x-axis) vs new architecture (y-axis), log-log
@ -1139,10 +1141,10 @@ unoptimised prototype to a mature piece of software.
\section{Summary}%
\label{sec:rew-summary}
In this chapter, we introduced a systematic view of the execution of \minizinc,
revisiting the rewriting from high-level \minizinc\ to solver-level \flatzinc.
We first introduced introduced the intermediate languages \microzinc\ and
\nanozinc\ and explained how \minizinc\ can be transformed into a set of
In this chapter, we introduced a systematic view of the execution of \minizinc{},
revisiting the rewriting from high-level \minizinc\ to solver-level \flatzinc{}.
We first introduced the intermediate languages \microzinc{} and
\nanozinc{} and explained how \minizinc\ can be transformed into a set of
\microzinc\ definitions and a \nanozinc\ program. We then, crucially, discussed
how to partially evaluate a \nanozinc\ program using \microzinc\ definitions and
present formal definitions of the rewriting rules applied during partial
@ -1151,7 +1153,7 @@ evaluation.
Continuously applying these rules would result in a correct solver-level model,
but it is unlikely to be the best model for the solver. We, therefore, discuss
multiple techniques to improve the solver-level constraint model during the
partial evaluation of \nanozinc. First, we introduce a novel technique to
partial evaluation of \nanozinc{}. First, we introduce a novel technique to
eliminate all unused variables and constraints: we track all constraints that
are introduced only to define a variable. This means we can keep an accurate
count of when a variable becomes unused by counting only the references to a
@ -1160,7 +1162,7 @@ variable in constraints that do not help define it. Then, we discuss the use of
technique can help shrink the domains of the decision variable or even combine
variables known to be equal. When a redefinition of a constraint requires the
introspection into the current domain of a variable, it is often important to
have the tightest possible domain. Hence, we discuss how in choosing the the
have the tightest possible domain. Hence, we discuss how in choosing the
next \nanozinc\ constraint to rewrite, the interpreter can sometimes delay the
rewriting of certain constraints to ensure the most amount of information is
available. \Gls{cse}, our next optimisation technique, ensures that we do not
@ -1171,7 +1173,7 @@ individual functional constraints can be collected and combined into an
aggregated form. This allows us to avoid the existence of intermediate variables
in some cases. This optimisation is very important for \gls{mip} solvers.
Finally, we test the described system using a experimental implementation. We
Finally, we test the described system using an experimental implementation. We
compare this experimental implementation against the current \minizinc\
interpreter, version 2.5.3, and look at both runtime and its memory usage.
Although the experimental implementation there are instances where the

View File

@ -9,7 +9,7 @@ modifications, thousands of times. Examples of these methods are:
\begin{itemize}
\item Multi-objective search \autocite{jones-2002-multi-objective}. Optimising
multiple objectives is often not supported directly in solvers. Instead
multiple objectives is often not supported directly in solvers. Instead,
it can be solved using a \gls{meta-search} approach: find a solution to
a (single-objective) problem, then add more constraints to the original
problem and repeat.
@ -41,17 +41,17 @@ All of these examples have in common that a problem instance is solved, new
constraints are added, the resulting instance is solved again, and constraints
may be removed again.
The usage of these methods is not new to \gls{constraint-modelling} and they
The usage of these methods is not new to \gls{constraint-modelling}, and they
have proven to be very useful \autocite{schrijvers-2013-combinators,
rendl-2015-minisearch, schiendorfer-2018-minibrass, ek-2020-online,
ingmar-2020-diverse}. In its most basic form, a simple scripting language is
rendl-2015-minisearch, schiendorfer-2018-minibrass, ek-2020-online,
ingmar-2020-diverse}. In its most basic form, a simple scripting language is
sufficient to implement these methods, by repeatedly calling on the
\gls{constraint-modelling} infrastructure to compile and solve the adjusted
constraint models. While improvements of the compilation of constraint models,
such as the ones discussed in previous chapters, can increase the performance of
these approaches, the overhead of re-compiling an almost identical model may
still prove prohibitive, warranting direct support from the
\gls{constraint-modelling} infrastructure. In this chapter we introduces two
\gls{constraint-modelling} infrastructure. In this chapter we introduce two
methods to provide this support:
\begin{itemize}
@ -61,7 +61,7 @@ methods to provide this support:
algorithms into efficient solver-level specifications based on solver
restarts, avoiding re-compilation all-together.
\item Alternatively, we can add an incremental interface for adding and
removing constraints to the infrastructure of the \cml. Although this
removing constraints to the infrastructure of the \cml{}. Although this
does not avoid the need for re-compilation, it can reduce the work to
only the part of the constraint model that has changed. This approach
can be used when an algorithm cannot be described using restart-based
@ -70,7 +70,7 @@ methods to provide this support:
The rest of the chapter is organised as follows. \Cref{sec:6-modelling}
discusses the declarative modelling of restart-based \gls{meta-search}
algorithms that can be modelled directly in a \cml.
algorithms that can be modelled directly in a \cml{}.
\Cref{sec:6-solver-extension} introduces the method to compile these
\gls{meta-search} specifications into efficient solver-level specifications that
only require a small extension of existing \glspl{solver}.
@ -83,8 +83,8 @@ Finally, \Cref{sec:6-conclusion} presents the conclusions.
\section{Modelling of Restart-Based Meta-Search}\label{sec:6-modelling}
This section introduces a \minizinc\ extension that enables modellers to define
\gls{meta-search} algorithms in \cmls. This extension is based on the construct
This section introduces a \minizinc{} extension that enables modellers to define
\gls{meta-search} algorithms in \cmls{}. This extension is based on the construct
introduced in \minisearch\ \autocite{rendl-2015-minisearch}, as summarised
below.
@ -96,7 +96,7 @@ below.
% variables that need to be applied to the base model, \eg\ forcing variables to
% take the same value as in the previous solution.
\minisearch\ introduced a \minizinc\ extension that enables modellers to express
\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.
@ -157,9 +157,9 @@ To address these two issues, we propose to keep modelling neighbourhoods as
predicates, but define \gls{meta-search} algorithms from an imperative
perspective.
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
We define a few 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.
@ -200,7 +200,7 @@ The second component of our \gls{lns} definition is the \emph{restarting
strategy}, defining how much effort the solver should put into each
neighbourhood (\ie\ restart), and when to stop the overall search.
We propose adding new search annotations to \minizinc\ to control this behaviour
We propose adding new search annotations to the \minizinc\ language 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}
@ -220,7 +220,7 @@ search after a fixed number of restarts.
Although using just a restart annotations by themselves allows us to run the
basic \gls{lns} algorithm, more advanced \gls{meta-search} algorithms will
require more then reapplying the same \gls{neighbourhood} time after time. It
require more than reapplying the same \gls{neighbourhood} time after time. It
is, for example, often beneficial to use several \gls{neighbourhood} definitions
for a problem. Different \glspl{neighbourhood} may be able to improve different
aspects of a solution, at different phases of the search. Adaptive \gls{lns}
@ -273,8 +273,7 @@ status is not \mzninline{START}:
\mznfile{assets/mzn/6_basic_lns.mzn}
In order to use this predicate with the \mzninline{on_restart} annotation, we
cannot simply pass \mzninline{basic_lns(uniform_neighbourhood(x, 0.2))}. First
of all, calling \mzninline{uniform_neighbourhood} like that would result in a
cannot simply pass \mzninline{basic_lns(uniform_neighbourhood(x, 0.2))}. Calling \mzninline{uniform_neighbourhood} 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
@ -299,7 +298,7 @@ activate a different neighbourhood for each restart
\begin{listing}
\mznfile{assets/mzn/6_round_robin.mzn}
\caption{\label{lst:6-round-robin} A predicate providing the round robin
\caption{\label{lst:6-round-robin} A predicate providing the round-robin
meta-heuristic}
\end{listing}
@ -330,7 +329,7 @@ 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
to decide whether a new solution is globally the 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).
@ -345,9 +344,9 @@ through the built-in variable \mzninline{_objective}. A more interesting example
is a simulated annealing strategy. When using this strategy, the solutions that
the solver finds are no longer required to steadily improve in quality. Instead,
we ask the solver to find a solution that is a significant improvement over the
previous solution. Over time we decrease the amount by which we require the
previous solution. Over time, we decrease the amount by which we require the
solution needs to improve until we are just looking for any improvements. This
\gls{meta-search} can help improving the qualities of solutions quickly and
\gls{meta-search} can help improve the qualities of solutions quickly and
thereby reaching the optimal solution quicker. This strategy is also easy to
express using our restart-based modelling:
@ -365,29 +364,29 @@ on. We can model this strategy restarts as such:
\mznfile{assets/mzn/6_lex_minimize.mzn}
The lexicographic objective changes the objective at each stage in the
evaluation. Initially the stage is 1. Otherwise is we have an UNSAT result, then
the last stage has been completed (proved optimal). We increase the stage by one
if we have stages to go otherwise we finish. Otherwise if the last all was SAT
we maintain the same stage, and store the objective value (for this stage) in
the \mzninline{best} array. For normal computation we fix all the earlier stage
variables to their best value. If we are not in the first run for a stage we add
the branch and bound cut to try to find better solutions. Finally we set the
objective to be the objective for the current stage.
evaluation. Initially the stage is 1. Otherwise, is we have an unsatisfiable
result, then the last stage has been completed (proved optimal). We increase the
stage by one if we have stages to go otherwise we finish. Otherwise, if the last
all was SAT we maintain the same stage, and store the objective value (for this
stage) in the \mzninline{best} array. For normal computation we fix all the
earlier stage variables to their best value. If we are not in the first run for
a stage we add the branch and bound cut to try to find better solutions.
Finally, we set the objective to be the objective for the current stage.
There is not always a clear order of importance for different objectives in a
problem. In these cases we might instead look for a number of diverse solutions
and allow the user to pick the most acceptable options. The following fragment
shows a \gls{meta-search} for the pareto optimality of a pair of objectives:
shows a \gls{meta-search} for the Pareto optimality of a pair of objectives:
\mznfile{assets/mzn/6_pareto_optimal.mzn}
In this implementation we keep track of the number of solutions found so far
using \mzninline{nsol}. There is a maximum number we can handle
(\mzninline{ms}). At the start the number of solutions is 0. If we find no
solutions we finish the entire search. Otherwise we record that we have one more
solution. We store the solution values in \mzninline{s1} and \mzninline{s2}
arrays. Before each restart we add constraints removing pareto dominated
solutions, based on each previous solution.
solutions, then we finish the entire search. Otherwise, we record that we have
one more solution. We store the solution values in \mzninline{s1} and
\mzninline{s2} arrays. Before each restart we add constraints removing Pareto
dominated solutions, based on each previous solution.
\section{Compilation of Meta-Search Specifications}\label{sec:6-solver-extension}
@ -410,10 +409,10 @@ executed by standard \gls{cp} solvers with a small set of simple extensions.
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.
in terms of standard \minizinc\ expressions, except for 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
@ -426,8 +425,8 @@ 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 \gls{lns} specification to standard \flatzinc, the \minizinc\ compiler
performs four simple steps:
To compile a \gls{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
@ -506,13 +505,12 @@ Calls to the random number functions have been renamed by appending
The definition of these new functions follows the same pattern as for
\mzninline{sol}, \mzninline{status}, and \mzninline{last_val}. 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 \gls{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}.
\Cref{lst:6-int-rnd}. \footnote{Random number functions need to be marked as
\mzninline{::impure} for the compiler not to apply \gls{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}
\mznfile{assets/mzn/6_uniform_slv.mzn}
@ -524,7 +522,7 @@ as \mzninline{sol}.
We will now show the minimal extensions required from a solver to interpret the
new \flatzinc\ constraints and, consequently, to execute \gls{lns} definitions
expressed in \minizinc.
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
@ -541,13 +539,13 @@ new constraints the solver needs to:
\item \mzninline{last_val(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{last_val} for their
\gls{sat} and \gls{lcg} solvers) already track \mzninline{last_val} for their
variables for use in search. To support \gls{lns} a solver must at least
track the \emph{last value} of each of the variables involved in such a
constraint. This is straightforward by using the \mzninline{last_val}
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
\item Random number functions: fix their variable argument to a random number
in the appropriate probability distribution.
\end{itemize}
@ -557,8 +555,8 @@ 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).
implemented these extensions for both \gls{gecode} (110 new lines of code) and
\gls{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
@ -590,7 +588,7 @@ presentation.
\begin{listing}
\mznfile{assets/mzn/6_basic_complete_transformed.mzn}
\caption{\label{lst:6-flat-pred} \flatzinc\ that results from compiling \\
\caption{\label{lst:6-flat-pred} \flatzinc{} that results from compiling \\
\mzninline{basic_lns(uniformNeighbourhood(x,0.2))}.}
\end{listing}
@ -600,7 +598,7 @@ 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
When the solver restarts, all 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
@ -614,9 +612,10 @@ against being invoked before the first solution is found, since the
solution has been recorded yet, but we use this simple example to illustrate how
these Boolean conditions are compiled and evaluated.
\section{An Incremental Interface for Constraint Modelling Languages}\label{sec:6-incremental-compilation}
\section{An Incremental Interface for Constraint Modelling Languages}%
\label{sec:6-incremental-compilation}
As an alternative approach to run \gls{meta-search} algorithm we propose the
As an alternative approach to run \gls{meta-search} algorithm, we propose the
possibility of incremental flattening. The execution of any
In order to support incremental flattening, the \nanozinc\ interpreter must be
@ -649,7 +648,7 @@ structure~\autocite{warren-1983-wam}. The trail records all changes to the
\nanozinc\ program:
\begin{itemize}
\item the addition or removal of new variables or constraints,
\item The addition or removal of new variables or constraints,
\item changes made to the domains of variables,
\item additions to the \gls{cse} table, and
\item substitutions made due to equality propagation.