1620 lines
112 KiB
TeX
1620 lines
112 KiB
TeX
%************************************************
|
|
\chapter{Background}\label{ch:background}
|
|
%************************************************
|
|
|
|
\input{chapters/2_background_preamble}
|
|
|
|
\glsresetall{}
|
|
|
|
\section{Introduction to Constraint Modelling Languages}
|
|
\label{sec:back-intro}
|
|
|
|
A goal shared between all programming languages is to provide a certain level of abstraction to their users.
|
|
This reduces the complexity of the programming tasks by hiding unnecessary information.
|
|
For example, an assembly language allows its user to abstract from the machine instructions and memory positions that are used by the hardware.
|
|
Early imperative programming languages, like FORTRAN, were the first to offer abstraction from the processor architecture of the targeted system.
|
|
Consequently, in current times, writing a computer program requires little knowledge of how the targeted computer system operates.
|
|
|
|
\textcite{freuder-1997-holygrail} states that the ``Holy Grail'' of programming languages would be where the user merely states the problem, and the computer solves it, and that \constraint{} modelling is one of the biggest steps towards this goal to this day.
|
|
\Cmls{} operate differently from other computer languages.
|
|
The modeller does not describe how to solve a problem, but rather formalizes the requirements of the problem.
|
|
It could be said that a \cmodel{} actually describes the answer to the problem.
|
|
|
|
In a \cmodel{}, instead of specifying the manner in which we find a \gls{sol}, we give a concise description of the problem.
|
|
The elements of a \cmodel{} include \prbpars{}, what we already know; \variables{}, what we wish to know; and \constraints{}, the relations that should exist between them.
|
|
Through the variation of \prbpars{}, a \cmodel{} describes a full class of problems.
|
|
A specific problem is captured by an \instance{}, the combination of a \cmodel{} with a complete \gls{parameter-assignment} (i.e., a mapping from all \prbpars{} to a value).
|
|
|
|
The type of problem described by a \cmodel{} is called a \gls{dec-prb}.
|
|
The goal of a \gls{dec-prb} is to find a \gls{sol}: a complete \gls{variable-assignment} that satisfies the \constraints{}, or, when this is not possible, prove that such an \gls{assignment} cannot exist.
|
|
Many \cmls{} also support the modelling of \glspl{opt-prb}, where a \gls{dec-prb} is augmented with an \gls{objective}.
|
|
In this case the goal is to find a \gls{sol} that satisfies all \constraints{} while maximizing (or minimizing) the value of the \gls{objective}.
|
|
|
|
Although a \cmodel{} does not contain any instructions on how to find a suitable \gls{sol}, \instances{} of \cmodels{} can generally be given to a dedicated \solver{}.
|
|
However, to solve these \instances{} they first have to go through a \gls{rewriting} process to arrive at a \gls{slv-mod}, which is the input accepted by a \solver{}.
|
|
The \solver{} then uses a dedicated algorithm that finds a \gls{sol} that fits the requirements of the \instance{}.
|
|
|
|
\begin{example}%
|
|
\label{ex:back-knapsack}
|
|
|
|
As an example, let us consider the following scenario.
|
|
Packing for a weekend trip, I have to decide which toys to bring for my dog, Audrey.
|
|
We only have a small amount of space left in the car, so we cannot bring all the toys.
|
|
Since Audrey enjoys playing with some toys more than others, we try to pick the toys that bring Audrey the most amount of joy, but still fit in the car.
|
|
The following set of equations describes this ``knapsack'' problem as an \gls{opt-prb}.
|
|
|
|
\begin{equation*}
|
|
\text{maximize}~z~\text{subject to}~
|
|
\begin{cases}
|
|
S \subseteq T \\
|
|
z = \sum_{i \in S} joy(i) \\
|
|
\sum_{i \in S} space(i) \leq{} C \\
|
|
\end{cases}
|
|
\end{equation*}
|
|
|
|
In these equations \(S\) is a set \variable{}.
|
|
It represents the selection of toys to be packed for the trip.
|
|
The \gls{objective} evaluates the quality of the \gls{sol} through the \variable{} \(z\), which is bound to the amount of joy that the selection of toys will bring.
|
|
This is to be maximized.
|
|
The \prbpars{} \(T\) is the set of all available toys.
|
|
The \(joy\) and \(space\) functions are \prbpars{} used to map toys, \( t \in T\), to a numeric value describing the amount of enjoyment and space required respectively.
|
|
Finally, the \prbpar{} \(C\) gives the numeric value of the total space that is left in the car before packing the toys.
|
|
|
|
This \cmodel{} gives an abstract mathematical definition of the \gls{opt-prb} that would be easy to adjust to changes in the requirements.
|
|
To solve \instances{} of this problem, however, the \instances{} have to be rewritten into input accepted by a \solver{}.
|
|
\Cmls{} are designed to allow the modeller to express combinatorial problems similar to the above mathematical definition and generate input that can be directly used by dedicated \solvers{}.
|
|
\end{example}
|
|
|
|
\section{\glsentrytext{minizinc}}%
|
|
\label{sec:back-minizinc}
|
|
|
|
\minizinc{} is a high-level, \solver{}- and data-independent \cml{} for discrete decision and \glspl{opt-prb} \autocite{nethercote-2007-minizinc}.
|
|
Its expressive language and extensive library of \glspl{global} allow users to easily model complex problems.
|
|
|
|
\begin{example}%
|
|
\label{ex:back-mzn-knapsack}
|
|
|
|
Let us introduce the language by modelling the problem from \cref{ex:back-knapsack}.
|
|
A \minizinc{} model encoding this problem is shown in \cref{lst:back-mzn-knapsack}.
|
|
|
|
The model starts with the declaration of the \prbpars{}.
|
|
\Lref{line:back:knap:toys} declares the enumerated type \mzninline{TOYS} that represents all possible toys, \(T\) in the mathematical model in \cref{ex:back-knapsack}.
|
|
\Lrefrange{line:back:knap:joy}{line:back:knap:space} declare the \glspl{array} \mzninline{toy_joy} and \mzninline{toy_space}, that map toys to integer values.
|
|
These represent the functional mappings \(joy\) and \(space\).
|
|
Finally, \lref{line:back:knap:left} declares the integer \prbpar{} \mzninline{total_space} to represent the remaining car capacity, equivalent to \(C\).
|
|
|
|
The model then declares its \variables{}.
|
|
\Lref{line:back:knap:sel} declares the main \variable{} \mzninline{selection}, which represents the selection of toys to be packed.
|
|
This is \(S\) in our earlier model.
|
|
We also declare the \variable{} \mzninline{total_joy}, on \lref{line:back:knap:tj}, which is functionally defined to be the summation of all the joy for the toy picked in our selection.
|
|
|
|
The model then contains a \constraint{}, on \lref{line:back:knap:con}, which ensures we do not exceed the given capacity.
|
|
Finally, it states the goal for the \solver{}: to maximize the value of the \variable{} \mzninline{total_joy}.
|
|
\end{example}
|
|
|
|
\begin{listing}
|
|
\mznfile[l]{assets/listing/back_knapsack.mzn}
|
|
\caption{\label{lst:back-mzn-knapsack} A \minizinc{} model describing a 0-1 knapsack problem.}
|
|
\end{listing}
|
|
|
|
Note that, although more textual and explicit, the \minizinc{} model definition is very similar to our earlier mathematical definition.
|
|
|
|
A \minizinc{} model cannot be solved directly.
|
|
It first needs to be transformed into a \gls{slv-mod}: a list of \variables{} and \constraints{} that are directly supported as input by the \solver{}.
|
|
We call these types of \variables and \constraints{} \gls{native} to the \solver{}.
|
|
|
|
Given complete \gls{parameter-assignment}, a \minizinc{} model forms a \minizinc{} instance.
|
|
The process to transform a \minizinc{} instance into a \gls{slv-mod} is called \gls{rewriting}.
|
|
\Glspl{slv-mod} for \minizinc{} \solvers{} are generally represented in \flatzinc{}.
|
|
\flatzinc{} is a strict subset of \minizinc{} specifically chosen to represent \glspl{slv-mod}.
|
|
It is the primary way in which \minizinc{} communicates with \solvers{}.
|
|
|
|
\begin{example}
|
|
For example, the \minizinc{} model in \cref{lst:back-mzn-knapsack} and the following \gls{assignment} can form a \minizinc{} \instance{}.
|
|
|
|
\begin{mzn}
|
|
TOYS = {football, tennisball, stuffed_elephant};
|
|
toy_joy = [63, 12, 100];
|
|
toy_space = [32, 8, 40];
|
|
space_left = 44;
|
|
\end{mzn}
|
|
|
|
The modeller can then choose a \solver{}.
|
|
Let us assume we choose a \glsxtrshort{mip} \solver{}, whose \gls{native} \variables{} are only integer \variables{} and whose \gls{native} \constraints{} are only linear \constraints{}.
|
|
\Gls{rewriting} the \instance{} would result in the \flatzinc{} \gls{slv-mod} in \cref{lst:back-fzn-knapsack}.
|
|
The set type \variable{} \mzninline{selection} is now represented using three integer \variables{}, \mzninline{selection_@\(i\)@}.
|
|
Each represent whether an element is present in the set.
|
|
They take the value one if the element is present, and zero otherwise.
|
|
The sum-\constraints{} have been transformed into integer linear \constraints{}, \mzninline{int_lin_le} and \mzninline{int_lin_eq}.
|
|
The former constrains that the selection \variables{} multiplied by the space required for the represented element and ensures their sum is smaller than the available space.
|
|
The latter calculates the value of the \mzninline{total_joy} \variable{} by adding together selection \variables{} multiplied by the joy value of the represented element.
|
|
In these \constraints{}, the \prbpars{} are merely represented by the values given in the \gls{parameter-assignment}.
|
|
Their names are not present in the \gls{slv-mod}.
|
|
|
|
This \gls{slv-mod} is then passed to the targeted \solver{}.
|
|
The \solver{} attempts to determine a complete \gls{variable-assignment} and maximize the \gls{assignment} of the \mzninline{total_joy} \variable{}.
|
|
If such an \gls{assignment} does not exist, then it reports that the \gls{slv-mod} is \gls{unsat}.
|
|
\end{example}
|
|
\begin{listing}
|
|
\mznfile{assets/listing/back_knapsack.fzn}
|
|
\caption{\label{lst:back-fzn-knapsack} A \flatzinc{} \gls{slv-mod} for a \glsxtrshort{mip} \solver{}, resulting from \gls{rewriting} \cref{lst:back-mzn-knapsack} with a given complete \gls{parameter-assignment}.}
|
|
\end{listing}
|
|
|
|
|
|
\subsection{Model Structure}%
|
|
\label{subsec:back-mzn-structure}
|
|
|
|
The structure of a \minizinc{} model can be described directly in terms of a \cmodel{}:
|
|
|
|
\begin{itemize}
|
|
\item \variables{} and \prbpars{} are found in the form of variable declarations,
|
|
\item \constraints{} are explicitly defined using their own keyword,
|
|
\item and the \gls{objective} is included as a solving goal.
|
|
\end{itemize}
|
|
|
|
More complex models also include definitions for custom types, user-defined functions, and a custom output format.
|
|
These items are not constrained to occur in any particular order.
|
|
We briefly discuss the most important model items.
|
|
Note that these items already refer to \minizinc{} expressions, which will be discussed in the next subsection.
|
|
For a detailed overview of the structure of \minizinc{} models the full syntactic structure of \minizinc{} 2.5.5 can be consulted in \cref{ch:minizinc-grammar}.
|
|
\textcite{nethercote-2007-minizinc} offer a detailed discussion of \minizinc{}.
|
|
Much of \minizinc{}'s history can be learned from the description of its predecessor, \zinc{} \autocite{marriott-2008-zinc}.
|
|
|
|
\paragraph{Variable Declaration Items} Variables are declared using variable declaration items.
|
|
The term ``variable'' has two overlapping, and slightly confusing meanings.
|
|
As a programming language, \minizinc uses it to describe a distinct object that contains (currently unknown) information.
|
|
As such, a variable in \minizinc{} can be used to represent either a \variable{}, as defined before, or a \gls{parameter} variable.
|
|
The latter is used to represent any information that will be known during \gls{rewriting}.
|
|
This includes \prbpars{}, but also the result of introspection, or the result of calculations over other \parameters{}.
|
|
In the remainder of this thesis we will refer to \parameter{} variables merely as \parameters{}, but will distinguish them from \variables{}.
|
|
|
|
Variable declarations are stated in the form \mzninline{@\(T\)@: @\(I\)@ = @\(E\)@}, where:
|
|
\begin{itemize}
|
|
\item \(T\) is the type instance of the declared value,
|
|
\item \(I\) is a new identifier used to reference the declared value,
|
|
\item and the modeller can functionally define the value using an expression \(E\).
|
|
\end{itemize}
|
|
The syntax \mzninline{= @\(E\)@} is optional.
|
|
It is omitted when a \variable{} has is not functionally defined, or when a \parameter{} is a \prbpar{} and assigned externally.
|
|
The identifier used in a top-level variable declaration must be unique.
|
|
Two declarations with the same identifier result in an error during the \gls{rewriting} process.
|
|
|
|
The main types used in \minizinc{} are Boolean, integer, floating point numbers, sets of integers, and (user-defined) enumerated types.
|
|
The declaration of \parameters{} and \variables{} are distinguished through the instance of these types.
|
|
In the type instance of a \variable{}, the type is preceded by the \mzninline{var} keyword.
|
|
In the type instance of a \parameter{}, the type can similarly be marked by the \mzninline{par} keyword, but this is not required.
|
|
\minizinc{} allows collections of these types to be contained in \glspl{array}.
|
|
Unlike other languages, \glspl{array} have a user-defined index set, which can start at any value, but they have to be a continuous range.
|
|
For example, the following declaration declares an array going from 5 to 10 of new Boolean \variables{}.
|
|
|
|
\begin{mzn}
|
|
array[5..10] of var bool: bs;
|
|
\end{mzn}
|
|
|
|
The type in a declaration can, however, be more complex when the modeller uses a type expression.
|
|
These constrain a declaration, not just to a certain type, but also to a set of values.
|
|
This set of values is generally referred to as the \gls{domain} of a \variable{}.
|
|
In \minizinc{} any expression that has a set type can be used as a type expression.
|
|
For example, the following two declarations declare two integer \variables{}.
|
|
The first takes the values from three to five and the second takes the values one, three, and five.
|
|
|
|
\begin{mzn}
|
|
var 3..5: x;
|
|
var {1,3,5}: y;
|
|
\end{mzn}
|
|
|
|
If a declaration includes a functional definition, then the identifier is merely a name given to the expression.
|
|
However, if the declaration also includes a type expression, then this places an implicit \constraint{} on the expression.
|
|
It forces the result of the expression to take a value according to the type expression.
|
|
|
|
\paragraph{Constraint Items} \Constraints{} in a \minizinc{} model are specified using the syntax: \mzninline{constraint @\(E\)@}.
|
|
A \constraint{} item contains only a single expression \(E\) of Boolean type.
|
|
The \gls{rewriting} of the \instance{} translates the expressions in \constraints{} into \constraints{} and potentially additional \variables{} that are \gls{native} to the \solver{}.
|
|
It is important that the \gls{native} expressions are \gls{eqsat}.
|
|
This means that the \constraints{} in the \solver{} are \gls{satisfied} if-and-only-if the original \constraint{} would have been \gls{satisfied}.
|
|
|
|
\paragraph{Solving Goal Item} A \minizinc{} model can contain a single solving goal item.
|
|
This item signals the \solver{} to perform one of three actions:
|
|
|
|
\begin{description}
|
|
\item[\mzninline{solve satisfy}] to find an \gls{assignment} to the \variables{} that satisfies the \constraints{},
|
|
\item[\mzninline{solve minimize @\(E\)@}] to find an \gls{assignment} to the \variables{} that satisfies the \constraints{} and minimizes the value of the expression \(E\), or
|
|
\item[\mzninline{solve maximize @\(E\)@}] to similarly maximize the value of the expression \(E\).
|
|
\end{description}
|
|
|
|
\noindent{}The first type of goal indicates that the problem is a \gls{dec-prb}.
|
|
The other two types of goals are used when the model describes an \gls{opt-prb}.
|
|
If the model does not contain a goal item, then the problem is assumed to be a \gls{dec-prb}.
|
|
|
|
\paragraph{Function Items} Common structures in \minizinc\ are captured using function declarations.
|
|
A function is declared using the syntax \mzninline{function @\(T\)@: @\(I\)@(@\(P\)@) = @\(E\)@}, where:
|
|
\begin{itemize}
|
|
\item \(T\) is the type of its result;
|
|
\item \(I\) is its identifier;
|
|
\item \(P\) is a list types and identifiers for its arguments;
|
|
\item and \(E\) is the body of the function, an expression that can use the arguments \(P\).
|
|
\end{itemize}
|
|
|
|
\noindent{}\Gls{rewriting} replaces a call to a function by its body instantiated with the arguments given in the call.
|
|
The \minizinc{} language allows users to write the keywords \mzninline{predicate} as a shorthand \mzninline{function var bool} and \mzninline{test} as a shorthand for \mzninline{function bool}.
|
|
|
|
As an example, we can define a function that squares an integer as follows.
|
|
|
|
\begin{mzn}
|
|
function int: square(int: a) = a * a;
|
|
\end{mzn}
|
|
|
|
\Gls{rewriting} (eventually) transforms all \minizinc{} expressions into function calls.
|
|
As such, function declarations are the primary method for \solvers{} to specify how to rewrite a \minizinc{} model into a \gls{slv-mod}.
|
|
The collection of function declarations to rewrite for a \solver{} is generally referred to as a \solver{} library.
|
|
In this library, functions can be declared without a function body.
|
|
This marks them as \gls{native} to the \solver{}.
|
|
Calls to these functions are directly placed in the \gls{slv-mod}.
|
|
For non-\gls{native} functions, \solvers{} provide \glspl{decomp}: functions with a body that rewrites calls into (or towards) \gls{native} functions.
|
|
\Solver{} implementers are, however, not forced to provide a definition for all functions in \minizinc{}'s extensive library.
|
|
Instead, they can rely on a set of standard declarations, known as the standard library, that rewrite functions into a minimal subset of \gls{native} functions, known as the \flatzinc{} built-ins.
|
|
|
|
\subsection{MiniZinc Expressions}%
|
|
\label{subsec:back-mzn-expr}
|
|
|
|
One of the powers of the \minizinc{} language is the extensive expression language.
|
|
It helps modellers create \cmodels{} that are intuitive to read, but are transformed to fit the structure best suited to the chosen \solver{}.
|
|
We now briefly discuss the most important types of expression in \minizinc{} and the possible methods employed when \gls{rewriting} them.
|
|
|
|
\paragraph{Global Constraints} It could be said that the basic building blocks of the \minizinc{} language are \glspl{global}.
|
|
These expressions capture common (complex) relations between \variables{}.
|
|
\Glspl{global} in the \minizinc{} language are used as function calls.
|
|
The following is an example of a \gls{global}.
|
|
|
|
\begin{mzn}
|
|
predicate knapsack(
|
|
array [int] of int: w,
|
|
array [int] of int: p,
|
|
array [int] of var int: x,
|
|
var int: W,
|
|
var int: P,
|
|
);
|
|
\end{mzn}
|
|
|
|
This \gls{global} expresses the knapsack relation, with the following arguments:
|
|
\begin{itemize}
|
|
\item \mzninline{w} are the weights of the items,
|
|
\item \mzninline{p} are the profits for each item,
|
|
\item the \variables{} in \mzninline{x} represent how many of each item are present in the knapsack,
|
|
\item and \mzninline{W} and \mzninline{P}, respectively, represent the weight and profit of the knapsack
|
|
\end{itemize}
|
|
|
|
Note that using this \gls{global} as follows would have simplified the \minizinc{} model in \cref{ex:back-mzn-knapsack}.
|
|
|
|
\begin{mzn}
|
|
var 0..total_space: space_used;
|
|
constraint knapsack(toy_space, toy_joy, set2bool(selection), space_used, total_joy);
|
|
\end{mzn}
|
|
|
|
\noindent{}This has the additional benefit that the knapsack structure of the problem is then known.
|
|
The \constraint{} can be rewritten using a specialized \gls{decomp} provided by the \solver{}, or it can be marked as a \gls{native} \constraint{}.
|
|
|
|
Although \minizinc{} contains an extensive library of \glspl{global}, many problems contain structures that are not covered by a \gls{global}.
|
|
There are many other types of expressions in \minizinc{} that help modellers express complex \constraints{}.
|
|
|
|
\paragraph{Operators} When we express a mathematical formula, we generally do this through the use of \gls{operator} symbols.
|
|
\minizinc{} includes \glspl{operator} for many mathematical, logic, and set operations.
|
|
Consider, for example, the following \constraint{}.
|
|
|
|
\begin{mzn}
|
|
constraint not (a + b < c);
|
|
\end{mzn}
|
|
|
|
\noindent{}It contains the infix \glspl{operator} \mzninline{+} and \mzninline{<}, and the prefix \gls{operator} \mzninline{not}.
|
|
|
|
\Gls{operator} symbols in \minizinc{} are a shorthand for \minizinc{} functions.
|
|
The \glspl{operator} in the above expression are evaluated using the addition, less-than comparison, and Boolean negation functions respectively.
|
|
Although the \gls{operator} syntax for \variables{} and \parameters{} is the same, different (overloaded) versions of these functions are used during \gls{rewriting}.
|
|
If the arguments to a function consist of only \parameters{}, then the result of the function can be computed directly.
|
|
However, \gls{rewriting} functions with \variable{} as arguments results in a new \variable{} that is constrained to the result of the function.
|
|
|
|
\paragraph{Conditional Expression} The choice between different expressions is often expressed using a \gls{conditional} expression, also known as an ``if-then-else'' expression.
|
|
It can, for example, force that the absolute value of \mzninline{a} is larger than \mzninline{b} using the \constraint{} as follows.
|
|
|
|
\begin{mzn}
|
|
constraint if a >= 0 then a > b else b < a endif;
|
|
\end{mzn}
|
|
|
|
The result of a \gls{conditional} expression is not limited to Boolean types.
|
|
The condition in the expression, the ``if'', must be of a Boolean type, but as long as the different sides of the \gls{conditional} expression are the same type it is a valid \gls{conditional} expression.
|
|
This can be used to, for example, define an absolute value function for integer \parameters{} as follows.
|
|
|
|
\begin{mzn}
|
|
function int: abs(int: a) =
|
|
if a >= 0 then a else -a endif;
|
|
\end{mzn}
|
|
|
|
When the condition does not contain any \variables{}, then the \gls{rewriting} process merely has to choose the correct side of the expression.
|
|
If, however, the condition does contain a \variable{}, then the result of the expression cannot be defined during \gls{rewriting}.
|
|
Instead, an \gls{avar} is created and a \constraint{} is added to ensure that it takes the correct value.
|
|
A special \mzninline{if_then_else} \glspl{global} is available to implement this relation.
|
|
|
|
\paragraph{Array Access} For the selection of an element from an \gls{array} the \minizinc{} language uses an \gls{array} access syntax similar to most other computer languages.
|
|
The expression \mzninline{a[i]} selects the element with index \mzninline{i} from the \gls{array} \mzninline{a}.
|
|
Note this is not necessarily the \(\mzninline{i}^{\text{th}}\) element because \minizinc{} allows modellers to provide a custom index set.
|
|
|
|
The selection of an element from an \gls{array} is in many ways similar to the choice in a \gls{conditional} expression.
|
|
Like a \gls{conditional} expression, the selector \mzninline{i} can be both a \parameter{} or a \variable{}.
|
|
If the expression is a \gls{variable}, then the expression is rewritten to an \gls{avar} and an \mzninline{element} \constraint{}.
|
|
Otherwise, the \gls{rewriting} replaces the \gls{array} access expression by the chosen element of the \gls{array}.
|
|
|
|
\paragraph{Comprehensions} \Gls{array} \glspl{comprehension} are expressions which allows modellers to construct \gls{array} objects.
|
|
The generation of new \gls{array} structures allows modellers adjust, combine, filter, or order values from within the \minizinc{} model.
|
|
|
|
\Gls{comprehension} expressions \mzninline{[@\(E\)@ | @\(G\)@ where @\(F\)@]} consist of three parts:
|
|
|
|
\begin{description}
|
|
\item[\(G\)] The \gls{generator} expression which assigns the values of collections to identifiers,
|
|
\item[\(F\)] an optional filtering condition, which decided whether the iteration to be included in the \gls{array},
|
|
\item[\(E\)] and the expression that is evaluated for each iteration when the filtering condition succeeds.
|
|
\end{description}
|
|
|
|
The following example constructs an \gls{array} that contains the tripled even values of an \gls{array} \mzninline{x}.
|
|
|
|
\begin{mzn}
|
|
[ xi * 3 | xi in x where x mod 2 == 0]
|
|
\end{mzn}
|
|
|
|
During \gls{rewriting}, the instantiation of the expression with current generator values is added to the new \gls{array}.
|
|
This means that the type of the \gls{array} primarily depends on the type of the expression.
|
|
However, in recent versions of \minizinc{} both the collections over which we iterate and the filtering condition could have a \variable{} type.
|
|
Since we then cannot decide during \gls{rewriting} if an element is present in the \gls{array}, the elements are made of an \gls{optional} type.
|
|
This means that the \solver{} will decide if the element is present in the \gls{array} or if it takes a special ``absent'' value (\mzninline{<>}).
|
|
|
|
\paragraph{Let Expressions} Together with function definitions, \glspl{let} are the primary scoping mechanism in the \minizinc{} language.
|
|
A \gls{let} allows a modeller to provide a list of declarations, that can only be used in the body of the \gls{let}.
|
|
Additionally, a \gls{let} can contain \constraints{} to constrain the declared values.
|
|
There are three main purposes for \glspl{let}.
|
|
|
|
\begin{enumerate}
|
|
|
|
\item It can name an intermediate expression, for it to be used multiple times or to simplify the expression.
|
|
For example, the following \constraint{} constrains that half of \mzninline{x} is even or takes the value one.
|
|
|
|
\begin{mzn}
|
|
constraint let { var int: tmp = x div 2; } in tmp mod 2 == 0 \/ tmp = 1;
|
|
\end{mzn}
|
|
|
|
\item It can introduce a scoped \variable{}.
|
|
For example, the following \constraint{} constrains that \mzninline{x} and \mzninline{y} are at most two apart.
|
|
|
|
\begin{mzn}
|
|
constraint let {var -2..2: slack;} in x + slack = y;
|
|
\end{mzn}
|
|
|
|
|
|
\item It can constrain the resulting expression.
|
|
For example, the following function returns an \gls{avar} \mzninline{z} that is constrained to be the multiplication of \mzninline{x} and \mzninline{y} by the relational multiplication \constraint{} \mzninline{pred_int_times}.
|
|
|
|
\begin{mzn}
|
|
function var int: int_times(var int: x, var int: y) =
|
|
let {
|
|
var int: z;
|
|
constraint pred_int_times(x, y, z);
|
|
} in z;
|
|
\end{mzn}
|
|
|
|
\end{enumerate}
|
|
|
|
An important detail in \gls{rewriting} \glspl{let} is that any \variables{} that are introduced may need to be renamed in the resulting \gls{slv-mod}.
|
|
Different to variables declared directly in declaration items, the variables declared in \glspl{let} can be evaluated multiple times when used in loops, function definitions (that are called multiple times), and \gls{array} \glspl{comprehension}.
|
|
The \gls{rewriting} process must assign any variables in the \gls{let} a new name and use this name in any subsequent definitions and in the resulting expression.
|
|
|
|
\paragraph{Annotations} Although \minizinc{} does not typically prescribe a way to find the \gls{sol} for an \instance{}, it provides the modeller with a way to give ``hints'' to the \solver{}.
|
|
It does this through the use of \glspl{annotation}.
|
|
Any item or expression can be annotated.
|
|
An \gls{annotation} is indicated by the \mzninline{::} \gls{operator} followed by an identifier or function call.
|
|
The same syntax can be repeated to place multiple \glspl{annotation} on the same item or expression.
|
|
|
|
A common use of \glspl{annotation} in \minizinc{} is to provide a \gls{search-heuristic}.
|
|
Through the use of an \gls{annotation} on the solving goal item, the modeller can express an order in which they think values should be tried for an arrangement of \variables{} in the model.
|
|
|
|
\subsection{Reification}%
|
|
\label{subsec:back-reif}
|
|
|
|
With the rich expression language in \minizinc{}, \constraints{} can consist of complex expressions that cannot be rewritten into a single \constraint{} in the \gls{slv-mod}.
|
|
The sub-expressions of a complex expressions are often rewritten into \glspl{avar}.
|
|
If the sub-expression, and therefore \gls{avar}, is of Boolean type, then it needs to be rewritten into a \gls{reified} \constraint{}.
|
|
The \gls{reif} of a \constraint{} \(c\) creates a Boolean \gls{avar} \mzninline{b}, also referred to as its \gls{cvar}, constrained to be the truth-value of this \constraint{}: \(\texttt{b} \leftrightarrow{} c\).
|
|
|
|
\begin{example}
|
|
Consider the following \minizinc{} model.
|
|
|
|
\begin{mzn}
|
|
array[1..10] of var 1..15: x;
|
|
constraint all_different(x);
|
|
solve maximize sum(i in 1..10) (x[i] mod 2 = 0);
|
|
\end{mzn}
|
|
|
|
This model maximizes the number of even numbers taken by the elements of the \gls{array} \mzninline{x}.
|
|
In this model the expression \mzninline{x[i] mod 2 = 0} has to be \gls{reified}.
|
|
This means that for each \mzninline{i}, a \gls{cvar} \mzninline{b_i} is added, together with a constraint that makes \mzninline{b_i} take the value \true{} if-and-only-if \mzninline{x[i] mod 2 = 0}.
|
|
We can then add up the values of all these \mzninline{b_i}, as required by the maximization.
|
|
Since the elements have a domain from 1 to 15 and are constrained to take different values, not all elements of \mzninline{x} can take even values.
|
|
Instead, the \solver{} is tasked to maximize the number of \glspl{cvar} it sets to \true{}.
|
|
\end{example}
|
|
|
|
When an expression occurs in a position where it can be directly enforced, we say it occurs in \rootc{}.
|
|
Contrarily, an expression that occurs in non-\rootc{} is reified during the \gls{rewriting} process.
|
|
In \minizinc{}, almost all expressions can be used in both \rootc{} and non-\rootc{} contexts.
|
|
|
|
\subsection{Handling Undefined Expressions}%
|
|
\label{subsec:back-mzn-partial}
|
|
|
|
In this subsection we discuss the handling of \gls{partial} functions in \cmls{} as studied by \textcite{frisch-2009-undefinedness}.
|
|
|
|
When a function has a well-defined result for all its possible inputs it is said to be total.
|
|
Some expressions in \cmls{}, however, are rewritten into functions that do not have well-defined results for all possible inputs.
|
|
Part of the semantics of a \cml{} is the choice as to how to treat these \gls{partial} functions.
|
|
|
|
\begin{example}\label{ex:back-undef}
|
|
Consider, for example, the following ``complex \constraint{}''.
|
|
|
|
\begin{mzn}
|
|
constraint i <= 4 -> a[i] * x >= 6;
|
|
\end{mzn}
|
|
|
|
\noindent{}It requires that if \mzninline{i} takes a value less or equal to four, then the value in the \texttt{i}\(^{th}\) position of \gls{array} \mzninline{a} multiplied by \mzninline{x} must be at least six.
|
|
|
|
Suppose the \gls{array} \texttt{a} has index set \mzninline{1..5}, but \mzninline{i} takes the value seven.
|
|
This means the expression \mzninline{a[i]} is undefined.
|
|
If \minizinc{} did not implement any special handling for \gls{partial} functions, then the whole expression would have to be marked as undefined and a \gls{sol} cannot be found.
|
|
However, intuitively if \mzninline{i = 7} the \constraint{} should be trivially \gls{satisfied}.
|
|
\end{example}
|
|
|
|
Other examples of \gls{partial} functions in \minizinc{} are:
|
|
|
|
\begin{itemize}
|
|
\item division (or modulus) when the divisor is zero,
|
|
|
|
\begin{mzn}
|
|
x div 0 = @??@
|
|
\end{mzn}
|
|
|
|
\item finding the minimum or maximum of an empty set, and
|
|
|
|
\begin{mzn}
|
|
min({}) = @??@
|
|
\end{mzn}
|
|
|
|
\item computing the square root of a negative value.
|
|
|
|
\begin{mzn}
|
|
sqrt(-1) = @??@
|
|
\end{mzn}
|
|
|
|
\end{itemize}
|
|
|
|
The existence of undefined expressions can cause confusion in \cmls{}.
|
|
There is both the question of what happens when an undefined expression is evaluated and at what point during the process undefined values are resolved, during \gls{rewriting} or by the \solver{}.
|
|
|
|
\textcite{frisch-2009-undefinedness} define three semantic models to deal with the undefinedness in \cmls{}.
|
|
|
|
\begin{description}
|
|
|
|
\item[Strict] \Cmls{} employing a \gls{strict-sem} do not allow any undefined behaviour during the evaluation of the \cmodel{}.
|
|
If during the \gls{rewriting} or solving process an expression is found to be undefined, then any expressions in which it is used is also marked as undefined.
|
|
Consequently, this means that the occurrence of a single undefined expression causes the full \instance{} to be undefined.
|
|
|
|
\item[Kleene] The \Gls{kleene-sem} treat undefined expressions as expressions for which not enough information is available.
|
|
If an expression contains an undefined sub-expression, then it is only marked as undefined if the value of the sub-expression is required to compute its result.
|
|
Take for example the expression \mzninline{false -> @\(E\)@}.
|
|
When \(E\) is undefined the result of the expression is still be said to be \true{}, since the value of \(E\) does not influence the result of the expression.
|
|
However, if we take the expression \mzninline{true /\ @\(E\)@}, then when \(E\) is undefined the overall expression is also undefined since the value of the expression cannot be determined.
|
|
|
|
\item[Relational] The \gls{rel-sem} follow from all expressions in a \cml{} eventually becoming part of a relational \constraint{}.
|
|
So even though a (functional) expression does not have a well-defined result, we can still decide whether its surrounding relation holds.
|
|
For example, the expression \mzninline{x div 0} is undefined, but the relation \mzninline{int_div(x, 0, y)} is \false{}.
|
|
Values for \mzninline{x} and \mzninline{y} such that the relation holds do not exist.
|
|
It can be said that \gls{rel-sem} make the closest relational expression that contains an undefined expression \false{}.
|
|
|
|
\end{description}
|
|
|
|
In practice, it is often natural to guard against undefined behaviour using Boolean logic.
|
|
\Gls{rel-sem} therefore often feel the most natural for the users of \glspl{cml}.
|
|
This is why \minizinc{} uses \gls{rel-sem} during its evaluation.
|
|
|
|
If we, for example, reconsider the \constraint{} from \cref{ex:back-undef}, we will find that \gls{rel-sem} will correspond to the intuitive expectation.
|
|
When \mzninline{i} takes the value seven, the expression \mzninline{a[i]} is undefined.
|
|
Its closest Boolean context will take the value \false{}.
|
|
In this case, that means the right-hand side of the implication becomes \false{}.
|
|
However, since the left-hand side of the implication is also \false{}, the \constraint{} is \gls{satisfied}.
|
|
|
|
\textcite{frisch-2009-undefinedness} also show that different \solvers{} often employ different semantics.
|
|
\minizinc{} therefore implements the \gls{rel-sem} by translating any potentially undefined expression into an expression that is always defined, by introducing appropriate \glspl{avar} and \gls{reified} \constraints{}.
|
|
That way, the solving result is independent of the semantics of the chosen \solver{}, and always consistent with the \gls{rel-sem} of the model.
|
|
|
|
\section{Solving Constraint Models}%
|
|
\label{sec:back-solving}
|
|
|
|
There are many prominent techniques to solve a \constraint{} model, but none of them solve \minizinc{} instances directly.
|
|
Instead, a \minizinc{} instance is rewritten into a \gls{slv-mod} containing only \constraints{} and types of \variables{} that are \gls{native} to the \solver{}.
|
|
|
|
\minizinc{} was initially designed as an input language for \gls{cp} \solvers{}.
|
|
These \glspl{solver} often directly support many of the \glspl{global} in a \minizinc{} model.
|
|
For \gls{cp} solvers the amount of \gls{rewriting} required varies a lot.
|
|
It depends on which \constraints{} are \gls{native} to the targeted \gls{cp} \solver{} and which \constraints{} have to be decomposed.
|
|
|
|
In some ways \gls{cp} \solvers{} work on a similar level as the \minizinc{} language.
|
|
Therefore, some techniques used in \gls{cp} \solvers{} are also used during the \gls{rewriting} process.
|
|
The usage of these techniques shrinks the \domains{} of \variables{} and eliminates or simplifies \constraints{}.
|
|
\Cref{subsec:back-cp} explains the general method employed by \gls{cp} \solvers{} to solve a \cmodel{}.
|
|
|
|
Throughout the years \minizinc{} has started targeting \solvers{} using different approaches.
|
|
Although these \solvers{} allow new classes of problems to be solved using \minizinc{}, they also bring new challenges to the \gls{rewriting} process.
|
|
To understand these \gls{rewriting} challenges, the remainder of this section discusses the other dominant technologies used by \minizinc{} \solvers{} and their \glspl{slv-mod}.
|
|
|
|
\subsection{Constraint Programming}%
|
|
\label{subsec:back-cp}
|
|
\glsreset{cp}
|
|
|
|
When given an \instance{} of a \cmodel{}, one may wonder how to find a \gls{sol}.
|
|
The simplest algorithm would be to apply ``brute force'': try every value in the \domains{} of all \variables{}.
|
|
This is an inefficient approach.
|
|
Consider, for example, the following \constraint{}.
|
|
|
|
\begin{mzn}
|
|
constraint a + b = 5;
|
|
\end{mzn}
|
|
|
|
It is clear that when the value \mzninline{a} is known, then the value of \mzninline{b} can be deduced.
|
|
Therefore, only that one value for \mzninline{b} has to be tried.
|
|
\gls{cp} is the idea of solving \glspl{dec-prb} by performing an intelligent search by inferring which values are still feasible for each \variable{} \autocite{rossi-2006-cp}.
|
|
|
|
A \gls{cp} \solver{} performs a depth first search.
|
|
Using a mechanism called \gls{propagation} the \solver{} removes values from \domains{} that are impossible.
|
|
\Gls{propagation} works through the use of \glspl{propagator}: algorithms dedicated to a specific \constraint{} that prune \domains{} when they contain values that are proven to be inconsistent.
|
|
This mechanism can be very efficient because a \gls{propagator} only has to be run again if the \domain{} of one of its \variables{} has changed.
|
|
If a \gls{propagator} can prove that it is always \gls{satisfied}, then it is subsumed: it never has to be run again.
|
|
|
|
In the best case scenario, when \gls{propagation} eliminates values, all \variables{} are \gls{fixed} to a single value.
|
|
In this case we have arrived at a \gls{sol}.
|
|
Often, \gls{propagation} alone is not enough to find a \gls{sol}.
|
|
When we reach a \gls{fixpoint}, where \gls{propagation} cannot reduce any \domains{}, and a \gls{sol} is not found, the \solver{} then has to make a search decision.
|
|
It fixes a \variable{} to a value or adds a new \constraint{}.
|
|
This search decision is an assumption made by the \solver{} in the hope of finding a \gls{sol}.
|
|
If a \gls{sol} is not found using the search decision, then it needs to try making the opposite decision which requires the exclusion of the chosen value or adding the opposite \constraint{}.
|
|
|
|
Note that there is an important difference between values excluded by \gls{propagation} and making search decisions.
|
|
Values excluded by propagation are guaranteed to not occur in any \gls{sol}, whereas, values excluded by a search heuristic are merely removed locally and can still be part of a \gls{sol}.
|
|
A \gls{cp} \solver{} is only able to prove that the \instance{} is \gls{unsat} by trying all possible search decisions.
|
|
|
|
\Gls{propagation} is not only used when starting the search, but also after making each search decision.
|
|
This means that some \gls{propagation} depends on the search decision.
|
|
Therefore, if the \solver{} needs to reconsider a search decision, then it must also undo all \domain{} changes that were caused by \gls{propagation} dependent on that search decision.
|
|
|
|
The most common method in \gls{cp} \solvers{} to keep track of \gls{domain} changes uses a \gls{trail} data structure \autocite{warren-1983-wam}.
|
|
Every \domain{} change that is made during \gls{propagation}, after the first search decision, is stored in a list.
|
|
Whenever a new search decision is made, the current position of the list is tagged.
|
|
If the \solver{} now needs to undo a search decision (i.e., \gls{backtrack}), it reverses all changes until it reaches the change that is tagged with the search decision.
|
|
Since all changes before the tagged point on the \gls{trail} were made before the search decision was made, it is guaranteed that these \domain{} changes do not depend on the search decision.
|
|
Furthermore, because \gls{propagation} is performed to a \gls{fixpoint}, \gls{propagation} is never duplicated.
|
|
|
|
The solving method used by \gls{cp} \solvers{} is very flexible.
|
|
A \solver{} can support many types of \variables{}: they can range from Boolean, floating point numbers, and integers, to intervals, sets, and functions.
|
|
Similarly, \solvers{} do not all have implementations for the same \glspl{propagator}.
|
|
Therefore, a \gls{slv-mod} for one \solver{} looks very different from an \gls{eqsat} \gls{slv-mod} for a different \solver{}.
|
|
\Cmls{}, like \minizinc{}, serve as a standardized form of input for these \solvers{}.
|
|
They allow modellers to always use \glspl{global} and depending on the \solver{} they are either used directly, or they are rewritten using a \gls{decomp}.
|
|
|
|
\begin{example}%
|
|
\label{ex:back-nqueens}
|
|
As an example of the \gls{propagation} mechanism, let us consider the N-Queens problem.
|
|
Given a chessboard of size \(n \times n\), find a placement for \(n\) queen chess pieces such that the queens cannot attack each other.
|
|
This means we can only place one queen per row, one queen per column, and one queen per diagonal.
|
|
The problem can be modelled in \minizinc{} as follows.
|
|
|
|
\begin{mzn}
|
|
int: n;
|
|
set of int: ROW = 1..n;
|
|
set of int: COL = 1..n;
|
|
array [COL] of var ROW: q;
|
|
|
|
constraint all_different(q);
|
|
constraint all_different([q[i] + i | i in COL]);
|
|
constraint all_different([q[i] - i | i in COL]);
|
|
\end{mzn}
|
|
|
|
The \variables{} in the \gls{array} \mzninline{q} decide for each column in which row the queen is placed.
|
|
This restricts every column to only have a single queen.
|
|
As such, the queen will not be able to attack vertically.
|
|
The \constraints{} model the remaining rules of the problem: two queens cannot be placed in the same row, two queens cannot be placed in the same upward diagonal, and two queens cannot be placed in the same downward diagonal.
|
|
For many \gls{cp} \solvers{} this model is close to a \gls{slv-mod}, since integer \variables{} and an \mzninline{all_different} \gls{propagator} are common.
|
|
|
|
When solving the problem, initially we cannot eliminate any values from the \glspl{domain} of the \variables{}.
|
|
The first \gls{propagation} happens when the first queen is placed on the board, the first search decision.
|
|
|
|
\Cref{fig:back-nqueens} visualizes the \gls{propagation} after placing a queen on the d3 square of an eight by eight chessboard.
|
|
When the queen it placed on the board in \cref{sfig:back-nqueens-1}, it fixes the value of column 4 (d) to the value 3. This implicitly eliminates any possibility of placing another queen in the column.
|
|
Fixing the \domain{} of the column triggers the \glspl{propagator} of the \mzninline{all_different} \constraints{}.
|
|
As shown in \cref{sfig:back-nqueens-2}, the first \mzninline{all_different} \constraint{} now stops other queens from being placed in the same column.
|
|
It eliminates the value 3 from the domains of the queens in the remaining columns.
|
|
Similarly, the other \mzninline{all_different} \constraints{} remove all values that correspond to positions on the same diagonal as the placed queen, shown in \cref{sfig:back-nqueens-3,sfig:back-nqueens-4}.
|
|
|
|
The \gls{propagation} after the first placed queen severely limits the positions where a second queen can be placed.
|
|
\end{example}
|
|
|
|
\begin{figure}
|
|
\centering
|
|
\begin{subfigure}[b]{.48\columnwidth}
|
|
\centering
|
|
\includegraphics{assets/img/back_chess1}
|
|
\caption{\label{sfig:back-nqueens-1} Assign a queen to d3.}
|
|
\end{subfigure}%
|
|
\hspace{0.04\columnwidth}%
|
|
\begin{subfigure}[b]{.48\columnwidth}
|
|
\centering
|
|
\includegraphics{assets/img/back_chess2}
|
|
\caption{\label{sfig:back-nqueens-2} Propagate the rows.}
|
|
\end{subfigure}
|
|
\begin{subfigure}[b]{.48\columnwidth}
|
|
\centering
|
|
\includegraphics{assets/img/back_chess3}
|
|
\caption{\label{sfig:back-nqueens-3} Propagate the upwards diagonal.}
|
|
\end{subfigure}%
|
|
\hspace{0.04\columnwidth}%
|
|
\begin{subfigure}[b]{.48\columnwidth}
|
|
\centering
|
|
\includegraphics{assets/img/back_chess4}
|
|
\caption{\label{sfig:back-nqueens-4} Propagate the downward diagonal.}
|
|
\end{subfigure}
|
|
\caption{\label{fig:back-nqueens} An example of domain propagation when a queen gets assigned in the N-Queens problem.}
|
|
\end{figure}
|
|
|
|
In \gls{cp} solving there is a trade-off between the amount of time spent propagating a \constraint{} and the amount of search that is otherwise required.
|
|
The gold standard for a \gls{propagator} is to be \gls{domain-con}.
|
|
A \gls{domain-con} \gls{propagator} leaves only the values in a \domain{} for which there is least one possible \gls{variable-assignment} that satisfies its \constraint{}.
|
|
Designing such a \gls{propagator} is, however, not an easy task.
|
|
The algorithm can require high computational complexity.
|
|
Instead, it is sometimes better to use a \gls{propagator} with a lower level of consistency.
|
|
Although it does not eliminate all possible values of the domain, searching the values that are not eliminated may take less time than achieving domain consistency.
|
|
|
|
This is, for example, the case for integer linear \constraints{} \[ \sum_{i} c_{i} x_{i} = d\] where \(c_{i}\) and \(d\) are integer \parameters{} and \(x_{i}\) are integer \variable{}.
|
|
For these \constraints{}, a realistic \gls{domain-con} \gls{propagator} cannot exist because the problem is \glsxtrshort{np}-hard \autocite{choi-2006-fin-cons}.
|
|
A more feasible problem is to find the minimal and maximal values, or \gls{bounds}, for the \variables{} had they been rational numbers.
|
|
A \gls{boundsr-con} \gls{propagator} then ensures that the values in the \domain{} of the integer \variables{} are between their rational \gls{bounds}.
|
|
Note that this is a relaxation of calculating the integer \gls{bounds}, to create a \gls{boundsz-con} \gls{propagator}, which is still \glsxtrshort{np}-hard.
|
|
We will see the same relaxation in mathematical programming, discussed in the next section.
|
|
|
|
Thus far, we have only considered finding \glspl{sol} for \glspl{dec-prb}.
|
|
\gls{cp} solving can, however, also be used to solve \glspl{opt-prb} using a method called \gls{bnb}.
|
|
The \gls{cp} \solver{} follows the same method as previously described.
|
|
However, when it finds a \gls{sol}, it does not yet know if this \gls{sol} is an \gls{opt-sol}.
|
|
It is merely an incumbent \gls{sol}.
|
|
The \solver{} must therefore resume its search, but it is not interested in just any \gls{sol}, only \glspl{sol} for which the \gls{objective} returns a better value.
|
|
This is achieved by adding a new \constraint{} that enforces a better objective value than the incumbent \gls{sol}.
|
|
If the search process finds another \gls{sol}, then the incumbent \gls{sol} is updated and the search process continues.
|
|
If the search process does not find any other \glspl{sol}, then it is proven that a better \gls{sol} than the current incumbent \gls{sol} cannot exist.
|
|
It is an \gls{opt-sol}.
|
|
|
|
\gls{cp} solvers like \gls{chuffed} \autocite{chu-2011-chuffed}, Choco \autocite{prudhomme-2016-choco}, \gls{gecode} \autocite{schulte-2019-gecode}, and OR-Tools \autocite{perron-2021-ortools} have long been one of the leading methods to solve \minizinc\ instances.
|
|
|
|
\subsection{Mathematical Programming}%
|
|
\label{subsec:back-mip}
|
|
\glsreset{lp}
|
|
\glsreset{mip}
|
|
|
|
Mathematical programming \solvers{} are the most prominent solving technique employed in \gls{or} \autocite{schrijver-1998-mip}.
|
|
At its foundation lies \gls{lp}.
|
|
A linear program describes a problem using \constraints{} in the form of linear (in\nobreakdash)equations over continuous \variables{}.
|
|
In general, a linear program can be expressed in the following form.
|
|
|
|
\begin{align*}
|
|
\text{maximize} \hspace{2em} & \sum_{j=1}^{V} c_{j} x_{j} & \\
|
|
\text{subject to} \hspace{2em} & l_{i} \leq \sum_{j=0}^{V} a_{ij} x_{j} \leq u_{i} & \forall_{1 \leq{} i \leq{} C} \\
|
|
& x_{j} \in \mathbb{R} & \forall_{1 \leq{} j \leq{} V}
|
|
\end{align*}
|
|
|
|
In this definition \(V\) and \(C\) represent the number of \variables{} and number of \constraints{} respectively.
|
|
The vector \(c\) holds the coefficients of the objective function and the matrix \(a\) holds the coefficients for the \constraints{}.
|
|
The vectors \(l\) and \(u\) respectively contain the lower and upper \gls{bounds} of the \constraints{}.
|
|
Finally, the \variables{} of the linear program are held in the \(x\) vector.
|
|
|
|
For problems that are in the form of a linear program, there are proven methods to find an \gls{opt-sol}.
|
|
One such method, the simplex method, was first conceived by \textcite{dantzig-1955-simplex} after the second world war.
|
|
It finds an \gls{opt-sol} of a linear program in worst-case exponential time.
|
|
It was questioned whether the same problem could be solved in worst-case polynomial time, until \textcite{karmarkar-1984-interior-point} proved this possible through the use of interior point methods.
|
|
|
|
Methods for solving linear programs provide the foundation for a harder problem.
|
|
In \gls{lp} our \variables{} must be continuous.
|
|
If we require that one or more take an integer value (\(x_{i} \in \mathbb{Z}\)), then the problem becomes \glsxtrshort{np}-hard.
|
|
The problem is referred to as \gls{mip} (or Integer Programming if \textbf{all} \variables{} must take an integer value).
|
|
|
|
Unlike \gls{lp}, there is not an algorithm that solves a mixed integer program in polynomial time.
|
|
We can, however, adapt \gls{lp} solving methods to solve a mixed integer program.
|
|
We do this by treating the mixed integer program as a linear program and find an \gls{opt-sol}.
|
|
If the \variables{} happen to take integer values in the \gls{sol}, then we have found an \gls{opt-sol} to the mixed integer program.
|
|
Otherwise, we pick one of the \variables{} that needs to be integer but whose value in the \gls{sol} of the linear program is not.
|
|
For this \variable{} we create two versions of the linear program: a version where this \variable{} is constrained to be less or equal to the value in the \gls{sol} rounded down to the nearest integer value; and a version where it is constrained to be greater or equal to the value in the \gls{sol} rounded up.
|
|
Both versions are solved to find the best \gls{sol}.
|
|
The process is repeated recursively until an integer \gls{sol} is found.
|
|
|
|
Much of the power of this solving method comes from \gls{bounds} that are inferred during the process.
|
|
The \gls{sol} to the linear program provides an upper bound for the \gls{sol} in the current step of the solving process.
|
|
Similarly, any integer \gls{sol} found in an earlier branch of the search process provides a lower bound.
|
|
When the upper bound given by the linear program is lower that the lower bound from an earlier \gls{sol}, then we know that any integer \gls{sol} following from the linear program is strictly worse than the incumbent.
|
|
|
|
Over the years \gls{lp} and \gls{mip} \solvers{} have developed immensely.
|
|
Modern \solvers{}, such as \gls{cbc} \autocite{forrest-2020-cbc}, \gls{cplex} \autocite{ibm-2020-cplex}, \gls{gurobi} \autocite{gurobi-2021-gurobi}, and \gls{scip} \autocite{gamrath-2020-scip}, are routinely used to solve very large industrial optimization problems.
|
|
These \solvers{} are therefore prime targets to solve \minizinc{} \instances{}.
|
|
|
|
To solve an \instance{} of a \cmodel{}, it can be rewritten into a mixed integer program.
|
|
This process is known as \gls{linearization}.
|
|
It does, however, come with its challenges.
|
|
Many \minizinc{} models contain \constraint{} that are not linear (in\nobreakdash)equations.
|
|
The translation of a single such \constraint{} can introduce many linear \constraints{} and \glspl{avar}.
|
|
For example, when a \constraint{} reasons about the value that a variable takes, the \gls{linearization} process introduces indicator variables.
|
|
An indicator variable \(y_{i}\) is a \gls{avar} that for a \variable{} \(x\) take the value 1 if \(x = i\) and 0 otherwise.
|
|
\Constraints{} reasoning about the value of \(x\) are then rewritten as linear \constraints{} using the \variables{} \(y_{i}\).
|
|
|
|
\begin{example}
|
|
Let us again consider the N-Queens problem from \cref{ex:back-nqueens}.
|
|
The following model shows an integer program of this problem.
|
|
|
|
\begin{align}
|
|
\text{given} \hspace{2em} & N = {1,\ldots,n} & \notag{}\\
|
|
\text{maximize} \hspace{2em} & 0 & \notag{}\\
|
|
\text{subject to} \hspace{2em} & q_{i} \in N & \forall_{i \in N} \notag{}\\
|
|
& y_{ij} \in \{0,1\} & \forall_{i,j \in N} \notag{}\\
|
|
\label{line:back-mip-channel} & x_{i} = \sum_{j \in N} j * y_{ij} & \forall_{i \in N} \\
|
|
\label{line:back-mip-row} & \sum_{i \in N} y_{ij} \leq 1 & \forall_{j \in N} \\
|
|
\label{line:back-mip-diag1} & \sum_{i,j \in N: i + j =k} y_{ij} \leq 1 & \forall_{3 \leq{} k \leq{} 2n-1} \\
|
|
\label{line:back-mip-diag2} & \sum_{i,j \in N: i - j =k} y_{ij} \leq 1 & \forall_{-n+2 \leq{} k \leq{} n-2}
|
|
\end{align}
|
|
|
|
As we can see, this \cmodel{} only uses integer \variables{} and linear \constraints{}.
|
|
Like the \minizinc{} model, \variables{} \(q\) are used to represent where the queen is located in each column.
|
|
To encode the \mzninline{all_different} \constraints{}, the indicator variables \(y\) are inserted to reason about the value of \(q\).
|
|
The \cref{line:back-mip-channel} is used to connect the \(q\) and \(y\) \variables{} and make sure that their values correspond.
|
|
\Cref{line:back-mip-row} ensures that only one queen is placed in the same column.
|
|
Finally, \cref{line:back-mip-diag1,line:back-mip-diag2} constrain all diagonals to contain only one queen.
|
|
\end{example}
|
|
|
|
\subsection{Boolean Satisfiability}%
|
|
\label{subsec:back-sat}
|
|
\glsreset{sat}
|
|
\glsreset{maxsat}
|
|
|
|
The study of the \gls{sat} problem is one of the oldest in computer science.
|
|
The DPLL algorithm that is still the basis for modern \gls{sat} solving stems from the 1960s \autocite{davis-1960-dpll, davis-1962-dpll}, and \gls{sat} was the first problem to be proven to be \glsxtrshort{np}-complete \autocite{cook-1971-sat}.
|
|
The problem asks if there is an \gls{assignment} for the \variables{} of a given Boolean formula, such that the formula is \gls{satisfied}.
|
|
This problem is a restriction of the general \gls{dec-prb} where all \variables{} have a Boolean type and all \constraints{} are simple Boolean formulas.
|
|
|
|
There is a field of research dedicated to solving \gls{sat} problems \autocite{biere-2021-sat}.
|
|
In this field a \gls{sat} problem is generally standardized to be in \gls{cnf}.
|
|
A \gls{cnf} is formulated in terms of literals.
|
|
These are Boolean \variables{} \(x\) or their negations \(\neg x\).
|
|
These literals are then used in a conjunction of disjunctive clauses: a Boolean formula in the form \(\forall \exists b_{i}\).
|
|
To solve the \gls{sat} problem, the \solver{} has to find an \gls{assignment} for the \variables{} where at least one literal takes the value \true{} in every clause.
|
|
|
|
Even though the problem is proven to be hard to solve, much progress has been made towards solving even very complex \gls{sat} problems.
|
|
Modern day \gls{sat} solvers, like Clasp \autocite{gebser-2012-clasp}, Kissat \autocite{biere-2021-kissat} and MiniSAT \autocite{een-2003-minisat}, solve instances of the problem with thousands of \variables{} and clauses.
|
|
|
|
Many real world problems modelled using \cmls{} directly correspond to \gls{sat}.
|
|
However, even problems that contain \variables{} with types other than Boolean can still be encoded as a \gls{sat} problem.
|
|
This process is known as \gls{booleanization}.
|
|
Depending on the problem, using a \gls{sat} \solvers{} to solve the encoded problem can still be the most efficient way to solve the problem.
|
|
|
|
\begin{example}
|
|
Let us once more consider the N-Queens problem presented in \cref{ex:back-nqueens}.
|
|
A possible \gls{sat} encoding for this problem is the following.
|
|
|
|
\begin{align}
|
|
\text{given} \hspace{2em} & N = {1,\ldots,n} & \notag{}\\
|
|
\text{find} \hspace{2em} & q_{ij} \in \{\text{true}, \text{false}\} & \forall_{i,j \in N} \notag{}\\
|
|
\label{line:back-sat-at-least}\text{subject to} \hspace{2em} & \exists_{j \in N} q_{ij} & \forall_{i \in N} \\
|
|
\label{line:back-sat-row} & \neg q_{ij} \lor \neg q_{ik} & \forall_{i,j \in N} \forall_{j \leq{} k \leq{} n} \\
|
|
\label{line:back-sat-col} & \neg q_{ij} \lor \neg q_{kj} & \forall_{i,j \in N} \forall_{i \leq{} k \leq{} n} \\
|
|
\label{line:back-sat-diag1} & \neg q_{ij} \lor \neg q_{(i+k)(j+k)} & \forall_{i,j \in N} \forall_{1 \leq{} k \leq{} \min(n-i, n-j)} \\
|
|
\label{line:back-sat-diag2} & \neg q_{ij} \lor \neg q_{(i+k)(j-k)} & \forall_{i,j \in N} \forall_{1 \leq{} k \leq{} \min(n-i, j)}
|
|
\end{align}
|
|
|
|
The encoding of the problem uses a Boolean \variable{} for every position of the chessboard.
|
|
Each \variable{} represents if a queen is located on this position or not.
|
|
\Cref{line:back-sat-at-least} forces that a queen is placed on every row of the chessboard.
|
|
\Cref{line:back-sat-row,line:back-sat-col} ensure that only one queens is placed in each row and column respectively.
|
|
\Cref{line:back-sat-diag1,line:back-sat-diag2} similarly constrain each diagonal to contain only one queen.
|
|
\end{example}
|
|
|
|
A variation on \gls{sat} is the \gls{maxsat} problem.
|
|
In a \gls{sat} problem all clauses need to be \gls{satisfied}, but this is not the case in a \gls{maxsat} problem.
|
|
Instead, clauses are given individual weights.
|
|
The higher the weight, the more important the clause is for the overall problem.
|
|
The goal in the \gls{maxsat} problem is then to find an \gls{assignment} for Boolean \variables{} that maximizes the cumulative weights of the \gls{satisfied} clauses.
|
|
|
|
The \gls{maxsat} problem is analogous to an \gls{opt-prb}.
|
|
Like an \gls{opt-prb}, the aim of \gls{maxsat} is to find the \gls{opt-sol} to the problem.
|
|
The difference is that the weights are given to the \constraints{} instead of the \variables{} or a function over them.
|
|
It is, again, possible to rewrite a \cmodel{} with an \gls{objective} as a \gls{maxsat} problem.
|
|
A naive approach to encode an integer objective is, for example, to encode each possible result of the \gls{objective} as a Boolean \variable{}.
|
|
This Boolean \variable{} then forms a singleton clause with the result value as its weight.
|
|
|
|
For many problems the use of \gls{maxsat} \solvers{}, such as \gls{openwbo} \autocite{martins-2014-openwbo} and RC2 \autocite{ignatiev-2019-rc2}, offers a very successful method to find an \gls{opt-sol} to a problem.
|
|
|
|
\section{Constraint Modelling Languages}%
|
|
\label{sec:back-other-languages}
|
|
|
|
Although \minizinc{} is the \cml{} that is the primary focus of this thesis, there are many other \cmls{}.
|
|
Each \cml{} has its own focus and purpose and comes with its own strengths and weaknesses.
|
|
Most of the techniques that are discussed in this thesis may be adapted to these languages.
|
|
|
|
We now discuss some prominent \cmls{} and compare them to \minizinc{} to indicate to the reader where our techniques will need to be adjusted to fit other languages.
|
|
|
|
A notable difference between all these languages and \minizinc{} is that only \minizinc{} allows modellers to extend the language using their own (user-defined) functions.
|
|
In other \cmls{} the modeller is restricted to the expressions and functions provided by the language.
|
|
|
|
\subsection{AMPL}%
|
|
\label{sub:back-ampl}
|
|
\glsreset{ampl}
|
|
|
|
One of the most used \cmls\ is \gls{ampl} \autocite{fourer-2003-ampl}.
|
|
As the name suggest, \gls{ampl} was designed to allow modellers to express problems through the use of mathematical equations.
|
|
It is therefore also described as an ``algebraic modelling language''.
|
|
Specifically \gls{ampl} was designed to model linear programs.
|
|
These days \gls{ampl} has been extended to allow more advanced \solver{} usage.
|
|
Depending on the \gls{solver} targeted by \gls{ampl}, the language gives the modeller access to additional functionality.
|
|
Different types of \solvers{} can also have access to different types of \constraints{}, such as quadratic and non-linear \constraints{}.
|
|
\gls{ampl} has even been extended to allow certain \glspl{global} when using a \gls{cp} \solver{} \autocite{fourer-2002-amplcp}.
|
|
|
|
\begin{example}
|
|
|
|
Let us consider modelling in \gls{ampl} using the well-known \gls{tsp}.
|
|
In the \gls{tsp}, we are given a list of cities and the distances between all cities.
|
|
The goal of the problem is to find a shortest path that visits all the cities exactly once and returns to its origin.
|
|
A possible \cmodel{} for this problem in \gls{ampl} is shown in \cref{lst:back-ampl-tsp}.
|
|
|
|
\begin{listing}
|
|
\amplfile[l]{assets/listing/back_tsp.mod}
|
|
\caption{\label{lst:back-ampl-tsp} An \gls{ampl} model describing the \gls{tsp}.}
|
|
\end{listing}
|
|
|
|
\Lrefrange{line:back:ampl:parstart}{line:back:ampl:parend} declare the \parameters{} of the \cmodel{}.
|
|
The \gls{ampl} model requires a set of names of cities, \texttt{Cities}, as input.
|
|
From these cities it declares a set \texttt{Paths} that contains all possible paths between the different cities.
|
|
Note how its definition uses a \gls{comprehension} to eliminate symmetric paths.
|
|
For each possible path the model also requires its cost.
|
|
|
|
On \lref{line:back:ampl:var} we find the declaration for the \variables{} of the model.
|
|
For each possible path it decides whether it is used or not.
|
|
The \constraint{} on \lref{line:back:ampl:con1} ensures that for each city, one possible path to the city and one possible path from the city is taken.
|
|
|
|
Crucially, this does not yet enforce that the taken variables represent a single path.
|
|
It is still possible for so-called subtours to exist, multiple unconnected paths that together span all the cities.
|
|
\Lrefrange{line:back:ampl:compstart}{line:back:ampl:compend} introduce the classic Miller--Tucker--Zemlin method \autocite{miller-1960-subtour} to the model to eliminate these subtours.
|
|
For each city, this method introduces a variable that represent the order of the cities in the path.
|
|
The \constraint{} forces that if a path is taken from \texttt{i} to \texttt{j}, then \texttt{Order[i] < Order[j]}.
|
|
As such, each \texttt{Order} \variable{} must take a unique value and all cities are on the same path.
|
|
To remove symmetries in the model we set the \texttt{Order} \variable{} of the first city in the set to be one.
|
|
|
|
Finally, \lref{line:back:ampl:goal} sets the goal of the \gls{ampl} model: to minimize the cost of all the paths taken.
|
|
|
|
This model shows that the \gls{ampl} syntax has many features similar to \minizinc{}.
|
|
Like \minizinc{}, \gls{ampl} has an extensive expression language, which includes \gls{generator} expressions and a large collection of \glspl{operator}.
|
|
The building blocks of the model are also similar: \parameter{} declarations, \variable{} declarations, \constraints{}, and a solving goal.
|
|
|
|
\begin{listing}
|
|
\mznfile{assets/listing/back_tsp.mzn}
|
|
\caption{\label{lst:back-mzn-tsp} An \minizinc{} model describing the \gls{tsp}.}
|
|
\end{listing}
|
|
|
|
A \minizinc{} model for the same problem is shown in \cref{lst:back-mzn-tsp}.
|
|
Even though \gls{ampl} has a similar syntax to \minizinc{}, the models could not be more different.
|
|
The main reason for this difference is the level at which these models are written.
|
|
|
|
The \gls{ampl} model is written to target a \gls{mip} solver.
|
|
In the \gls{ampl} language this means that the modeller is required to use the language functionality that is compatible with the targeted \solver{}; in this case, all expression have to be linear (in\nobreakdash)equations.
|
|
|
|
In \minizinc{} the modeller is not constrained in the same way.
|
|
It can use the viewpoint of choosing, from each city, to which city to go next.
|
|
A \mzninline{circuit} \gls{global} is then used to enforce that these decisions form a single path over all cities.
|
|
When targeting a \gls{mip} \solver{}, the \gls{decomp} of the \mzninline{circuit} \constraint{} will use a similar method to Miller--Tucker--Zemlin.
|
|
|
|
In \minizinc{}, the modeller is always encouraged to create high-level \cmodels{}.
|
|
\minizinc{} then rewrites these models into compatible \glspl{slv-mod}.
|
|
\end{example}
|
|
|
|
\subsection{OPL}%
|
|
\label{sub:back-opl}
|
|
\glsreset{opl}
|
|
|
|
\gls{opl} is a \cml{} that aims to combine the strengths of mathematical programming languages like \gls{ampl} with the strengths of \gls{cp} \autocite{van-hentenryck-1999-opl}.
|
|
The syntax of \gls{opl} is very similar to the \minizinc{} syntax.
|
|
|
|
Where \gls{opl} really shines is when modelling scheduling problems.
|
|
Resources and activities are separate objects in \gls{opl}.
|
|
This allows users to express resource scheduling \constraints{} in an incremental and more natural fashion.
|
|
When solving a scheduling problem, \gls{opl} makes use of specialized interval \variables{}, which represent when a task is scheduled.
|
|
|
|
\begin{example}
|
|
|
|
Let us consider modelling in \gls{opl} using the well-known ``job shop'' problem.
|
|
The job shop problem is similar to the open shop problem discussed in the introduction.
|
|
Like the open shop problem, the goal is to schedule jobs with multiple tasks.
|
|
Each task must be performed by an assigned machine.
|
|
A machine can only perform one task at any one time and only one task of the same job can be scheduled at the same time.
|
|
However, in the job shop problem, the tasks within a job also have a specified order.
|
|
Abstracting from the \parameter{} declarations, the possible formulation of the \variable{} declarations and \constraints{} for the job shop problem in \gls{opl} is shown in \cref{lst:back-opl-jsp}.
|
|
|
|
\begin{listing}
|
|
\plainfile[l]{assets/listing/back_jsp.mod}
|
|
\caption{\label{lst:back-opl-jsp} An \gls{opl} model describing the job shop problem, abstracting from \parameter{} declarations.}
|
|
\end{listing}
|
|
|
|
\Lref{line:back:opl:task} declares the \texttt{task} \variables{}, the main \variables{} in model.
|
|
These \variables{} have the type \texttt{Activity}, a special type used to declare any scheduling event with a start and end time.
|
|
\Variables{} of this type are influenced by the \texttt{ScheduleHorizon} \parameter{} defined on \lref{line:back:opl:horizon} of the model.
|
|
This \parameter{} restricts the time span in which all activities in the \cmodel{} must be scheduled.
|
|
On \lref{line:back:opl:con2}, we enforce the order between the different tasks for the same job.
|
|
The \constraint{} uses the \texttt{precedes} operator to enforce that one activity takes places before another.
|
|
|
|
Another activity is declared on \lref{line:back:opl:makespan}.
|
|
The \texttt{makespan} \variable{} represents the time spanned by all tasks.
|
|
This is enforced by the \constraint{} on \lref{line:back:opl:con1}.
|
|
\Lref{line:back:opl:goal} sets the minimization of \texttt{makespan} to be the goal of the model.
|
|
|
|
Resources are important notions in \gls{opl}.
|
|
A resource can be any requirement of an activity.
|
|
On \lref{line:back:opl:resources} a \texttt{UnaryResource} is declared for every machine.
|
|
A \texttt{UnaryResource} can be used by at most one activity at a time.
|
|
The \constraint{} on \lref{line:back:opl:con3} ensures that at most one task activity can use same machine at the same time.
|
|
The \constraint{} uses the \texttt{requires} operator to bind an activity to a resource.
|
|
|
|
\begin{listing}
|
|
\mznfile{assets/listing/back_jsp.mzn}
|
|
\caption{\label{lst:back-mzn-jsp} An \minizinc{} model describing the job shop problem, abstracting from \parameter{} declarations.}
|
|
\end{listing}
|
|
|
|
A fragment of a \minizinc{} model, modelling the same parts of the job shop problem, is shown in \cref{lst:back-mzn-jsp}.
|
|
Notably, \minizinc{} does not have explicit activity \variables{}.
|
|
It instead uses integer \variables{} that represent the start times of the tasks and the end time for the \mzninline{makespan} activity that spans all tasks.
|
|
This means that much of the implicit behaviour of the \texttt{Activity} \variables{} has to be defined explicitly.
|
|
Where in the \gls{opl} model we could just state a global scheduling horizon, in \minizinc{} it has to be explicitly included in the \domains{} of the time \variables{}.
|
|
Instead of a \texttt{precedes} operator, we have to explicitly enforce the precedence of tasks using linear \constraints{}.
|
|
|
|
\Gls{opl} model also has the advantage of its resource syntax.
|
|
It first states the resource objects and then merely has to use the \texttt{requires} keyword to force tasks on the same machine to be mutually exclusive.
|
|
In \minizinc{} the same requirement is implemented through the use of \mzninline{disjunctive} \constraints{}.
|
|
Although this has the same effect, all mutually exclusive jobs have to be combined in a single statement in the model.
|
|
This makes it harder in \minizinc{} to write the correct \constraint{} and its meaning is less clear.
|
|
\end{example}
|
|
|
|
The \gls{opl} also contains a specialized search syntax that is used to instruct its solvers \autocite{van-hentenryck-2000-opl-search}.
|
|
This syntax gives modellers full programmatic control over how the solver explores the search space depending on the current state of the variables.
|
|
This gives the modeller more control over the search in comparison to the \gls{search-heuristic} \glspl{annotation} in \minizinc{}, which only allow modellers to select predefined \glspl{search-heuristic} already implemented in the solver.
|
|
Take, for example, the following \gls{opl} search definition:
|
|
|
|
\begin{plain}
|
|
search {
|
|
try x < y | y >= x endtry;
|
|
}
|
|
\end{plain}
|
|
|
|
This search strategy ensures that we first try to find a \gls{sol} where the \variable{} \mzninline{x} takes a value smaller than \mzninline{y}.
|
|
If it does not find a \gls{sol}, then it tries finding a \gls{sol} by making the opposite assumption.
|
|
This search specification, like many others imaginable, cannot be enforced using \minizinc{}'s \gls{search-heuristic} \glspl{annotation}.
|
|
|
|
To support \gls{opl}'s dedicated search language, the language is tightly integrated with its supported \solvers{}.
|
|
Its search syntax requires that the \gls{opl} process directly interacts with the \solver{}'s internal search mechanism and that the \solver{} reasons about search on the same level as the \gls{opl} model.
|
|
It is therefore not always possible to connect other \solvers{} to \gls{opl}.
|
|
|
|
While this advanced search language is an interesting construct, it has been removed from the current version of \gls{opl} \autocite{ibm-2017-opl}.
|
|
Instead, \gls{opl} now offers the use of ``search phases'', which function similarly to \minizinc{}'s \gls{search-heuristic} \glspl{annotation}.
|
|
|
|
\subsection{Essence}%
|
|
\label{sub:back-essence}
|
|
|
|
\gls{essence} \autocite{frisch-2007-essence} is another high-level \cml{}.
|
|
It is notable for its adoption of high-level \variable{} types.
|
|
In addition to all variable types that are supported by \minizinc{}, \gls{essence} also contains:
|
|
|
|
\begin{itemize}
|
|
\item Finite sets of non-integer types,
|
|
\item finite multi-sets of any type,
|
|
\item finite (\gls{partial}) functions,
|
|
\item and (regular) partitions of finite types.
|
|
\end{itemize}
|
|
|
|
Since sets, multi-sets, and functions can be defined on any other type, these types can be arbitrarily nested, and the modeller could, for example, define a \variable{} that is a set of sets of integers.
|
|
|
|
\begin{example}
|
|
|
|
Let us consider modelling in \gls{essence} using the well-known ``social golfer'' problem.
|
|
In the social golfer problem, a community of golfers plans games of golf for a set number of weeks.
|
|
Every week all the golfers are split into groups of equal size and each group plays a game of golf.
|
|
The goal of the problem is to find a way to split the groups differently every week, such that two golfers will not meet each other twice.
|
|
|
|
\begin{listing}
|
|
\plainfile[l]{assets/listing/back_sgp.essence}
|
|
\caption{\label{lst:back-essence-sgp} An \gls{essence} model describing the social golfer problem}
|
|
\end{listing}
|
|
|
|
A \cmodel{} for the social golfer problem in \gls{essence} can be seen in \cref{lst:back-essence-sgp}.
|
|
It starts with the preamble declaring the version of the language that is used.
|
|
All the \parameters{} of the \cmodel{} are declared on \lref{line:back:essence:pars}: \texttt{nweeks} is the number of weeks to be considered, \texttt{ngroups} is the number of groups of golfers, and \texttt{size} is the size of each group.
|
|
The input for the \parameters{} is checked to ensure that they take a value that is one or higher.
|
|
\Lref{line:back:essence:ntype} then uses the \parameters{} to declare a new type to represent the golfers.
|
|
|
|
Most of the problem is modelled on \lref{line:back:essence:var}.
|
|
It declares a \variable{} that is a set of partitions of the golfers.
|
|
The choice in \variable already contains some implied \constraints{}.
|
|
Since the \variable{} reasons about partitions of the golfers, a correct \gls{assignment} is already guaranteed to correctly split the golfers into groups.
|
|
The usage of a set of size \texttt{nweeks} means that we directly reason about that number of partitions that have to be unique.
|
|
|
|
The type of the \variable{} does, however, not guarantee that two golfers will not meet each other twice.
|
|
Therefore, a \constraint{} that enforces this is found on \lref{line:back:essence:con}.
|
|
Notably, the \texttt{together} function tests whether two elements are in the same part of a partition.
|
|
|
|
A \minizinc{} model for the same problem can be found in \cref{lst:back-mzn-sgp}.
|
|
It starts similarly to the \gls{essence} model, with the declaration of the \parameters{} and a type for the golfers.
|
|
The differences start from the declaration of the \variables{}.
|
|
The \minizinc{} model is unable to use a set of partitions and instead uses an \gls{array} of sets.
|
|
Each set represents a single group in a single week.
|
|
Note that, through the \gls{essence} type system, the first two \constraints{} in the \minizinc{} model are implied in the \gls{essence} model.
|
|
This is an example where the use of high-level types helps the modeller create more concise models.
|
|
Apart from syntax and the \variable{} viewpoint, the \constraint{} that enforces that golfers only occur in the same group once is identical.
|
|
\end{example}
|
|
|
|
\begin{listing}
|
|
\mznfile{assets/listing/back_sgp.mzn}
|
|
\caption{\label{lst:back-mzn-sgp} A \minizinc{} model describing the social golfers problem}
|
|
\end{listing}
|
|
|
|
\Gls{essence} allows the use of many high-level \variable{} types and \constraints{} on these \variables{}.
|
|
Since these types of \variables{} are often not \gls{native} to the \solver{}, an extensive \gls{rewriting} process is required to arrive at a \gls{slv-mod}.
|
|
Different from the other two languages presented in this section, the implementation of the \gls{essence} toolchain is available open source and has been the subject of published research.
|
|
The \gls{rewriting} process of \gls{essence} is split into two steps.
|
|
First, an \gls{essence} model is transformed into \gls{ess-prime}.
|
|
Then, an \gls{ess-prime} model forms an \instance{}, and is subsequently rewritten into a \gls{slv-mod}.
|
|
|
|
Compared to \gls{essence}, the \gls{ess-prime} language does not contain the same high-level \variables{}.
|
|
As such, the main task of Conjure, the compiler from \gls{essence} to \gls{ess-prime}, is to decide on a representation of these \variables{} in terms of integer and Boolean \variables{} \autocite{akgun-2014-essence}.
|
|
However, there are often multiple ways to represent a high-level \variable{} or how to enforce its implicit \constraints{}.
|
|
Although the modeller is able to decide on representation, Conjure has been extended to automatically select among the models it produces \autocite{akgun-2013-auto-ess}.
|
|
|
|
\paragraph{Essence'}
|
|
|
|
Once a \cmodel{} is turned into \gls{ess-prime}, it is at a very similar level to a \minizinc{} model.
|
|
This can be illustrated using the N-Queens problem introduced in \cref{ex:back-nqueens}.
|
|
The same problem modelled in \gls{ess-prime} is shown in \cref{lst:back-ep-nqueens}.
|
|
Apart from the syntax used, both languages use the exact same concepts to model the problem.
|
|
|
|
\begin{listing}
|
|
\plainfile{assets/listing/back_nqueens.eprime}
|
|
\caption{\label{lst:back-ep-nqueens} A \gls{ess-prime} model describing the N-Queens problem.}
|
|
\end{listing}
|
|
|
|
An \instance{} of an \gls{ess-prime} model can be rewritten by Savile Row into a \gls{slv-mod} for a variety of \solvers{}, including \gls{cp}, \gls{sat}, and \gls{maxsat} \solvers{}.
|
|
Savile Row \autocite{nightingale-2017-ess-prime}, and its predecessor Tailor \autocite{rendl-2010-thesis}, have pioneered some of the most important \gls{rewriting} techniques.
|
|
As such, at present many simplification techniques used during \gls{rewriting} are shared between \gls{ess-prime} and \minizinc{}.
|
|
At its core, however, the \gls{rewriting} of \gls{ess-prime} works very differently from \minizinc{}.
|
|
\Gls{ess-prime} is rewritten using a more traditional compiler.
|
|
For each concept in the language the \compiler{} intrinsically knows how to rewrite it for its target \solver{}.
|
|
In particular, \glspl{decomp} for \constraints{} are not declared as predicates and functions in \gls{ess-prime}, but hard-coded in the compiler.
|
|
|
|
Recently, \textcite{kocak-2020-ess-incr} have also presented Savile Row as the basis of a \gls{meta-optimization} toolchain.
|
|
The authors extend Savile Row to bridge the gap between the incremental assumption interface of \gls{sat} \solvers{} and the modelling language and show how this helps to efficiently solve pattern mining and optimization problems.
|
|
Consequently, the usage of \gls{meta-optimization} in Savile Row reiterates the importance of the use of \gls{meta-optimization} algorithms in \cmls{} in general and the need for incremental bridging between the modelling language and the \solver{}.
|
|
|
|
\section{Term Rewriting}%
|
|
\label{sec:back-term}
|
|
\glsreset{trs}
|
|
|
|
At the heart of the \gls{rewriting} process that transforms a \minizinc{} \instance{} into a \gls{slv-mod}, lies a \gls{trs}.
|
|
A \gls{trs} describes a computational model.
|
|
The full process can be described as the application of rules \(l \rightarrow r_{1}, \ldots, r_{n}\), that replace a \gls{term} \(l\) with \(n \geq{} 1\) \glspl{term} \(r_{1}, \ldots, r_{n}\) \autocite{baader-1998-term-rewriting}.
|
|
A \gls{term} is an expression with nested sub-expressions consisting of function and constant symbols.
|
|
An example of a term is \(F(0 + 1,F(1,0))\), where \(F\) and \(+\) are function symbols and \(0\) and \(1\) are constant symbols.
|
|
In a term rewriting rule, a term can also contain a term variable, which captures a term sub-expression.
|
|
|
|
\begin{example}
|
|
|
|
Consider the following \gls{trs}, which consists of some (well-known) rules to rewrite logical conjunctions.
|
|
|
|
\begin{align*}
|
|
(r_{1}):\hspace{5pt} & 0 \land x \rightarrow 0 \\
|
|
(r_{2}):\hspace{5pt} & 1 \land x \rightarrow x \\
|
|
(r_{3}):\hspace{5pt} & x \land y \rightarrow y \land x
|
|
\end{align*}
|
|
|
|
From these rules it follows that
|
|
|
|
\[ 1 \land 1 \land 0 \rightarrow^{r_{2}} 1 \land 0 \rightarrow^{r_{3}} 0 \land 1 \rightarrow^{r_{1}} 0. \]
|
|
|
|
Notice that there is a choice between different rules.
|
|
In general, a \gls{trs} can be non-deterministic.
|
|
We could also have applied the \(r_{2}\) twice and arrived at the same result.
|
|
\end{example}
|
|
|
|
Two properties of a \gls{trs} that are often studied are \gls{termination} and \gls{confluence}.
|
|
A \gls{trs} is said to be terminating if, no-matter what order the term rewriting rules are applied, it always arrives at a \gls{normal-form} (i.e., a set of \glspl{term} for which none of the rules apply).
|
|
A \gls{trs} is confluent if, no-matter what order the term rewriting rules are applied, it always arrives at the same \gls{normal-form} (if it arrives at a \gls{normal-form}).
|
|
|
|
It is trivial to see that our previous example is non-terminating, since rule \(r_{3}\) can be repeated an infinite amount of times.
|
|
The system, however, is confluent, since, if it terminates, it always arrives at the same \gls{normal-form}: if the term contains any \(0\), then the result is \(0\); otherwise, the result is \(1\).
|
|
|
|
These properties could also be studied in the translation process of a \minizinc{} instance into \flatzinc{}.
|
|
The \gls{confluence} of the \gls{rewriting} process would ensure that the same \gls{slv-mod} is produced independently of the order in which the \minizinc{} \instance{} is processed.
|
|
Although this is a desirable quality, it is not guaranteed since it conflicts with important simplifications, discussed in \cref{sec:back-mzn-interpreter}, used to improve the quality of \gls{slv-mod}.
|
|
|
|
Many of the techniques used by \solvers{} targeted by \minizinc{} are proven to be complete.
|
|
This means that they are guaranteed to (eventually) find a (optimal) \gls{sol} for the \instance{} or prove that there is none.
|
|
For this quality to hold for the overall \minizinc{} solving process, it has to be guaranteed that the \minizinc{} \gls{rewriting} process terminates (so the solving process is able to start).
|
|
While this property is interesting, it cannot be guaranteed for the \gls{rewriting} process in general.
|
|
Since \minizinc{} is a Turing complete language, it is possible to create a \minizinc{} model for which the \gls{rewriting} process will infinitely recurse.
|
|
|
|
In the remainder of this section we discuss two types of \glspl{trs} that are closely related to \cmls{} and the \gls{rewriting} process: \gls{clp} and \gls{chr}.
|
|
|
|
\subsection{Constraint Logic Programming}%
|
|
\label{subsec:back-clp}
|
|
\glsreset{clp}
|
|
|
|
\gls{clp} \autocite{jaffar-1987-clp} is a predecessor of \cmls{} like \minizinc{}.
|
|
This subsection provides a brief introduction into the workings of a \gls{clp} system.
|
|
For a more comprehensive overview on modelling, rewriting, and solving using a \gls{clp} system, we recommend reading ``Programming with constraints: an introduction'' by \textcite{marriott-1998-clp}.
|
|
|
|
A constraint logic program describes the process in which a \cmodel{} is eventually rewritten into a \gls{slv-mod} and solved by a \solver{}.
|
|
Like in \minizinc{}, the user defines \constraint{} predicates to use in the definition of the \cmodel{}.
|
|
Different from \minizinc{}, \constraint{} predicates in \gls{clp} can be rewritten in multiple ways.
|
|
The goal of a constraint logic program is to rewrite all \constraints{} in such a way that all \gls{native} \glspl{constraint} are \gls{satisfied}.
|
|
|
|
Variables{} are another notable difference between \cmls{} and \gls{clp}.
|
|
In \gls{clp}, like in a conventional \gls{trs}, a variable is merely a name.
|
|
The symbol can be replaced or equated with a constant symbol, but, different from \cmls{}, this is not a requirement.
|
|
A variable can remain a name in the \gls{sol} of a constraint logic program.
|
|
This means that the \gls{sol} of a constraint logic program can be a relation between different variables.
|
|
In cases where an instantiated \gls{sol} is required, a special \mzninline{labeling} predicate is used to force a variable to take a constant value.
|
|
Similarly, there is a \mzninline{minimize} predicate that is used to find the optimal value for a variable.
|
|
|
|
The evaluation of a constraint logic program rewrites the list of \constraints{}, called the goal, in the order given by the programmer.
|
|
The \gls{rewriting} of the \constraint{} predicates is tried in the order in which the different \gls{rewriting} rules for the \constraint{} predicates are defined.
|
|
The process is completed when all \constraints{} are rewritten and the produced \gls{native} \constraints{} are not found to be inconsistent.
|
|
If all the possible ways of \gls{rewriting} the program are tried, but all of them prove to be inconsistent, then the program itself is said to be \gls{unsat}.
|
|
Even when a correct \gls{rewriting} is found, it is possible to continue the process.
|
|
This can discover all possible correct ways to rewrite the program.
|
|
|
|
To implement this mechanism there is a tight integration between the \solver{}, referred to as the constraint store, and the evaluator of the constraint logic program.
|
|
In addition to just adding \constraints{}, the program also inspects the status of the constraint store and retracts \constraints{} from the constraint store.
|
|
This allows the program to detect when the constraint store has become inconsistent.
|
|
It can then \gls{backtrack} the constraint store to the last decision (i.e., restore the constraint store to its state before the last decision was made) and try the next rewriting rule.
|
|
|
|
The strength of the constraint store affects the correctness of a constraint logic program.
|
|
Some \solvers{} are incomplete; they are unable to tell if some of their \constraints{} are \gls{satisfied} or not.
|
|
This, for example, happens with \solvers{} that work with integer \glspl{domain}.
|
|
In these cases the programmer must use the \mzninline{labeling} \constraint{} to force constant values for the variables.
|
|
Once the variables have been assigned constant values, the \solver{} is always able to decide if the \constraints{} are \gls{satisfied}.
|
|
|
|
\subsection{Constraint Handling Rules}%
|
|
\label{sub:back-chr}
|
|
\glsreset{chr}
|
|
|
|
When \constraints{} are seen as terms in a \gls{trs}, then it is not just possible to define rules to rewrite \constraints{} to \gls{native} \constraints{}.
|
|
It is also possible to define rules to simplify, propagate, and derive new \constraints{} within the \solver{}.
|
|
\gls{chr} \autocite{fruhwirth-1998-chr} follow from this idea.
|
|
\gls{chr} are a language extension (targeted at \gls{clp}) to allow for the definition of user-defined \constraints{} within a \solver{}.
|
|
\Constraints{} defined using \gls{chr} are rewritten into simpler \constraints{} until they are solved.
|
|
|
|
Different from \gls{clp}, \gls{chr} allows term rewriting rules that are multi-headed.
|
|
This means that, for some rules, multiple terms must match, to apply the rule.
|
|
|
|
\begin{example}
|
|
|
|
Consider the following user-defined \constraint{} for logical implication using \gls{chr}.
|
|
|
|
\begin{plain}
|
|
reflexivity @ X -> Y <=> X = Y | true
|
|
anti-symmetry @ X -> Y, Y -> X <=> X = Y
|
|
transitivity @ X -> Y, Y -> Z ==> X -> Z
|
|
\end{plain}
|
|
|
|
These definitions specify how \texttt{->} simplifies and propagates as a \constraint{}.
|
|
The rules follow the mathematical concepts of reflexivity, anti-symmetry, and transitivity.
|
|
|
|
\begin{itemize}
|
|
\item The first rule states that if \texttt{X = Y}, then \texttt{X -> Y} is logically true.
|
|
This rule removes the term \texttt{X -> Y}.
|
|
Since the \constraint{} is already \gls{satisfied}, nothing gets added.
|
|
\texttt{X = Y} functions as a guard.
|
|
This \solver{} \gls{native} \constraint{} is required for the rewriting rule to apply.
|
|
|
|
\item The second rule implements the anti-symmetry of logical implications; the two implications, \texttt{X -> Y} and \texttt{Y -> X}, are replaced by a \solver{} \gls{native}, \texttt{X = Y}.
|
|
|
|
\item Finally, the transitivity rule introduces a derived \constraint{}.
|
|
When it finds the \constraints{} \texttt{X -> Y} and \texttt{Y -> Z}, then it adds another \constraint{} \texttt{X -> Z}.
|
|
Different from the other rules, the matched \constraints{} are not removed.
|
|
\end{itemize}
|
|
|
|
Note that the use of multi-headed rewriting rules is essential to define these rules.
|
|
\end{example}
|
|
|
|
The rules in a \gls{chr} system are categorized into three different categories: simplification, propagation, and simpagation.
|
|
The first two rules in the previous example are simplification rules: they replace some \constraint{} atoms by others.
|
|
The final rule in the example was a propagation rule: based on the existence of certain \constraints{}, new \constraints{} are introduced.
|
|
Simpagation rules are a combination of both types of rules in the form:
|
|
|
|
\[ H_{1}, \ldots H_{l} \backslash H_{l+1}, \ldots, H_{n} \texttt{<=>} G_{1}, \ldots{}, G_{m} | B_{1}, \ldots, B_{o} \]
|
|
|
|
It is possible to rewrite using a simpagation rule when there are terms matching \(H_{1}, \ldots, H_{n}\) and there are \solver{} \gls{native} \constraints{} \(G_{1}, \ldots{}, G_{m}\).
|
|
When the simpagation rule is applied, the terms \(H_{l+1}, \ldots, H_{n}\) are replaced by the terms \(B_{1}, \ldots, B_{o}\).
|
|
The terms \(H_{1}, \ldots H_{l}\) are kept in the system.
|
|
Since simpagation rules incorporate both the elements of simplification and propagation rules, it is possible to formulate all rules as simpagation rules.
|
|
|
|
\section{Rewriting \glsentrytext{minizinc}}%
|
|
\label{sec:back-mzn-interpreter}
|
|
|
|
Traditionally a \compiler{} is split into three sequential parts: the ``front-end'', the ``middle-end'', and the ``back-end''.
|
|
It is the job of the front-end to parse the user input, report on any errors or inconsistencies in the input, and transform it into an internal representation.
|
|
The middle-end performs the main translation, independent of the compilation target.
|
|
It converts the internal representation at the level of the compiler front-end to another internal representation as close as possible to the level required by the compilation targets.
|
|
The final transformations into the format required by the compilation target are performed by the back-end.
|
|
When a \compiler{} is separated into these three steps, then adding support for a new language or compilation target only requires the addition of a front-end or back-end respectively.
|
|
|
|
The \minizinc{} compilation process can be split into the same three parts, as shown in \cref{fig:back-mzn-comp}.
|
|
In the front-end, a \minizinc{} model is combined with its data into an \instance{}.
|
|
The instance is parsed into an \gls{ast}.
|
|
The process then analyses the \gls{ast} to discover the types of all expressions used in the \instance{}.
|
|
If an inconsistency is discovered, then an error is reported to the modeller.
|
|
Finally, the front-end also preprocesses the \gls{ast}.
|
|
This process is used to transform expressions into a common form for the middle-end, removing the ``syntactic sugar''.
|
|
For instance, this replaces enumerated types by normal integers.
|
|
|
|
\begin{figure}
|
|
\centering
|
|
\includegraphics[width=\linewidth]{assets/img/back_compilation_structure}
|
|
\caption{\label{fig:back-mzn-comp} The compilation structure of the \minizinc \compiler{}.}
|
|
\end{figure}
|
|
|
|
The middle-end contains the most important two processes: \gls{rewriting} and optimization.
|
|
During the \gls{rewriting} process the \minizinc{} model is rewritten into a \gls{slv-mod}.
|
|
It could be noted that the \gls{rewriting} step depends on the compilation target to define its \gls{native} \constraints{}.
|
|
Even though the information required for this step is target dependent, we consider it part of the middle-end as the mechanism is the same for all compilation targets.
|
|
A full description of this process will follow in the next subsection.
|
|
Once a \gls{slv-mod} is constructed, the \minizinc{} \compiler{} tries to optimize this model: shrink \domains{} of \variables{}, remove \constraints{} that are proven to hold, and remove \variables{} that have become unused.
|
|
These optimization techniques are discussed in the remaining subsections.
|
|
|
|
The back-end converts the internal \gls{slv-mod} into a format to be used by the targeted \solver{}.
|
|
Given the formatted artefact, a \solver{} process, controlled by the back-end, is then started.
|
|
The \solver{} process produces \glspl{sol} for the \gls{slv-mod}.
|
|
Before these are given to the modeller, the back-end reformulates these \glspl{sol} to become \glspl{sol} of the modeller's \instance{}.
|
|
|
|
\subsection{Rewriting}%
|
|
\label{subsec:back-rewriting}
|
|
|
|
The goal of the \gls{rewriting} process is to arrive at a flat \gls{slv-mod}: an \gls{eqsat} \instance{} that only contains \constraints{} that consist of a singular calls, all arguments to calls are \parameter{} literals or \variable{} identifiers, and using only \constraints{} and \variable{} types that are \gls{native} to the target \solver{}.
|
|
|
|
To arrive at a flat \gls{slv-mod}, the \gls{rewriting} process traverses the declarations, \constraints{}, and the solver goal and rewrites any expression contained in these items.
|
|
An expression is rewritten into other \minizinc{} expressions according to the \gls{decomp} given in the target \solver{}'s library.
|
|
Enforced by \minizinc{}'s type system, at most one rule applies for any given \constraint{}.
|
|
The \gls{rewriting} of expressions is performed bottom-up, we rewrite any sub-expression before its parent expression.
|
|
For instance, in a call each argument is rewritten before the call itself is rewritten.
|
|
|
|
An exception to this bottom-up approach is the \gls{rewriting} of \glspl{comprehension} \mzninline{[@\(E\)@ | @\(i\)@ in @\(S\)@ where @\(F\)@]}.
|
|
\Gls{rewriting} \(E\) requires instantiating the identifier \(i\) with the values from the set \(S\), and evaluating the condition \(W\).
|
|
The \compiler{} therefore iterates through all values in \(S\), binds the values to the specified identifier(s), and rewrites the condition \(F\).
|
|
If \(F\) is \true{}, it rewrites the expression \(E\) and collects the result.
|
|
Once the \gls{generator} is exhausted, the compiler rewrites its surrounding expression using the collected values.
|
|
|
|
The \gls{decomp} system in \minizinc{} is defined in terms of function declarations.
|
|
Any call, whose declaration has a function body, is eventually replaced by an instantiation of this function body using the arguments to the call.
|
|
Calls are, however, not the only type of expression that are decomposed during the \gls{rewriting} process.
|
|
Other expressions, like \gls{operator} expressions, variable \gls{array} access, and \gls{conditional} expressions, may also have to be decomposed for the target \solver{}.
|
|
During the \gls{rewriting} process, these expressions are rewritten into equivalent call expressions that start the decomposition process.
|
|
|
|
A notable effect of the \gls{rewriting} is that sub-expressions are replaced by literals or \glspl{avar}.
|
|
If the expression contains only \parameters{}, then the \gls{rewriting} of the expression is merely a calculation to find the value of the literal to be put in its place.
|
|
If, however, the expression contains a \variable{}, then this calculation cannot be performed during the \gls{rewriting} process.
|
|
Instead, an \gls{avar} must be created to represent the value of the sub-expression, and it must be constrained to take the value corresponding to the expression.
|
|
The creation of this \gls{avar} and defining \constraints{} happens in one of two ways.
|
|
|
|
\begin{itemize}
|
|
|
|
\item For Boolean expressions in a non-\rootc{} context, the \gls{avar} is inserted by the \gls{rewriting} process itself.
|
|
To constrain this \gls{avar}, the \compiler{} then adds the \gls{reif} of the \constraint{}.
|
|
This \constraint{} contains a variation of the call that would have been generated for the expression in \rootc{} context.
|
|
The name of the function is appended with \mzninline{_reif} and an extra Boolean \variable{} argument is added to the call.
|
|
The definition of this \constraint{} should implement the \gls{reif} of the original expression: setting the additional argument to \true{} if the \constraint{} is \gls{satisfied}, and \false{} otherwise.
|
|
For example, consider the following \minizinc{} \constraint{}.
|
|
|
|
\begin{mzn}
|
|
constraint b \/ this_call(x, y);
|
|
\end{mzn}
|
|
|
|
\noindent{}During \gls{rewriting} it will be turned into the following \cmodel{} fragment.
|
|
|
|
\begin{mzn}
|
|
var bool: i1;
|
|
constraint this_call_reif(x, y, i1);
|
|
constraint b \/ i1
|
|
\end{mzn}
|
|
|
|
\noindent{}Rewriting then continues with the \mzninline{this_call_reif} function (if its declaration has a body), as well as the disjunction \gls{operator}.
|
|
|
|
\item For non-Boolean expressions, the \gls{avar} and defining \constraints{} are introduced in the definition of the function itself.
|
|
For example, the \mzninline{max} function in the standard library, which calculates the maximum of two values, is defined as follows.
|
|
|
|
\begin{mzn}
|
|
function var int: max(var int: x, var int: y) =
|
|
let {
|
|
var max(lb(x),lb(y))..max(ub(x),ub(y)): m;
|
|
constraint int_max(x,y,m);
|
|
} in m;
|
|
\end{mzn}
|
|
|
|
Using a \gls{let} the function body explicitly creates an \gls{avar}, constrains it to take to correct value, and then returns it.
|
|
\end{itemize}
|
|
|
|
These are the basic steps that are followed to rewrite \minizinc{} instances.
|
|
This is, however, not the complete process.
|
|
Following these steps alone would result in poor quality \glspl{slv-mod}.
|
|
A \gls{slv-mod} containing extra \variables{} and \constraints{} that do not add any information to the solving process can exponentially slow it down.
|
|
Therefore, the \minizinc{} \gls{rewriting} process is extended using many techniques to help improve the quality of the \gls{slv-mod}.
|
|
In the remainder of this chapter, we discuss the most important techniques used to improve the \gls{rewriting} process.
|
|
|
|
\subsection{Common Sub-expression Elimination}%
|
|
\label{subsec:back-cse}
|
|
\glsreset{cse}
|
|
|
|
Since \minizinc{} is, at its core, a pure functional programming language, the evaluation of a \minizinc{} expression does not have any side effects.
|
|
As a consequence, evaluating the same expression twice will always reach an equivalent result.
|
|
|
|
It is therefore possible to reuse the same result for equivalent expressions.
|
|
This simplification is called \gls{cse}.
|
|
It is a well understood technique that originates from compiler optimization \autocite{cocke-1970-cse}.
|
|
\Gls{cse} has also proven to be very effective in discrete optimization \autocite{marinov-2005-sat-optimizations, araya-2008-cse-numcsp}, including during the evaluation of \cmls{} \autocite{rendl-2009-enhanced-tailoring}.
|
|
|
|
\begin{example}
|
|
\label{ex:back-cse}
|
|
For instance, in the following \constraint{} the same expression, \mzninline{abs(x)}, occurs twice.
|
|
|
|
\begin{mzn}
|
|
constraint (abs(x)*2 >= 20) \/ (abs(x)+5 >= 15);
|
|
\end{mzn}
|
|
|
|
However, we do not need to create two separate \variables{} (and defining \constraints{}) to represent the absolute value of \mzninline{x}.
|
|
The same \variable{} can be used to represent the \mzninline{abs(x)} in both sides of the disjunction.
|
|
\end{example}
|
|
|
|
Seeing that the same expression occurs multiple times is not always easy.
|
|
Some expressions only become syntactically equal when instantiated, as in the following example.
|
|
|
|
\begin{example}
|
|
Consider the fragment:
|
|
|
|
\begin{mzn}
|
|
function var float: pythagoras(var float: a, var float: b) =
|
|
let {
|
|
var float: x = pow(a, 2);
|
|
var float: y = pow(b, 2);
|
|
} in sqrt(x + y);
|
|
constraint pythagoras(i, i) >= 5;
|
|
\end{mzn}
|
|
|
|
Although the expressions \mzninline{pow(a, 2)} and \mzninline{pow(b, 2)} are not syntactically equal, the function call \mzninline{pythagoras(i,i)} using the same \variable{} for \mzninline{a} and \mzninline{b} makes them equivalent.
|
|
\end{example}
|
|
|
|
To ensure that two identical instantiations of a function are only evaluated once, the \minizinc{} \compiler{} uses memoization.
|
|
After the \gls{rewriting} of an expression, the instantiated expression and its result are stored in a lookup table: the \gls{cse} table.
|
|
Then before any consequent expression is rewritten the \gls{cse} table is consulted.
|
|
If an equivalent expression 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 defining \mzninline{y}, \mzninline{pow(i, 2)}, is then found in the \gls{cse} table and replaced by the earlier stored result: \mzninline{y = x}.
|
|
|
|
\gls{cse} also has an important interaction with the occurrence of \glspl{reif}.
|
|
\Glspl{reif} are often defined in the library in terms of complicated \glspl{decomp} into \gls{native} \constraints{}, or require more complex algorithms in the target \solver{}.
|
|
In either case, it can be very beneficial for the efficiency of the solving process if we detect that a \gls{reified} \constraint{} is in fact not required.
|
|
|
|
If a \constraint{} is present in the \rootc{} context, it means that it must hold globally.
|
|
If the same \constraint{} is used in a non-\rootc{} context, \gls{cse} can then replace them with the constant \true{}, avoiding the need for \gls{reif} (or in fact any evaluation).
|
|
|
|
We 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 is taken depending on both the current and stored evaluation context.
|
|
If the stored expression was in \rootc{} context, then the constant \true{} is used, independent of the current context.
|
|
Otherwise, if the stored expression was in non-\rootc{} context and the current context is non-\rootc{}, then the stored result variable is used.
|
|
Finally, if the stored expression was in non-\rootc{} context and the current context is \rootc{} context, then the previous result is replaced by the constant \true{} and the evaluation proceeds as normal with the \rootc{} context \constraint{}.
|
|
|
|
\begin{example}
|
|
Consider the following \minizinc{} fragment.
|
|
|
|
\begin{mzn}
|
|
constraint b0 <-> q(x);
|
|
constraint b1 <-> t(x,y);
|
|
function var bool: p(var int: x, var int: y) = q(x) /\ r(y);
|
|
constraint b1 <-> p(x,y);
|
|
\end{mzn}
|
|
|
|
If we process the \constraints{} in order, we create a \gls{reified} call to \mzninline{q(x)} when \gls{rewriting} the first \constraint{}.
|
|
Suppose when we rewrite the second \constraint{}, we discover \mzninline{t(x,y)} is \true{}, fixing \mzninline{b1}.
|
|
When we then process \mzninline{q(x)} in instantiation of the call \mzninline{p(x,y)}, it is in the \rootc{} context.
|
|
So \gls{cse} needs to set \mzninline{b0} to \true{}.
|
|
\end{example}
|
|
|
|
\subsection{Constraint Propagation}%
|
|
\label{subsec:back-adjusting-dom}
|
|
|
|
Sometimes a \constraint{} can be detected \gls{satisfied} based on its semantics, and the known \glspl{domain} of \variables{}.
|
|
For example, consider the \constraint{} \mzninline{3*x + 7*y > 8}, and assume that both \mzninline{x} and \mzninline{y} cannot be smaller than one.
|
|
In this case, we can determine that the \constraint{} is always \gls{satisfied}, and remove it from the model without changing satisfiability.
|
|
This is a simple form of \gls{propagation}, which, as discussed in \cref{subsec:back-cp}, also tightens the \glspl{domain} of \variables{} in the presence of a \constraint{}.
|
|
|
|
The principles of \gls{propagation} can also be applied during the \gls{rewriting} of a \minizinc{} model.
|
|
It is generally a good idea to detect cases where we can directly change the \gls{domain} of a \variable{}.
|
|
Sometimes this means that the \constraint{} does not need to be added at all and that constricting the \gls{domain} is enough.
|
|
Tight domains also allow us to avoid the creation of \glspl{reif} when the truth-value of a reified \constraint{} can be determined from the \domains{}.
|
|
Finally, it can also be helpful for \solvers{} as they may need to decide on a representation of \variables{} based on their initial \domain{}.
|
|
|
|
\begin{example}%
|
|
\label{ex:back-adj-dom}
|
|
Consider the following \minizinc{} model:
|
|
|
|
\begin{mzn}
|
|
var 1..10: a;
|
|
var 1..5: b;
|
|
|
|
constraint a < b;
|
|
constraint (a > 5) -> (a + b > 12);
|
|
\end{mzn}
|
|
|
|
Given the \domain{} specified in the model, the second \constraint{} is rewritten using \gls{reified} \constraints{}.
|
|
One for each side of the implication.
|
|
|
|
If we however consider the first \constraint{}, then we deduce that \mzninline{a} must always take a value that is 4 or lower.
|
|
When the compiler adjusts the domain of \mzninline{a} while \gls{rewriting} the first \constraint{}, then the second \constraint{} can be simplified.
|
|
The expression \mzninline{a > 5} cannot hold, which means that the \constraint{} is already \gls{satisfied} and can be removed.
|
|
\end{example}
|
|
|
|
During \gls{rewriting}, the \minizinc{} compiler actively removes values from the \domain{} when it encounters \constraints{} that trivially reduce it.
|
|
For example, it detects \constraints{} with a single comparison expression between a \variable{} and a \parameter{} (e.g., \mzninline{x != 5}), \constraints{} with a single comparison between two \variables{} (e.g., \mzninline{x >= y}), \constraints{} that directly change the domain (e.g., \mzninline{x in 3..5}).
|
|
It even performs more complex \gls{propagation} for some known \constraints{}.
|
|
For example, it will reduce the \domains{} for \mzninline{int_times} and \mzninline{int_div}, and we will see in the next subsection how \gls{aggregation} will help simplify certain \constraints{}.
|
|
|
|
However, \gls{propagation} is only performed locally, when the \constraint{} is recognized.
|
|
During \gls{rewriting}, the \gls{propagation} of one \constraint{}, will not trigger the \gls{propagation} of another.
|
|
|
|
Crucially, the \minizinc{} compiler also handles equality \constraints{}.
|
|
During the \gls{rewriting} process we are in a unique position to perform effective equality \gls{propagation}.
|
|
If the \compiler{} finds that two \variables{} \mzninline{x} and \mzninline{y} are equal, then only a single \variable{} is required in the \gls{slv-mod} to represent them both.
|
|
Whenever any equality \constraint{} is found, it is removed and one of the \variables{} is replaced by the other.
|
|
|
|
This is often beneficial for the \solver{}, since it reduced the number of \variables{} and some \solver{} are not able to perform this replacement, forcing the propagation of an equality \constraint{}.
|
|
Moreover, replacing one variable for another can improve the effectiveness of \gls{cse}.
|
|
|
|
\begin{example}
|
|
Consider the following \minizinc{} fragment.
|
|
|
|
\begin{mzn}
|
|
var 1..5: a;
|
|
var 1..5: b;
|
|
|
|
constraint a = b;
|
|
constraint this(a);
|
|
constraint this(b);
|
|
\end{mzn}
|
|
|
|
The equality \constraint{}, replaces the \variable{} \mzninline{b} with the \variable{} \mzninline{a}.
|
|
Now the two calls to \mzninline{this} suddenly become equivalent, and the second will be found in the \gls{cse} table.
|
|
\end{example}
|
|
|
|
Note that if the equality \constraint{} in the example would have be found after both calls, then both calls would have already been rewritten.
|
|
The \minizinc{} compiler would be unable to revisit the rewriting of second calls after the equality is found.
|
|
It is therefore important that equalities are found early in the \gls{rewriting} process.
|
|
|
|
\subsection{Constraint Aggregation}%
|
|
\label{subsec:back-aggregation}
|
|
|
|
Complex \minizinc{} expressions sometimes result in the creation of many \glspl{avar} to represent intermediate results.
|
|
This is in particular \true{} for linear and Boolean equations that are generally written using \minizinc{} \glspl{operator}.
|
|
|
|
\begin{example}%
|
|
\label{ex:back-agg}
|
|
For example the evaluation of the linear \constraint{} \mzninline{x + 2*y <= z} could result in the following \flatzinc{}:
|
|
|
|
\begin{nzn}
|
|
var int: x;
|
|
var int: y;
|
|
var int: z;
|
|
var int: i1;
|
|
var int: i2;
|
|
constraint int_times(y, 2, i1);
|
|
constraint int_plus(x, i1, i2);
|
|
constraint int_le(i2, z);
|
|
\end{nzn}
|
|
|
|
This \flatzinc{} model is correct, but, at least for pure \gls{cp} solvers, the existence of the \gls{avar} is likely to have a negative impact on the \solver{}'s performance.
|
|
These \solvers{} would likely perform better had they directly received the equivalent linear \constraint{}:
|
|
|
|
\begin{mzn}
|
|
constraint int_lin_le([1,2,-1], [x,y,z], 0)
|
|
\end{mzn}
|
|
|
|
This \constraint{} directly represents the initial \constraint{} in the \cmodel{} without the use of \gls{avar}.
|
|
\end{example}
|
|
|
|
This can be resolved using \gls{aggregation}.
|
|
When we aggregate \constraints{} we collect multiple \minizinc{} expressions that would each have been rewritten separately, and combine them into a singular structure that eliminates the need for \gls{avar}.
|
|
For example, arithmetic expressions are combined into linear \constraints{}, Boolean logic expressions are combined into clauses, and counting \constraints{} are combined into global cardinality \constraints{}.
|
|
|
|
The \minizinc{} \compiler{} aggregates expressions whenever possible.
|
|
When the \minizinc{} \compiler{} reaches an expression that could potentially be part of an aggregated \constraint{}, the \compiler{} does not rewrite the expression.
|
|
The \compiler{} instead performs a search of its sub-expressions to collect all other expressions to form an aggregated \constraint{}.
|
|
The \gls{rewriting} process continues by \gls{rewriting} this aggregated \constraint{}.
|
|
|
|
\subsection{Delayed Rewriting}%
|
|
\label{subsec:back-delayed-rew}
|
|
|
|
Adding \gls{propagation} during \gls{rewriting} means that the system becomes non-confluent, and some orders of execution may produce ``better'', i.e., more compact or more efficient, \flatzinc{}.
|
|
|
|
\begin{example}
|
|
The following example is similar to code found in the \minizinc{} libraries of \gls{mip} \solvers{}.
|
|
|
|
\begin{mzn}
|
|
function var int: lq_zero_if_b(var int: x, var bool: b) =
|
|
x <= ub(x)*(1-b);
|
|
\end{mzn}
|
|
|
|
This predicate expresses the \constraint{} \mzninline{b -> x<=0}, using a well-known technique called the ``Big M method''.
|
|
The expression \mzninline{ub(x)} returns a valid upper bound for \mzninline{x}, i.e., a \gls{fixed} value known to be greater than or equal to \mzninline{x}.
|
|
This could be the initial upper bound \mzninline{x} was declared with, or the current value adjusted by the \minizinc{} \compiler{}.
|
|
If \mzninline{b} takes the value 0, the expression \mzninline{ub(x)*(1-b)} is equal to \mzninline{ub(x)}, and the \constraint{} \mzninline{x <= ub(x)} holds trivially.
|
|
If \mzninline{b} takes the value 1, \mzninline{ub(x)*(1-b)} is equal to 0, enforcing the \constraint{} \mzninline{x <= 0}.
|
|
\end{example}
|
|
|
|
For \gls{mip} solvers, it is quite important to enforce tight \gls{bounds} in order to improve efficiency and sometimes even numerical stability.
|
|
It would therefore be useful to rewrite the \mzninline{lq_zero_if_b} predicate only after the \domains{} of the involved \variables{} have been reduced as much as possible, in order to take advantage of the tightest possible \gls{bounds}.
|
|
On the other hand, evaluating a predicate may also impose new \gls{bounds} on \variables{}, so it is not always clear which order of evaluation is best.
|
|
|
|
The same problem occurs with \glspl{reif} that are produced during \gls{rewriting}.
|
|
Other \constraints{} could fix the \domain{} of the reified \variable{} and make the \gls{reif} unnecessary.
|
|
Instead, the \constraint{} (or its negation) can be rewritten in \rootc{} context.
|
|
This could avoid the use of a big \gls{decomp} or an expensive \gls{propagator}.
|
|
|
|
To tackle this problem, the \minizinc{} \compiler{} employs \gls{del-rew}.
|
|
When a linear \constraint{} is aggregated or a relational \gls{reif} \constraint{} is introduced it is not immediately rewritten.
|
|
Instead, these \constraints{} are appended to the end of the current \gls{ast}.
|
|
All other \constraints{}, that are not yet rewritten and could change the relevant \domains{}, are rewritten first.
|
|
|
|
Note that this heuristic does not guarantee that \variables{} have their tightest possible \gls{domain}.
|
|
One delayed \constraint{} can still influence the \domains{} of \variables{} used by other delayed \constraints{}.
|
|
|
|
Delaying the rewriting of \constraints{} also interferes with \gls{aggregation}.
|
|
Since \gls{aggregation} is eagerly performed only when a \constraint{} is first encountered, it cannot aggregate any \constraints{} that follow from delayed values.
|
|
For example, when aggregating Boolean logic expressions, we can come across an expression that needs to be \gls{reified}.
|
|
A Boolean \gls{avar} is created and the reified \constraint{} is delayed.
|
|
Although the Boolean \gls{avar} can be used in the aggregated \constraints{}, we did not consider the body of the \gls{reif}.
|
|
If the body of the \gls{reif} was defined in terms of Boolean logic, then it would have been aggregated as well.
|
|
However, since the rewriting of the body was delayed and the \compiler{} does not revisit \gls{aggregation}, this does not happen.
|
|
|
|
This problem can be solved through the use of \minizinc{}'s multi-pass compilation \autocite{leo-2015-multipass}.
|
|
It can rewrite (and propagate) an \instance{} multiple times, remembering information about the earlier iteration(s).
|
|
As such, information normally discovered later in the \gls{rewriting} process, such as the final \domains{} of \variables{}, whether two \variables{} are equal, and whether an expression must be \gls{reified}, can be used from the start.
|
|
|
|
\subsection{FlatZinc Optimization}%
|
|
\label{subsec:back-fzn-optimization}
|
|
|
|
After the \compiler{} has finished \gls{rewriting} the \minizinc{} instance, it enters the optimization phase.
|
|
The primary role of this phase is to further perform \gls{propagation} until \gls{fixpoint}.
|
|
However, this phase operates at the level of the \gls{slv-mod}, where all \constraints{} are now \gls{native} \constraints{} of the target \solver{}.
|
|
This means that, depending on the target \solver{}, the \compiler{} will not be able to understand the meaning of all \constraints{}.
|
|
It only recognizes the standard \flatzinc{} \constraints{}, but not any of the \solver{} specific \gls{native} \constraints{}.
|
|
For the standard \flatzinc{} \constraints{}, it employs \gls{propagation} methods, as discussed in \cref{subsec:back-cp}, are used to eliminate values from \domains{} and simplify \constraints{}.
|
|
|
|
In the current implementation, the \minizinc{} \compiler{} propagates mostly Boolean \constraints{} in this phase.
|
|
It tries to reduce the number of Boolean \variables{} and tries to reduce the number of literals in clauses and conjunctions.
|
|
|
|
\section{Summary}
|
|
|
|
This chapter gave an overview of the background knowledge required to understand the following technical chapters.
|
|
It introduced the practice of \constraint{} modelling, and the syntax of the \minizinc{} language.
|
|
We also compared \minizinc{} to other \cmls{} and found many similarities.
|
|
This indicates that the research presented in this thesis could be applied to these languages as well.
|
|
By using \cmls{}, a modeller can easily express a problem for a variety of \solver{} programs.
|
|
We gave a brief overview of the main methods used by these \solvers{} and their problem formats, to which a \cmodel{} must be rewritten.
|
|
Finally, we discussed the \gls{rewriting} process central to \cmls{} in more detail, focusing on the \gls{rewriting} conducted by the current implementation of \minizinc{}.
|
|
|
|
The next chapter is the first of the three main technical chapters of this thesis.
|
|
It presents a more efficient architecture to perform the \gls{rewriting} from \minizinc{} \instances{} to solver models, based on a set of formalized rewriting rules that support better reasoning about functional \constraints{}.
|