Update HR chapter

This commit is contained in:
Jip J. Dekker 2021-03-11 11:42:09 +11:00
parent 73d7c2cded
commit 873d277fe7
No known key found for this signature in database
GPG Key ID: 517DF4A00618C9C3
3 changed files with 217 additions and 221 deletions

View File

@ -8,8 +8,6 @@
\newcommand{\cml}{\gls{constraint-modelling} language\xspace{}}
\newcommand{\cmls}{\gls{constraint-modelling} languages\xspace{}}
\newcommand{\vari}{\mzninline{var}}
\newcommand{\pari}{\mzninline{par}}
\renewcommand{\phi}{\varphi}
\newcommand{\tuple}[1]{\ensuremath{\langle #1 \rangle}}
\newcommand{\Prog}{\ensuremath{\mathcal{P}}}
@ -17,17 +15,11 @@
\newcommand{\Env}{\ensuremath{\sigma}}
\newcommand{\Sem}[2]{\ptinline{[\!\![#1]\!\!]\tuple{#2}}}
\newcommand{\Cbind}{\ensuremath{\wedge}}
\newcommand{\true}{\mzninline{true}}
\newcommand{\false}{\mzninline{false}}
\newcommand{\mdiv}{\mzninline{div}}
\newcommand{\element}{\mzninline{element}}
\newcommand{\alldiff}{\mzninline{all_different}}
% Half-reification math things
\newcommand{\VV}{{\cal V}}
\newcommand{\PP}{{\cal P}}
\newcommand{\VV}{{\cal\ V}}
\newcommand{\PP}{{\cal\ P}}
\newcommand{\range}[2]{\left[\,#1\,..\,#2\,\right]}
\newcommand{\gfp}{\textup{gfp}}
\newcommand{\lfp}{\textup{lfp}}

View File

@ -134,7 +134,7 @@ have omitted the definitions of \syntax{<type-inst>} and \syntax{<ident>}, which
you can assume to be the same as in the definition of \minizinc. While we omit
the typing rules here for space reasons, we will assume that in well-typed
\microzinc\ programs, the conditions \syntax{<exp>} in if-then-else and where
expressions have \pari\ type.
expressions have \mzninline{par} type.
\begin{figure}
\begin{grammar}
@ -353,13 +353,14 @@ that introduces the variable.
A \nanozinc\ model (\cref{fig:4-nzn-syntax}) consists of a topologically-ordered
list of definitions. Each definition binds a variable to the result of a call or
another variable, and it is associated with a list of identifiers of auxiliary
constraints. The \nanozinc\ model contains a special definition $\true$,
containing all ``root-context'' constraints of the model, i.e., those that have
to hold globally and are not just used to define an auxiliary variable. Only
root-context constraints (attached to $\true$) can effectively constrain the
overall problem. Constraints attached to definitions originate from function
calls, and since all functions are guaranteed to be total, attached constraints
can only define the function result.
constraints. The \nanozinc\ model contains a special definition
\mzninline{true}, containing all ``root-context'' constraints of the model,
i.e., those that have to hold globally and are not just used to define an
auxiliary variable. Only root-context constraints (attached to \mzninline{true})
can effectively constrain the overall problem. Constraints attached to
definitions originate from function calls, and since all functions are
guaranteed to be total, attached constraints can only define the function
result.
\begin{example}\label{ex:4-absnzn}

View File

@ -13,7 +13,7 @@ formulated. Modelling languages map the more expressive formulations to
existentially quantified conjunction through a combination of loop unrolling,
and flattening using reification.
\begin{example}\label{ex:cons}
\begin{example}\label{ex:hr-cons}
Consider the following ``complex constraint'' written in \minizinc\ syntax
\begin{mzn}
@ -74,7 +74,7 @@ This is known as the \emph{strict} semantics
The usual choice for modeling partial functions in modelling languages is the
\emph{relational} semantics \autocite{frisch-2009-undefinedness}. In the
relational semantics the value $\bot$ percolates up through the term until it
reaches a Boolean subterm where it becomes $\false$. For the example
reaches a Boolean subterm where it becomes $\mzninline{false}$. For the example
\begin{mzn}
/* t1 @\(\equiv\)@ */ a[7] = ⊥;
@ -91,7 +91,7 @@ original complex constraint needs to be far more complex.
The \minizinc\ compiler unrolls, flattens, and reifies \minizinc\ models
implementing the relational semantics. Assuming \texttt{i} takes values in the
set \mzninline{1..8}, and \texttt{a} has an index set \mzninline{1..5}, its
translation of the constraint in \cref{ex:cons} is
translation of the constraint in \cref{ex:hr-cons} is
\begin{mzn}
constraint b1 <-> i <= 4; % b1 holds iff i <= 4
@ -120,7 +120,7 @@ functions, is that each reified version of a constraint requires further
implementation to create, and indeed most solvers do not provide any reified
versions of their global constraints.
\begin{example}\label{ex:alldiff}
\begin{example}\label{ex:hr-alldiff}
Consider the complex constraint
\begin{mzn}
@ -201,19 +201,22 @@ expressiveness in using global constraints.
\section{Propagation Based Constraint Solving}
\jip{This section looks like background and should probably be moved}
We consider a typed set of variables $\VV = \VV_I \cup \VV_B$ made up of
\emph{integer} variables, $\VV_I$, and \emph{Boolean} variables, $\VV_b$. We use
lower case letters such as $x$ and $y$ for integer variables and letters such as
$b$ for Booleans.
%
A \emph{domain} $D$ is a complete mapping from $\VV$ to finite sets of integers
(for the variables in $\VV_I$) and to subsets of $\{\true,\false\}$ (for the
variables in $\VV_b$).
A \gls{domain} $D$ is a complete mapping from $\VV$ to finite sets of integers
(for the variables in $\VV_I$) and to subsets of
$\{\mzninline{true},\mzninline{false}\}$ (for the variables in $\VV_b$).
%
We can understand a domain $D$ as a formula $\wedge_{v \in \VV} (v \in D(v))$
stating for each variable $v$ that its value is in its domain. A \emph{false
domain} $D$ is a domain where $\exists_{v \in \VV} D(v) = \emptyset$, and
corresponds to an unsatisfiable formula.
We can understand a \gls{domain} $D$ as a formula
$\wedge_{v \in \VV} (v \in D(v))$ stating for each variable $v$ that its value
is in its domain. A \emph{false domain} $D$ is a domain where
$\exists_{v \in \VV} D(v) = \emptyset$, and corresponds to an unsatisfiable
formula.
Let $D_1$ and $D_2$ be domains and $V\subseteq\VV$. We say that $D_1$ is
\emph{stronger} than $D_2$, written $D_1 \sqsubseteq D_2$, if
@ -281,7 +284,7 @@ $\theta \in solns(c) \cap D$ s.t. $\theta(v_i) = \min D(v_i)$ and
$\min D(v_j) \leq \theta(v_j) \leq \max D(v_j), 1 \leq j \neq i \leq n$, and
similarly exists $\theta \in solns(c) \cap D$ s.t. $\theta(v_i) = \max D(v_i)$
and $\min D(v_j) \leq \theta(v_j) \leq \max D(v_j), 1 \leq j \neq i \leq n$. For
Boolean variables $v$ we assume $\false < \true$.
Boolean variables $v$ we assume $\mzninline{false} < \mzninline{true}$.
% A domain $D$ is \emph{bounds(R) consistent} for constraint $c$ if the same
% conditions as for bounds(Z) consistency hold except $\theta \in solns(c')$ where
@ -323,7 +326,10 @@ fixpoint w.r.t $\sqsubseteq$ lifted to functions.
% not going up from an empty one.)
\subsection{A Language of Constraints}\label{sec:syntax}
\subsection{A Language of Constraints}\label{sec:hr-syntax}
\jip{This introduces a subset of \minizinc, but we can probably use \microzinc\
instead.}
\newcommand{\nonterm}[1]{\textsc{#1}}
\newcommand{\subs}[1]{[\![ #1 ]\!] }
@ -414,13 +420,13 @@ Given a \syntax{<cons>} term defining the constraints of the model we can split
its \syntax{<cons>} subterms as occurring in different kinds of places: root
contexts, positive contexts, negative contexts, and mixed contexts. A Boolean
subterm $t$ of constraint $c$ is in a \emph{root context} iff there is no
solution of $c\subs{t/\false}$, that is $c$ with subterm $t$ replaced by
$\false$.\footnote{For the definitions of context we assume that the subterm $t$
solution of $c\subs{t/\mzninline{false}}$, that is $c$ with subterm $t$ replaced by
$\mzninline{false}$.\footnote{For the definitions of context we assume that the subterm $t$
is uniquely defined by its position in $c$, so the replacement is of exactly
one subterm.} Similarly, a subterm $t$ of constraint $c$ is in a
\emph{positive context} iff for any solution $\theta$ of $c$ then $\theta$ is
also a solution of $c\subs{t/\true}$; and a \emph{negative context} iff for any
solution $\theta$ of $c$ then $\theta$ is also a solution of $c\subs{t/\false}$.
also a solution of $c\subs{t/\mzninline{true}}$; and a \emph{negative context} iff for any
solution $\theta$ of $c$ then $\theta$ is also a solution of $c\subs{t/\mzninline{false}}$.
The remaining Boolean subterms of $c$ are in \emph{mixed} contexts. While
determining contexts according to these definitions is hard, there are simple
syntactic rules which can determine the correct context for most terms, and the
@ -432,7 +438,7 @@ Consider the constraint expression
constraint x > 0 /\ (i <= 4 -> x + bool2int(b) = 5);
\end{mzn}
then $x > 0$ is in the root context, $i \leq 4$ is in a negative context,
then \mzninline{x > 0} is in the root context, $i \leq 4$ is in a negative context,
$x + \texttt{bool2int}(b) = 5$ is in a positive context, and $b$ is in a mixed
context. If the last equality were $x + \texttt{bool2int}(b) \geq 5$ then $b$
would be in a positive context.
@ -452,10 +458,10 @@ for each subterm bottom up using approximation.
% \cons{} subterms as occurring in kinds of places: positive contexts, negative
% contexts, and mixed contexts. A Boolean subterm $t$ of constraint $c$, written
% $c[t]$, is in a \emph{positive context} iff for any solution $\theta$ of $c$
% then $\theta$ is also a solution of $c[\true]$, that is $c$ with subterm $t$
% replaced by $\true$. Similarly, a subterm $t$ of constraint $c$ is in a
% then $\theta$ is also a solution of $c[\mzninline{true}]$, that is $c$ with subterm $t$
% replaced by $\mzninline{true}$. Similarly, a subterm $t$ of constraint $c$ is in a
% \emph{negative context} iff for any solution $\theta$ of $c$ then $\theta$ is
% also a solution of $c[\false]$. The remaining Boolean subterms of $c$ are in
% also a solution of $c[\mzninline{false}]$. The remaining Boolean subterms of $c$ are in
% mixed contexts.
% \begin{example}
@ -486,7 +492,7 @@ set of possible values of $t$ are included in the domain of $a$,
$poss(t) \subseteq \dom(a)$.
\section{Flattening with Full Reification}
\label{sec:translation}
\label{sec:hr-translation}
Constraint problems formulated in \minizinc\ are solved by translating them to a
simpler, solver-specific subset of \minizinc, called \flatzinc. \flatzinc
@ -534,13 +540,13 @@ detailed discussion) but makes the pseudo-code much longer.
The complex part of flattening arises in flattening the constraints, we shall
ignore the declarations part for flattening and assume that any objective
function term $e$ is replaced by a new variable $obj$ and a constraint item
$\texttt{constraint}~obj = e$ which will then be flattened as part of the
model.
function term \mzninline{e} is replaced by a new variable \mzninline{obj} and a
constraint item \mzninline{constraint obj = e} which will then be flattened as
part of the model.
\subsection{Flattening Constraints}
Flattening a constraint $c$ in context $\ctxt$, $\flatc(c,\ctxt)$, returns a
Flattening a constraint $c$ in context \ctxt, $\flatc(c,\ctxt)$, returns a
Boolean literal $b$ representing the constraint and as a side effect adds a set
of constraints (the flattening) $S$ to the store such that
$S \models b \full c$.
@ -616,7 +622,7 @@ actual flattening rules only differentiate between $\rootc$ and other contexts.
The importance of the $\pos$ context is that \texttt{let} expressions within a
$\pos$ context can declare new variables. The important of the $\negc$ context
is to track $\pos$ context arising under double negations. Note that flattening
in the root context always returns a Boolean $b$ made equivalent to $\true$ by
in the root context always returns a Boolean $b$ made equivalent to $\mzninline{true}$ by
the constraints in $S$. For succinctness we use the notation $\new b$ ($\new v$)
to introduce a fresh Boolean (resp.\ integer) variable and return the name of
the variable.
@ -628,7 +634,7 @@ again. We retrieve the result of the previous flattening $h$ and the previous
context $prev$. If the expression previously appeared at the root then we simply
reuse the old value, since regardless of the new context the expression must
hold. If the new context is $\rootc$ we reuse the old value but require it to be
$\true$ in $S$, and modify the hash table to record the expression appears at
$\mzninline{true}$ in $S$, and modify the hash table to record the expression appears at
the root. If the expression appeared in a mixed context we simply reuse the old
result since this is the most constrained context. Similarly if the previous
context is the same as the current one we reuse the old result. In the remaining
@ -683,7 +689,7 @@ expression using function \textsf{flatlet} which returns the extracted
constraint and a rewritten term (not used in this case, but used in \flatt{}).
The constraints returned by function \textsf{flatlet} are then flattened.
Finally if we are in the root context, we ensure that the Boolean $b$ returned
must be $\true$ by adding $b$ to $S$.
must be $\mzninline{true}$ by adding $b$ to $S$.
{
\begin{tabbing}
@ -701,8 +707,8 @@ $\tuple{h,\rootc}$; \textbf{return} $h$
\> \textbf{if} ($\fixed(c)$) $b$ := $\eval(c)$ \\
\> \textbf{else} \\
\> \> \textbf{switch} $c$ \\
%\> \> \textbf{case} \texttt{true}: $b$ := $\true$;\\
%\> \> \textbf{case} \texttt{false}: $b$ := $\false$;\\
%\> \> \textbf{case} \texttt{true}: $b$ := $\mzninline{true}$;\\
%\> \> \textbf{case} \texttt{false}: $b$ := $\mzninline{false}$;\\
\> \> \textbf{case} $b'$ (\syntax{<bvar>}): $b$ := $b'$; \\
\> \> \textbf{case} $t_1$ $r$ $t_2$ (\syntax{<relop>}): \\
\>\>\> $\tuple{v_1, b_1}$ := $\flatt(t_1,\ctxt)$;
@ -739,7 +745,7 @@ $S$ \cupp $\{ \new b \full (\flatc(c_1,\mix) \full
%\mathit{safeelement}(v, [b_1, \ldots, b_n], b'),
%b'' \full v \in \{1,..,n\} \} $ \\
\>\>\> \textbf{if} ($\ctxt = \rootc$) $S$ \cupp $\{\texttt{element}(v', [b_1, \ldots, b_n],
\true)\}$; $b$ := $\true$ \\
\mzninline{true})\}$; $b$ := $\mzninline{true}$ \\
\>\>\> \textbf{else} \\
\>\>\>\> $S$ \cupp
$\{ \new b \full (b_{n+1} \wedge \new b' \wedge \new b''), \new v' \in \{1,
@ -755,7 +761,7 @@ b'' \full v = v', b'' \full v \in \{1,\ldots,n\} \} $
\> \> \> \> \textbf{else} $S$ \cupp $\{ p\_reif(v_1,\ldots,v_n,\new b'),
\new b \Leftrightarrow b' \wedge \bigwedge_{j=1}^n b_j\}$ \\
\> \> \> \textbf{else} \\
\>\> \> \> $b$ := $\true$; $S$ \cupp $\{ p(v_1, \ldots, v_n) %, \bigwedge_{j=1}^n b_j
\>\> \> \> $b$ := $\mzninline{true}$; $S$ \cupp $\{ p(v_1, \ldots, v_n) %, \bigwedge_{j=1}^n b_j
\}$
\\
\>\> \textbf{case} $p$ ($t_1, \ldots, t_n$) (\syntax{<pred>}): user-defined predicate \\
@ -771,7 +777,7 @@ b_j\}$
\>\> \textbf{case} $p$ ($t_1, \ldots, t_n$) (\syntax{<pred>}) built-in predicate: \\
\> \> \> \textbf{if} ($\ctxt \neq \rootc$) \textbf{abort} \\
\> \> \> \textbf{foreach}($j \in 1..n$) $\tuple{v_j, \_}$ := $\flatt(t_j,\rootc)$; \\
\>\> \> $b$ := $\true$; $S$ \cupp $\{ p(v_1, \ldots, v_n)\}$
\>\> \> $b$ := $\mzninline{true}$; $S$ \cupp $\{ p(v_1, \ldots, v_n)\}$
\\
\>\> \textbf{case} $p$ ($t_1, \ldots, t_n$) (\syntax{<pred>}): user-defined predicate \\
\>\> \> \textbf{let} $p(x_1, \ldots, x_n) = c_0$ be defn of $p$ \\
@ -886,7 +892,7 @@ $\flatt(t,\ctxt)$ flattens an integer term $t$ in context $\ctxt$. It returns a
tuple $\tuple{v,b}$ of an integer variable/value $v$ and a Boolean literal $b$,
and as a side effect adds constraints to $S$ so that
$S \models b \full (v = t)$. Note that again flattening in the root context
always returns a Boolean $b$ made equivalent to $\true$ by the constraints in
always returns a Boolean $b$ made equivalent to $\mzninline{true}$ by the constraints in
$S$.
Flattening first checks if the same expression has been flattened previously and
@ -936,13 +942,13 @@ $\tuple{h,b',\rootc}$; \textbf{return} $\tuple{h,b'}$
\>\> \textbf{if} ($prev = \mix \vee prev = \ctxt$)
\textbf{return} $\tuple{h,b'}$ \\
\>\> $\ctxt$ := $\mix$ \\
\> \textbf{if} ($\fixed(t)$) $v$ := $\eval(t)$; $b$ := $\true$ \\
\> \textbf{if} ($\fixed(t)$) $v$ := $\eval(t)$; $b$ := $\mzninline{true}$ \\
\> \textbf{else} %\\
%% \> \> \textbf{if} ($t$ is marked \emph{total}) $\ctxt$ := $\rootc$ \\
%\> \>
\textbf{switch} $t$ \\
%\> \> \textbf{case} $i$ (\intc): $v$ := $i$; $b$ := $\true$ \\
\> \> \textbf{case} $v'$ (\syntax{<ivar>}): $v$ := $v'$; $b$ := $\true$\\
%\> \> \textbf{case} $i$ (\intc): $v$ := $i$; $b$ := $\mzninline{true}$ \\
\> \> \textbf{case} $v'$ (\syntax{<ivar>}): $v$ := $v'$; $b$ := $\mzninline{true}$\\
\> \> \textbf{case} $t_1$ $a$ $t_2$ (\syntax{<arithop>}): \\
\> \> \>
$\tuple{v_1,b_1}$ := $\flatt(t_1,\ctxt)$;
@ -1035,14 +1041,16 @@ $S$ \cupp $\{ \new b \full (b_1 \wedge b_2)\}$
\> \textbf{return} $\tuple{v,b}$
\end{tabbing}
\begin{example}\label{ex:flat}
\begin{example}\label{ex:hr-flat}
Consider the flattening of the expression
$$
a[x] + bool2int(x > y) - bool2int(x > z) \geq 4 \vee
(a[x] \leq 2 \wedge x > y \wedge (x > z \rightarrow b))
$$
The different terms and subterms of the expression are
illustrated and numbered in Figure~\ref{fig:tree}.
The different terms and subterms of the expression are illustrated and
numbered in \cref{fig:hr-tree}.
\begin{figure}[t]
$$
\xymatrix@R=1em@C=0.6em{
@ -1060,7 +1068,7 @@ $S$ \cupp $\{ \new b \full (b_1 \wedge b_2)\}$
}
$$
\caption{An expression to be flattened with non-leaf (sub-)terms
numbered.\label{fig:tree}}
numbered.\label{fig:hr-tree}}
\end{figure}
Note that positions 0 is in the $\rootc$ context, while position 10 is in a
$\negc$ context, positions 13 and 15 are in a $\mix$ context, and the rest are
@ -1171,7 +1179,7 @@ $S$ \cupp $\{ \new b \full (b_1 \wedge b_2)\}$
% The pseudo-code for \flatc($b$,$c$) flattens a constraint expression $c$ to be
% equal to $b$, returning a set of constraints implementing $b \full c$. We
% flatten a whole model $c$ using $\flatc(\true, c)$. In the pseudo-code the
% flatten a whole model $c$ using $\flatc(\mzninline{true}, c)$. In the pseudo-code the
% expressions $\new b$ and $\new v$ create a new Boolean and integer variable
% respectively.
@ -1193,8 +1201,8 @@ $S$ \cupp $\{ \new b \full (b_1 \wedge b_2)\}$
% \> \textbf{case} \texttt{not} $c_1$:
% \textbf{return} $\flatc(\new b_1, c_1) \cup \{ b \full \neg b_1 \}$ \\
% \> \textbf{case} $c_1$ \verb+/\+ $c_2$: \=
% \textbf{if} ($b \equiv \true$) \textbf{return} $\flatc(\true, c_1) \cup
% \flatc(\true, c_2)$ \\
% \textbf{if} ($b \equiv \mzninline{true}$) \textbf{return} $\flatc(\mzninline{true}, c_1) \cup
% \flatc(\mzninline{true}, c_2)$ \\
% \> \> \textbf{else} \textbf{return} $\flatc(\new b_1, c_1) \cup
% \flatc(\new b_2, c_2) \cup \{ b \full (b_1 \wedge b_2)\}$ \\
% xx\=xx\=xx\=xx\=xx\=xx\=xx\= \kill
@ -1208,7 +1216,7 @@ $S$ \cupp $\{ \new b \full (b_1 \wedge b_2)\}$
% \flatc(\new b_2, c_2) \cup \{ b \full (b_1 \full
% b_2)\}$ \\
% \> \textbf{case} $p$ ($t_1, \ldots, t_n$) (\syntax{<pred>}): \\
% \> \> \textbf{if} ($b \equiv \true$) \textbf{return} $\safen(b,
% \> \> \textbf{if} ($b \equiv \mzninline{true}$) \textbf{return} $\safen(b,
% \cup_{j=1}^n \flatt(\new v_j, t_j)) \cup \{ p(v_1, \ldots, v_n) \}$ \\
% \> \> \textbf{else} \textbf{abort}
% \end{tabbing}
@ -1237,21 +1245,22 @@ The procedure \safen($b, C$) enforces the relational semantics for unsafe
expressions, by ensuring that the unsafe relational versions of partial
functions are made safe. Note that to implement the \emph{strict semantics} as
opposed to the relational semantics we just need to define $\safen(b,C) = C$. If
$b \equiv \true$ then the relational semantics and the strict semantics
$b \equiv \mzninline{true}$ then the relational semantics and the strict semantics
coincide, so nothing needs to be done. The same is true if the set of
constraints $C$ is safe. For $div(x,y,z)$, the translation introduces a new
variable $y'$ which cannot be 0, and equates it to $y$ if $y \neq 0$. The
constraint $div(x,y',z)$ never reflects a partial function application. The new
variable $b'$ captures whether the partial function application returns a non
$\bot$ value. For $\element(v,a,x)$, it introduces a new variable $v'$ which
only takes values in $\dom(a)$ and forces it to equal $v$ if $v \in \dom(a)$. A
partial function application forces $b = \false$ since it is the conjunction of
the new variables $b'$. The \%HALF\% comments will be explained later.
$\bot$ value. For \mzninline{element(v, a, x)}, it introduces a new variable $v'$
which only takes values in $\dom(a)$ and forces it to equal $v$ if
$v \in \dom(a)$. A partial function application forces $b = \mzninline{false}$ since it is
the conjunction of the new variables $b'$. The \%HALF\% comments will be
explained later.
\begin{tabbing}
xx\=xx\=xx\=xx\=xx\=xx\=xx\= \kill
\safen($b$,$C$) \\
\> \textbf{if} ($b \equiv \true$) \textbf{return} $C$ \\
\> \textbf{if} ($b \equiv \mzninline{true}$) \textbf{return} $C$ \\
\> \textbf{if} ($C$ is a set of safe constraints) \textbf{return} $C$ \\
\> $B$ := $\emptyset$; $S$ := $\emptyset$ \\
\> \textbf{foreach} $c \in C$ \\
@ -1261,13 +1270,13 @@ xx\=xx\=xx\=xx\=xx\=xx\=xx\= \kill
= y', div(x,y',z) \}$ \\
\> \> \> \%HALF\% $S$ := $S \cup \{ b' \full y \neq 0, b' \half
div(x,y,z)\}$ \\
\> \> \textbf{else if} $c \equiv \element(v,a,x)$ and $v$ can take a value outside the domain of $a$) \\
\> \> \textbf{else if} \(c \equiv \mzninline{element(v, a, x)}\) and $v$ can take a value outside the domain of $a$) \\
\> \> \> $B$ := $B \cup \{ \new b' \}$ \\
\> \> \> $S$ := $S \cup \{ \new v' \in \dom(a),
b' \full v \in \dom(a), b' \full v = v',
\element(v',a,x)\}$ \\
\mzninline{element(v', a, x)}\}$ \\
\> \> \> \%HALF\% $S$ := $S \cup \{ b' \full v \in \dom(a), b' \half
\element(v,a,x)\}$ \\
\mzninline{element(v, a, x)}\}$ \\
\> \> \textbf{else} $S$ := $S \cup \{c\}$ \\
\> \> \textbf{return} $S \cup \{b \full \wedge_{b' \in B} b'
\})$
@ -1279,7 +1288,7 @@ renamed-apart copies of already-generated constraints, but for simplicity of
presentation, we omit the algorithms we use to do this.
\section{Half Reification}
\label{sec:halfreif}
\label{sec:hr-halfreif}
Given a constraint $c$, the \emph{half-reified version} of $c$ is a constraint
of the form $b \half c$ where $b \not\in vars(c)$ is a Boolean variable.
@ -1288,11 +1297,11 @@ We can construct a propagator $f_{b \half c}$ for the half-reified version of
$c$, $b \half c$, using the propagator $f_c$ for $c$.
$$
\begin{array}{rcll}
f_{b \half c}(D)(b) & = & \{ \false \} \cap D(b) & \text{if~} f_c(D) \text{~is a false domain} \\
f_{b \half c}(D)(b) & = & \{ \mzninline{false} \} \cap D(b) & \text{if~} f_c(D) \text{~is a false domain} \\
f_{b \half c}(D)(b) & = & D(b) & \text{otherwise} \\
f_{b \half c}(D)(v) & = & D(v) & \text{if~} v \in vars(c) \text{~and~} \false \in D(b) \\
f_{b \half c}(D)(v) & = & D(v) & \text{if~} v \in vars(c) \text{~and~} \mzninline{false} \in D(b) \\
f_{b \half c}(D)(v) & = & f_c(D)(v) & \text{if~} v \in vars(c)
\text{~and~} \false \not\in D(b)
\text{~and~} \mzninline{false} \not\in D(b)
\end{array}
$$
@ -1325,27 +1334,28 @@ $$
% Given this separation we can improve the definition of $f_{b \half c}$ above by
% replacing the check ``$f_c(D)$ is a false domain'' by ``$f_{check(c)}(D)$ is a
% false domain''. In practice it means that the half-reified propagator can be
% implemented to only perform the checking part until $D(b) = \{\true\}$, when it
% implemented to only perform the checking part until $D(b) = \{\mzninline{true}\}$, when it
% needs to perform both.
In practice most propagator implementations for $c$ first check whether $c$ is
satisfiable, before continuing to propagate. For example,
$\sum_i a_i x_i \leq a_0$ determines $L = \sum_i min_D(a_i x_i) - a_0$ and fails
if $L > 0$ before propagating; Regin's domain propagator for
\alldiff$([x_1, \ldots, x_n]$) determines a maximum matching between variables
and values first, if this is not of size $n$ it fails before propagating; the
timetable \texttt{cumulative} constraint determines a profile of necessary
resource usage, and fails if this breaks the resource limit, before considering
propagation. We can implement the propagator for $f_{b \half c}$ by only
performing the checking part until $D(b) = \{\true\}$.
In practice most propagator implementations for \mzninline{c} first check
whether \mzninline{c} is satisfiable, before continuing to propagate. For
example, \mzninline{sum(i in N)(a[i] * x[i]) <= C} determines \mzninline{L =
sum(i in N)(lb(a[i] * x[i])) - C} and fails if \mzninline{L > 0} before
propagating; Regin's domain propagator for \mzninline{all_different(X)}
determines a maximum matching between variables and values first, if this is not
of size \mzninline{length(X)} it fails before propagating; the timetable
\texttt{cumulative} constraint determines a profile of necessary resource usage,
and fails if this breaks the resource limit, before considering propagation. We
can implement the propagator for $f_{\mzninline{b -> c}}$ by only performing the
checking part until $D(\texttt{b}) = \{\mzninline{true}\}$.
Half reification naturally encodes the relational semantics for partial function
applications in positive contexts. We associate a Boolean variable $b$ with each
applications in positive contexts. We associate a Boolean variable \texttt{b} with each
Boolean term in an expression, and we ensure that all unsafe constraints are
half-reified using the variable of the nearest enclosing Boolean term.
\begin{example}
Consider flattening of the constraint of \cref{ex:cons}. First we will convert
Consider flattening of the constraint of \cref{ex:hr-cons}. First we will convert
it to an equivalent expression with only positive contexts
\begin{mzn}
@ -1364,10 +1374,10 @@ half-reified using the variable of the nearest enclosing Boolean term.
constraint b1 \/ b2;
\end{mzn}
The unsafe $\element$ constraint is half reified with the name of its nearest
The unsafe \mzninline{element} constraint is half reified with the name of its nearest
enclosing Boolean term. Note that if $i = 7$ then the second constraint makes
$b2 = \false$. Given this, the final constraint requires $b1 = \true$, which
in turn requires $i > 4$. Since this holds, the whole constraint is $\true$
$b2 = \mzninline{false}$. Given this, the final constraint requires $b1 = \mzninline{true}$, which
in turn requires $i > 4$. Since this holds, the whole constraint is $\mzninline{true}$
with no restrictions on $x$.
\end{example}
@ -1376,7 +1386,7 @@ assume that each global constraint predicate $p$ is available in half-reified
form. Recall that this places no new burden on the solver implementer.
\begin{example}
Consider the constraint of \cref{ex:alldiff}. Half reification results in
Consider the constraint of \cref{ex:hr-alldiff}. Half reification results in
\begin{mzn}
constraint b1 -> i > 4;
@ -1385,14 +1395,15 @@ form. Recall that this places no new burden on the solver implementer.
constraint b1 \/ b2 % b1 or b2
\end{mzn}
We can easily modify any existing propagator for \alldiff to support the
half-reified form, hence this model is executable by our constraint solver.
We can easily modify any existing propagator for \mzninline{all_different} to
support the half-reified form, hence this model is executable by our
constraint solver.
\end{example}
Half reification can lead to more efficient constraint solving, since it does
not propagate unnecessarily.
\begin{example}\label{ex:cumul}
\begin{example}\label{ex:hr-cumul}
Consider the task decomposition of a \texttt{cumulative} constraint (see \eg\
\autocite{schutt-2009-cumulative}) which includes constraints of the form
@ -1415,7 +1426,7 @@ not propagate unnecessarily.
\end{mzn}
Whenever the start time of task $i$ is constrained so that it does not overlap
time $s[j]$, then $b3[i]$ is fixed to $\false$ and $a[i]$ to 0, and the long
time $s[j]$, then $b3[i]$ is fixed to $\mzninline{false}$ and $a[i]$ to 0, and the long
linear sum is awoken. But this is useless, since it cannot cause failure.
% Half-reification of this expression which appears in a negative context produces
@ -1442,7 +1453,7 @@ not propagate unnecessarily.
\pjs{Add linearization example here!}
Half reification can cause propagators to wake up less frequently, since
variables that are fixed to $\true$ by full reification will never be fixed by
variables that are fixed to $\mzninline{true}$ by full reification will never be fixed by
half reification. This is advantageous, but a corresponding disadvantage is that
variables that are fixed can allow the simplification of the propagator, and
hence make its propagation faster. We can reduce this disadvantage by fully
@ -1463,7 +1474,7 @@ rewriting used with full reification.
The procedure $\halfc(b,c)$ defined below returns a set of constraints
implementing the half-reification $b \half c$. We flatten a constraint item
$\texttt{constraint}~c$ using $\halfc(\true, c)$. The half-reification
$\texttt{constraint}~c$ using $\halfc(\mzninline{true}, c)$. The half-reification
flattening transformation uses half reification whenever it is in a positive
context. If it encounters a constraint $c_1$ in a negative context, it negates
the constraint if it is safe, thus creating a new positive context. If this is
@ -1541,8 +1552,8 @@ $\tuple{h,\rootc}$; \textbf{return} $h$
\> \textbf{if} ($\fixed(c)$) $b$ := $\eval(c)$ \\
\> \textbf{else} \\
\> \> \textbf{switch} $c$ \\
%\> \> \textbf{case} \texttt{true}: $b$ := $\true$;\\
%\> \> \textbf{case} \texttt{false}: $b$ := $\false$;\\
%\> \> \textbf{case} \texttt{true}: $b$ := $\mzninline{true}$;\\
%\> \> \textbf{case} \texttt{false}: $b$ := $\mzninline{false}$;\\
\> \> \textbf{case} $b'$ (\syntax{<bvar>}): $b$ := $b'$; \\
\> \> \textbf{case} $t_1$ $r$ $t_2$ (\syntax{<relop>}): \\
\> \> \> \textbf{switch} $r$ \\
@ -1586,7 +1597,7 @@ $S$ \cupp $\{ \new b \half (\flatc(c_1,\mix) \full
%\mathit{safeelement}(v, [b_1, \ldots, b_n], b'),
%b'' \full v \in \{1,..,n\} \} $ \\
\>\>\> \textbf{if} ($\ctxt = \rootc$)
$S$ \cupp $\{\texttt{element}(v, [b_1, \ldots, b_n], \true), \new b\}$ \\
$S$ \cupp $\{\texttt{element}(v, [b_1, \ldots, b_n], \mzninline{true}), \new b\}$ \\
\>\>\> \textbf{else}
$S$ \cupp $\{ \new b \half (b_{n+1} \wedge \new b'), b \half
\texttt{element}(v, [b_1, \ldots, b_n], b') \}$ \\
@ -1601,7 +1612,7 @@ $S$ \cupp $\{ \new b \half (b_{n+1} \wedge \new b'), b \half
\> \> \> \textbf{foreach}($j \in 1..n$) $\tuple{v_j, b_j}$ :=
$\halft(t_j,\ctxt,\iboth)$; \\
\> \> \> \textbf{if} ($\ctxt = \rootc$)
$b$ := $\true$; $S$ \cupp $\{ p(v_1, \ldots, v_n) %, \bigwedge_{j=1}^n b_j
$b$ := $\mzninline{true}$; $S$ \cupp $\{ p(v_1, \ldots, v_n) %, \bigwedge_{j=1}^n b_j
\}$
\\
\>\>\> \textbf{else} %($\ctxt = \pos$) \\ \> \> \> \>
@ -1626,7 +1637,7 @@ b_j\}$
\>\> \textbf{case} $p$ ($t_1, \ldots, t_n$) (\syntax{<pred>}) built-in predicate: \\
\> \> \> \textbf{if} ($\ctxt \neq \rootc$) \textbf{abort} \\
\> \> \> \textbf{foreach}($j \in 1..n$) $\tuple{v_j, \_}$ := $\flatt(t_j,\rootc)$; \\
\>\> \> $b$ := $\true$; $S$ \cupp $\{ p(v_1, \ldots, v_n)\}$
\>\> \> $b$ := $\mzninline{true}$; $S$ \cupp $\{ p(v_1, \ldots, v_n)\}$
\\
\>\> \textbf{case} $p$ ($t_1, \ldots, t_n$) (\syntax{<pred>}): user-defined predicate \\
\>\> \> \textbf{let} $p(x_1, \ldots, x_n) = c_0$ be defn of $p$ \\
@ -1689,8 +1700,8 @@ $\tuple{h,\rootc}$; \textbf{return} $h$\\
\> \textbf{if} ($\fixed(c)$) $b$ := $\neg \eval(c)$ \\
\> \textbf{else} \\
\> \> \textbf{switch} $c$ \\
%\> \> \textbf{case} \texttt{true}: $b$ := $\true$;\\
%\> \> \textbf{case} \texttt{false}: $b$ := $\false$;\\
%\> \> \textbf{case} \texttt{true}: $b$ := $\mzninline{true}$;\\
%\> \> \textbf{case} \texttt{false}: $b$ := $\mzninline{false}$;\\
\> \> \textbf{case} $b'$ (\syntax{<bvar>}): $S$ \cupp $\{\new b \half \neg b'\}$; \\
\> \> \textbf{case} $t_1$ $r$ $t_2$ (\syntax{<relop>}): \\
\> \> \> \textbf{if} ($t_1$ and $t_2$ are safe) \\
@ -1786,7 +1797,7 @@ bounds. If the previous context was root or the context are the same we examine
the bounds. If they are the same we reuse the result, otherwise we set the
$\bnds$ to $\iboth$ and reflatten since we require both bounds (perhaps in
different situations). Otherwise the new context is $\rootc$ and the previous
$\pos$, we force $b'$ to be $\true$ in $S$, and then reuse the result if the
$\pos$, we force $b'$ to be $\mzninline{true}$ in $S$, and then reuse the result if the
bounds are the same, or set $\bnds$ to $\iboth$ and reflatten. We store the
result found at the end of flattening, note that we only need one entry: context
can move from $\pos$ to $\rootc$ and bounds can move from one of $\iabove$ or
@ -1853,13 +1864,13 @@ xx\=xx\=xx\=xx\=xx\=xx\=xx\= \kill
\> \> \> \textbf{if} ($pbnds = \bnds$) hhash[$t$] := $\tuple{h,b',\rootc,\bnds}$; \textbf{return} $\tuple{h,b'}$
\\
\> \> \> \textbf{else} $\bnds$ := $\iboth$ \\
\> \textbf{if} ($\fixed(t)$) $v$ := $\eval(t)$; $b$ := $\true$ \\
\> \textbf{if} ($\fixed(t)$) $v$ := $\eval(t)$; $b$ := $\mzninline{true}$ \\
\> \textbf{else} %\\
%% \> \> \textbf{if} ($t$ is marked \emph{total}) $\ctxt$ := $\rootc$ \\
%\> \>
\textbf{switch} $t$ \\
%\> \> \textbf{case} $i$ (\intc): $v$ := $i$; $b$ := $\true$ \\
\> \> \textbf{case} $v'$ (\syntax{<ivar>}): $v$ := $v'$; $b$ := $\true$\\
%\> \> \textbf{case} $i$ (\intc): $v$ := $i$; $b$ := $\mzninline{true}$ \\
\> \> \textbf{case} $v'$ (\syntax{<ivar>}): $v$ := $v'$; $b$ := $\mzninline{true}$\\
\> \> \textbf{case} $t_1$ $a$ $t_2$ (\syntax{<arithop>}): \\
\> \> \> \textbf{switch} $a$ \\
\> \> \> \textbf{case} $+$:
@ -1957,8 +1968,8 @@ $S$ \cupp $\{ \new b \half (b_1 \wedge b_2)\}$
% context, negates $c_1$ and half-reifies the result if $c_1$ is safe and in a
% negative context, and uses full flattening otherwise.
\begin{example}\label{ex:half}
Now lets consider half reifying the expression of \cref{ex:flat}.
\begin{example}\label{ex:hr-half}
Now lets consider half reifying the expression of \cref{ex:hr-flat}.
The result of flattening with half reification is:
@ -2029,7 +2040,7 @@ $S$ \cupp $\{ \new b \half (b_1 \wedge b_2)\}$
transformation will construct many implications of the form $b \half b'$, note
that the constraint $b \half b_1 \wedge \cdots \wedge b_n$ is equivalent to
$b \half b_1, b \half b_2, \ldots, b \half b_n$. Just as in
Example~\ref{ex:half} above we can simplify the result of half reification. For
\cref{ex:hr-half} above we can simplify the result of half reification. For
each $b'$ which appears only on the right of one constraint of the form
$b \half b'$ and other constraints of the form $b' \half c_i$ we remove the
first constraint and replace the others by $b \half c_i$.
@ -2047,17 +2058,17 @@ To do so independent of propagation strength, we define the behaviour of the
propagators of the half-reified forms in terms of the full reified propagator.
$$
\begin{array}{rcll}
f_{b \half c}(D)(b) & = & D(b) \cap ( \{ \false \} \cup f_{b \full c}(D)(b)) \\
f_{b \half c}(D)(v) & = & D(v) & \texttt{if}~v \in vars(c),~\false \in D(b) \\
f_{b \half c}(D)(b) & = & D(b) \cap ( \{ \mzninline{false} \} \cup f_{b \full c}(D)(b)) \\
f_{b \half c}(D)(v) & = & D(v) & \texttt{if}~v \in vars(c),~\mzninline{false} \in D(b) \\
f_{b \half c}(D)(v) & = & f_{b \full c}(D)(v) & \texttt{if}~v \in vars(c),~\text{otherwise}
\end{array}
$$
and
$$
\begin{array}{rcll}
f_{b' \half \neg c}(D)(b') & = & D(b') & \texttt{if}~\{ \false \} \in f_{b \full c}(D)(b) \\
f_{b' \half \neg c}(D)(b') & = & D(b') \cap \{ \false\} & \text{otherwise}\\
f_{b' \half \neg c}(D)(v) & = & D(v) & \texttt{if}~v \in vars(c),~\false \in D(b') \\
f_{b' \half \neg c}(D)(b') & = & D(b') & \texttt{if}~\{ \mzninline{false} \} \in f_{b \full c}(D)(b) \\
f_{b' \half \neg c}(D)(b') & = & D(b') \cap \{ \mzninline{false}\} & \text{otherwise}\\
f_{b' \half \neg c}(D)(v) & = & D(v) & \texttt{if}~v \in vars(c),~\mzninline{false} \in D(b') \\
f_{b' \half \neg c}(D)(v) & = & f_{b \full c}(D)(v) & \texttt{if}~v \in vars(c),~\text{otherwise}
\end{array}
$$
@ -2067,7 +2078,7 @@ half reified split versions of the propagator should act.
In order to prove the same behaviour of the split reification versions we must
assume that $f_{b \full c}$ is \emph{reified-consistent} which is defined as
follows: suppose $f_{b \full c}(D)(v) \neq D(v), v \not\equiv b$, then
$f_{b \full c}(D)(b) \neq \{ \false, \true\}$. That is if the propagator ever
$f_{b \full c}(D)(b) \neq \{ \mzninline{false}, \mzninline{true}\}$. That is if the propagator ever
reduces the domain of some variable, then it must also fix the Boolean variable
$b$. This is sensible since if $b$ is not fixed then every possible value of $v$
is consistent with constraint $b \full c$ regardless of the domain $D$. This is
@ -2105,14 +2116,14 @@ the usual case for such a trivial propagator.
% $D'' = \solv(\{ f_{b \half c}, f_{b' \half \neg c}, f_{b' \full \neg b}\}, D)$.
% The proof is by cases of $D$.
% \textbf{(a)} Suppose $D(b) = \{\true,\false\}$. \textbf{(a-i)} Suppose
% \textbf{(a)} Suppose $D(b) = \{\mzninline{true},\mzninline{false}\}$. \textbf{(a-i)} Suppose
% $f_{b \full c}(D) = D$. Then clearly $f_{b \half c}(D) = D$ by definition, and
% similarly $f_{b' \half \neg c}(D) = D$. Hence $D_1 = D_2 = D$. Note that since
% $f_{b \full c}$ is reified consistent, then $f_{b \full c}(D) \neq D$ implies
% that $f_{b \full c}(D)(b) \neq \{ \false, \true \}$. \textbf{(a-ii)} Suppose
% $f_{b \full c}(D)(b) = \{ \true \}$. Let $D_1 = f_{b' \half \neg c}(D)$, then
% $D_1(b') = \{ \false \}$ by definition and $D_1(v) = D(v), v \not\equiv b'$.
% Let $D_2 = f_{b' \full \neg b}(D_1)$ then $D_2(b)= \{ \true \}$ and
% that $f_{b \full c}(D)(b) \neq \{ \mzninline{false}, \mzninline{true} \}$. \textbf{(a-ii)} Suppose
% $f_{b \full c}(D)(b) = \{ \mzninline{true} \}$. Let $D_1 = f_{b' \half \neg c}(D)$, then
% $D_1(b') = \{ \mzninline{false} \}$ by definition and $D_1(v) = D(v), v \not\equiv b'$.
% Let $D_2 = f_{b' \full \neg b}(D_1)$ then $D_2(b)= \{ \mzninline{true} \}$ and
% $D_2(v) = D(v), v \not\in \{b,b'\}$. Then
% $D_3 = f_{b \half c}(D_2) = f_{b \full c}(D_2)$ by definition, and by
% idempotence of $f_{b \full c}$ and since $D_2$ and $D$ differ only on $b$ and
@ -2121,9 +2132,9 @@ the usual case for such a trivial propagator.
% $f_{b \half c}$, $f_{b' \full \neg b}$ and $f_{b' \half \neg c}$ and hence
% $D'' = D_3$. But also $D' = f_{b' \full \neg b}(f_{b \full c}(D)) = D_3$ since
% $D_3$ and $f_{b \full c}(D)$ only differ on $b'$. \textbf{(a-iii)} Suppose
% $f_{b \full c}(D)(b) = \{ \false \}$. Let $D_1 = f_{b \half c}(D)$, then
% $D_1(b) = \{ \false \}$ by definition and $D_1(v) = D(v), v \not\equiv b$. Let
% $D_2 = f_{b' \full \neg b}(D_1)$ then $D_2(b')= \{ \true \}$ and
% $f_{b \full c}(D)(b) = \{ \mzninline{false} \}$. Let $D_1 = f_{b \half c}(D)$, then
% $D_1(b) = \{ \mzninline{false} \}$ by definition and $D_1(v) = D(v), v \not\equiv b$. Let
% $D_2 = f_{b' \full \neg b}(D_1)$ then $D_2(b')= \{ \mzninline{true} \}$ and
% $D_2(v) = D(v), v \not\in \{b,b'\}$. Then
% $D_3 = f_{b' \half \neg c}(D_2) = f_{b \full c}(D_2)$ by definition, and by
% idempotence of $f_{b \full c}$ and since $D_2$ and $D$ differ only on $b$ and
@ -2132,10 +2143,10 @@ the usual case for such a trivial propagator.
% $D' = f_{b' \full \neg b}(f_{b \full c}(D)) = D_3$ since $D_3$ and
% $f_{b \full c}(D)$ only differ on $b'$.
% \textbf{(b)} If $D(b) = \{\true\}$ then clearly $f_{b \full c}$ and
% \textbf{(b)} If $D(b) = \{\mzninline{true}\}$ then clearly $f_{b \full c}$ and
% $f_{b \half c}$ act identically on variables in $vars(c)$.
% \textbf{(c)} If $D(b) = \{\false\}$ then $D(b') = \{\true\}$ and clearly
% \textbf{(c)} If $D(b) = \{\mzninline{false}\}$ then $D(b') = \{\mzninline{true}\}$ and clearly
% $f_{b \full c}$ and $f_{b' \half \neg c}$ act identically on variables in
% $vars(c)$. \qed
% \end{proof}
@ -2181,7 +2192,7 @@ still fixed at the same time, but there is less overhead.
Implementing half-reification within the \minizinc\ distribution consists of two
parts: the \minizinc\ compiler needs to be extended with an implementation of
the half-reification flattening algorithm as described in \cref{sec:halfreif}
the half-reification flattening algorithm as described in \cref{sec:hr-halfreif}
and for any target solver that supports half-reification its library needs to be
appended with definitions for the predicates that are half-reified. Because the
\minizinc\ language allows for the definition of new predicates, we allow any
@ -2220,35 +2231,34 @@ has propagators for half-reified constraints we have declared the corresponding
predicates as solver builtins. Gecode provides half-reified propagators for
almost all \flatzinc\ builtins. The other targeted solvers use the linear
library. The linear library \jip{,as seen before + reference???,} already has
redefinition for their fully reified predicates, \(b \full c\). These generally
consist of two parts which are equivalent to the implications
\(b \half c \land \lnot b \half \lnot c \). Because of this, the implementations
of the half-reified version of the same predicates usually consist of using only
redefinition for their fully reified predicates, \mzninline{b <-> c}. These
generally consist of two parts which are equivalent to the implications
\mzninline{b -> c /\ not b -> not c}. Because of this, the implementations of
the half-reified version of the same predicates usually consist of using only
one of these two parts, which will eliminate the need of many constraints. We
added half-reified versions for all predicates for which a full reification was
provided.
\subsection{Implication Chain Compression}
As shown in \cref{ex:half}, flattening with half reification will in many cases
result in implication chains: \((b \half b') \land (b' \half c)\), where \(b'\)
has no other occurrences. In this case the conjunction can be replaced by
\(b \half c\) and \(b'\) can be removed from the model. The case shown in the
example can be generalised to
\((b \half b') \land \left(\forall_{i \in N} b' \half c_i \right)\), which, if
\(b'\) has no other usage in the instance, can be resolved to
\(\forall_{i \in N} b \half c_i\) after which \(b'\) can be removed from the
model.
As shown in \cref{ex:hr-half}, flattening with half reification will in many
cases result in implication chains: \mzninline{b1 -> b2 /\ b2 -> c}, where
\texttt{b2} has no other occurrences. In this case the conjunction can be
replaced by \mzninline{b1 -> c} and \texttt{b2} can be removed from the model.
The case shown in the example can be generalised to \mzninline{b1 -> b2 /\
forall(i in N)(b2 -> c[i])}, which, if \texttt{b2} has no other usage in the
instance, can be resolved to \mzninline{forall(i in N)(b1 -> c[i])} after which
\texttt{b1} can be removed from the model.
An algorithm to remove these chains of implications is best visualised through
the use of an implication graph. An implication graph \(\tuple{V,E}\) is a
directed graph where the vertices \(V\) represent the variables in the instance
and an edge \(\tuple{x,y} \in E\) represents the presence of an implication
\(x \half y\) in the instance. Additionally, for the benefit of the algorithm, a
vertex is marked when it is used in other constraints in the constraint model.
The goal of the algorithm is now to identify and remove vertices that are not
marked and have only one incoming edge. The following pseudo code provides a
formal specification for this algorithm.
\mzninline{x -> y} in the instance. Additionally, for the benefit of the
algorithm, a vertex is marked when it is used in other constraints in the
constraint model. The goal of the algorithm is now to identify and remove
vertices that are not marked and have only one incoming edge. The following
pseudo code provides a formal specification for this algorithm.
\newcommand{\setminusp}{\ensuremath{\setminus}\text{:=}~}
\begin{tabbing}
@ -2266,12 +2276,12 @@ formal specification for this algorithm.
\jip{Here we could actually show an example using a drawn graph}
The algorithm can be further improved by considering implied conjunctions. These
can be split up into multiple implications: \(b' \half \forall_{i \in N} b_i\)
is equivalent to \(\forall_{i \in N} (b' \half b_i)\). This transformation both
simplifies a complicated constraint and possibly allows for the further
compression of implication chains. It should however be noted that although this
transformation can result in an increase in the number of constraints, it
generally increases the propagation efficiency.
can be split up into multiple implications: \mzninline{b -> forall(i in
N)(bs[i])} is equivalent to \mzninline{forall(i in N)(b -> bs[i])}. This
transformation both simplifies a complicated constraint and possibly allows for
the further compression of implication chains. It should however be noted that
although this transformation can result in an increase in the number of
constraints, it generally increases the propagation efficiency.
To adjust the algorithm to simplify implied conjunctions more introspection from
the \minizinc\ compiler is required. The compiler must be able to tell if a
@ -2289,8 +2299,8 @@ available \jip{Insert new link} has our experimental \minizinc\ instances and
the infrastructure that performs the benchmarks.
The first experiment considers ``QCP-max'' problems which are defined as
quasi-group completion problems where the \alldiff constraints are soft, and the
aim is to satisfy as many of them as possible.
quasi-group completion problems where the \mzninline{all_different} constraints
are soft, and the aim is to satisfy as many of them as possible.
\begin{mzn}
int: n; % size
@ -2308,20 +2318,21 @@ predicate all_different(array[int] of var int: x) =
forall(i,j in index_set(x) where i < j)(x[i] != x[j]);
\end{mzn}
Note that this is not the same as requiring the maximum number of
disequality constraints to be satisfied. The \alldiff constraints, while
Note that this is not the same as requiring the maximum number of disequality
constraints to be satisfied. The \mzninline{all_different} constraints, while
apparently in a mixed context, are actually in a positive context, since the
maximization in fact is implemented by inequalities forcing at least some number
maximisation in fact is implemented by inequalities forcing at least some number
to be true.
In Table~\ref{tab:qcp} we compare three different resulting programs on QCP-max
problems: full reification of the model above, using the \alldiff decomposition
defined by the predicate shown (\textsf{full}), half reification of the model
using the \alldiff decomposition (\textsf{half}), and half reification using a
half-reified global \alldiff (\textsf{half-g}) implementing arc consistency
(thus having the same propagation strength as the decomposition). \jip{is this
still correct??} We use standard QCP examples from the literature, and group
them by size.
In \cref{tab:hr-qcp} we compare three different resulting programs on QCP-max
problems: full reification of the model above, using the
\mzninline{all_different} decomposition defined by the predicate shown
(\textsf{full}), half reification of the model using the
\mzninline{all_different} decomposition (\textsf{half}), and half reification
using a half-reified global \mzninline{all_different} (\textsf{half-g})
implementing arc consistency (thus having the same propagation strength as the
decomposition). \jip{is this still correct??} We use standard QCP examples from
the literature, and group them by size.
% and whether they are satisfiable or unsatisfiable as QCP problems.
We compare both a standard finite domain solver (FD) and a learning lazy clause
generation solver (FD + Explanations). We use the same fixed search strategy of
@ -2329,7 +2340,7 @@ labeling the matrix in order left-to-right from highest to lowest value for all
approaches to minimize differences in search.
\begin{table} [tb] \begin{center}
\caption{\label{tab:qcp} QCP-max problems:
\caption{\label{tab:hr-qcp} QCP-max problems:
Average time (in seconds), number of solved instances (300s timeout).}
\begin{tabular}{|l||rr|rr|rr|}
\hline
@ -2359,7 +2370,7 @@ The second experiment shows how half reification can reduce the overhead of
handling partial functions correctly. Consider the following model for
determining a prize collecting path, a simplified form of prize collecting
travelling salesman problem
\autocite{balas-1989-pctsp}, %shown in Figure~\ref{fig:pct}
\autocite{balas-1989-pctsp}, %shown in \cref{fig:hr-pct}
where the aim is define an acyclic path from node 1 along weighted edges to
collect the most weight. Not every node needs to be visited ($pos[i] = 0$).
@ -2382,12 +2393,12 @@ constraint alldifferent_except_0(next) /\ pos[1] = 1;
solve minimize sum(i in 1..n)(prize[i]);
\end{mzn}
It uses the global constraint \texttt{alldifferent\_except\_0} which constrains
each element in the $next$ array to be different or equal 0. The model has one
unsafe array lookup $pos[next[i]]$. We compare using full reification
(\textsf{full}) and half reification (\textsf{half}) to model this problem. Note
that if we extend the $pos$ array to have domain $0..n$ then the model becomes
safe, by replacing its definition with the following two lines
It uses the global constraint \mzninline{alldifferent_except_0} which constrains
each element in the \mzninline{next} array to be different or equal 0. The model
has one unsafe array lookup \mzninline{pos[next[i]]}. We compare using full
reification (\textsf{full}) and half reification (\textsf{half}) to model this
problem. Note that if we extend the $pos$ array to have domain $0..n$ then the
model becomes safe, by replacing its definition with the following two lines
\begin{mzn}
array[0..n] of var 0..n: pos; % posn on node i in path, 0=notin
@ -2397,18 +2408,18 @@ safe, by replacing its definition with the following two lines
We also compare against this model (\textsf{extended}).
We use graphs with both positive and negative weights for the tests. The search
strategy fixes the $next$ variables in order of their maximum value. First we
note that \textsf{extended} is slightly better than \textsf{full} because of the
simpler translation, while \textsf{half} is substantially better than
\textsf{extended} since most of the half reified \texttt{element} constraints
become redundant. Learning increases the advantage because the half reified
formulation focusses on propagation which leads to failure which creates more
reusable nogoods.
strategy fixes the \mzninline{next} variables in order of their maximum value.
First we note that \textsf{extended} is slightly better than \textsf{full}
because of the simpler translation, while \textsf{half} is substantially better
than \textsf{extended} since most of the half reified \texttt{element}
constraints become redundant. Learning increases the advantage because the half
reified formulation focuses on propagation which leads to failure which creates
more reusable nogoods.
\begin{table} [tb] \begin{center}
\caption{\label{tab:pctsp} Prize collecting paths:
Average time (in seconds) and number of solved
instances with a 300s timeout for various number of nodes.}
\caption{\label{tab:hr-pctsp} Prize collecting paths: Average time (in seconds)
and number of solved instances with a 300s timeout for various number of
nodes.}
\begin{tabular}{|l||rr|rr|rr||rr|rr|rr|}
\hline
& \multicolumn{6}{|c||}{FD} & \multicolumn{6}{|c|}{FD + Explanations} \\
@ -2457,11 +2468,11 @@ Nodes &
In the final experiment we compare resource constrained project scheduling
problems (RCPSP) where the \texttt{cumulative} constraint is defined by the task
decomposition as in \cref{ex:cumul} above, using both full reification and
decomposition as in \cref{ex:hr-cumul} above, using both full reification and
half-reification. We use standard benchmark examples from PSPlib
\autocite{kolisch-1997-psplib}. \Cref{tab:rcpsp} compares RCPSP instances using
\textsf{full} reification and \textsf{half} reification. We compare using J30
instances (\textsf{J30} ) and instances due to Baptiste and Le Pape
\autocite{kolisch-1997-psplib}. \Cref{tab:hr-rcpsp} compares RCPSP instances
using \textsf{full} reification and \textsf{half} reification. We compare using
J30 instances (\textsf{J30} ) and instances due to Baptiste and Le Pape
(\textsf{BL}). Each line in the table shows the average run time and number of
solved instances. The search strategy tries to schedule the task with the
earliest possible start time. We find a small and uniform speedup for
@ -2469,7 +2480,7 @@ earliest possible start time. We find a small and uniform speedup for
learning, again because learning is not confused by propagations that do not
lead to failure.
\begin{table} [tb] \caption{\label{tab:rcpsp} RCPSP:
\begin{table} [tb] \caption{\label{tab:hr-rcpsp} RCPSP:
Average time (in seconds) and number of solved instances with a 300s timeout.}
\begin{center}
\begin{tabular}{|l||rr|rr||rr|rr|}
@ -2494,15 +2505,13 @@ To verify the effectiveness of the half reification implementation within the
\minizinc{} translator (rev. \jip{add revision}) against the equivalent version
for which we have implemented half reification. We use the model instances used
by the \minizinc{} challenges
\autocite{stuckey-2010-challenge,stuckey-2014-challenge} from 2012 until 2017.
Note that the instances that the instances without any reifications have been
excluded from the experiment as they would not experience any change. The
\autocite{stuckey-2010-challenge,stuckey-2014-challenge} from 2019 and 2020. The
performance of the generated \flatzinc{} models will be tested using Gecode,
flattened with its own library, and Gurobi and CBC, flattened with the linear
library.
\begin{table} [tb]
\caption{\label{tab:flat_results} Flattening results: Average changes in the
\caption{\label{tab:hr-flat-results} Flattening results: Average changes in the
generated the \flatzinc{} models}
\begin{center}
\begin{tabular}{|l|r||r|r|r||r|}
@ -2517,9 +2526,9 @@ library.
\end{tabular}
\end{center}
\end{table}
\jip{Better headers for \cref{tab:flat_results} and update with current results}
\jip{Better headers for \cref{tab:hr-flat-results} and update with current results}
\Cref{tab:flat_results} shows the average change on the number of full
\Cref{tab:hr-flat-results} shows the average change on the number of full
reification, constraints, and variables between the \flatzinc\ models generated
by the different versions of the translator using the different libraries. We
find that the number full reifications is reduced by a significant amount.
@ -2535,8 +2544,7 @@ of memory running the Ubuntu 16.04.3 LTS operating system.
\jip{Scatter plot of the runtimes + discussion!}
%=============================================================================%
\section{Related Work and Conclusion}
\label{conclusion}
\section{Related Work and Conclusion}\label{sec:hr-conclusion}
%=============================================================================%
Half reification on purely Boolean constraints is well understood, this is the
@ -2546,33 +2554,28 @@ clausal representation of the circuit (see \eg\
total) and the calculation of polarity for Booleans terms inside
\texttt{bool2int} do not arise in pure Boolean constraints.
Half reified constraints have been used in constraint modeling but are typically
not visible as primitive constraints to users, or produced through flattening.
Indexicals \autocite{van-hentenryck-1992-indexicals} can be used to implement
reified constraints by specifying how to propagate a constraint
$c$, %($prop(c)$),
propagate its negation,
%$prop(\neg c)$,
check disentailment, % $check(c)$,
and check entailment, %$check(\neg c)$,
and this is implemented in SICstus Prolog \autocite{carlsson-1997-sicstus}. A
half reified propagator simply omits entailment and propagating the negation.
% $prop(\neg c)$ and $check(\neg c)$. Half reification was briefly discussed in
% our earlier work \autocite{cp2009d}, in terms of its ability to reduce
% propagation.
Half reified constraints have been used in constraint modelling but are
typically not visible as primitive constraints to users, or produced through
flattening. Indexicals \autocite{van-hentenryck-1992-indexicals} can be used to
implement reified constraints by specifying how to propagate a constraint,
propagate its negation, check disentailment, and check entailment, and this is
implemented in SICstus Prolog \autocite{carlsson-1997-sicstus}. A half reified
propagator simply omits entailment and propagating the negation.
Half reified constraints appear in some constraint systems, for example SCIP
\autocite{gamrath-2020-scip} supports half-reified real linear constraints of
the form $b \half \sum_i a_i x_i \leq a_0$ exactly because the negation of the
linear constraint $\sum_i a_i x_i > a_0$ is not representable in an LP solver so
full reification is not possible.
the form \mzninline{b -> sum(i in N)(a[i] * x[i]) <= C} exactly because the
negation of the linear constraint \mzninline{sum(i in N)(a[i] * x[i]) > C} is
not representable in an LP solver so full reification is not possible.
While flattening is the standard approach to handle complex formula involving
constraints, there are a number of other approaches which propagate more
strongly. Schulte proposes a generic implementation of $b \full c$ propagating
(the flattened form of) $c$ in a separate constraint space which does not affect
the original variables \autocite*{schulte-2000-deep}; entailment and
disentailment of $c$ fix the $b$ variable appropriately, although when $b$ is
made $\false$ the implementation does not propagate $\neg c$. This can also be
strongly. Schulte proposes a generic implementation of \mzninline{b <-> c}
propagating (the flattened form of) \mzninline{c} in a separate constraint space
which does not affect the original variables \autocite*{schulte-2000-deep};
entailment and disentailment of \mzninline{c} fix the \mzninline{b} variable
appropriately, although when \mzninline{b} is made \mzninline{false} the
implementation does not propagate \mzninline{not c}. This can also be
implemented using propagator groups \autocite{lagerkvist-2009-groups}. Brand and
Yap define an approach to propagating complex constraint formulae called
controlled propagation which ensures that propagators that cannot affect the