Work on the reification chapter

- Comments by Peter
- Additional reference
- Listings
This commit is contained in:
Jip J. Dekker 2021-07-20 18:40:23 +10:00
parent 4ff0e5c7b5
commit c4e8296e74
No known key found for this signature in database
GPG Key ID: 517DF4A00618C9C3
7 changed files with 94 additions and 74 deletions

View File

@ -706,6 +706,18 @@
year = {2020}, year = {2020},
} }
@inbook{lodi-2013-variability,
author = {Andrea Lodi and Andrea Tramontani},
title = {Performance Variability in Mixed-Integer Programming},
booktitle = {Theory Driven by Influential Applications},
chapter = {Chapter 1},
year = 2013,
pages = {1-12},
doi = {10.1287/educ.2013.0112},
URL = {https://pubsonline.informs.org/doi/abs/10.1287/educ.2013.0112},
eprint = {https://pubsonline.informs.org/doi/pdf/10.1287/educ.2013.0112},
}
@article{lougee-heimer-2003-coin, @article{lougee-heimer-2003-coin,
author = {Robin Lougee{-}Heimer}, author = {Robin Lougee{-}Heimer},
title = {The Common Optimization INterface for Operations Research: Promoting title = {The Common Optimization INterface for Operations Research: Promoting

View File

@ -0,0 +1,4 @@
predicate all_different(array[int] of var int: x) =
forall(i,j in index_set(x) where i < j)(
x[i] != x[j]
);

View File

@ -0,0 +1,5 @@
predicate impli(
var bool: x ::promise_ctx_antitone,
var bool: y ::promise_ctx_monotone
) =
not x \/ y;

View File

@ -0,0 +1,15 @@
predicate _int_lin_le_imp(
array[int] of int: c,
array[int] of var int: x,
int: d,
var bool: b
) =
if is_fixed(b) then
if fix(b) then
int_lin_le(c, x, d)
else
true
endif
else
int_lin_le_imp(c, x, d, b)
endif;

View File

@ -155,7 +155,7 @@ The structure of a \minizinc{} model can be described directly in terms of a \cm
\item and the \gls{objective} is included as a solving goal. \item and the \gls{objective} is included as a solving goal.
\end{itemize} \end{itemize}
More complex models also include definitions for custom types, user defined functions, and a custom output format. 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. These items are not constrained to occur in any particular order.
We briefly discuss the most important model items. We briefly discuss the most important model items.
Note that these items already refer to \minizinc{} expressions, which will be discussed in \cref{subsec:back-mzn-expr}. Note that these items already refer to \minizinc{} expressions, which will be discussed in \cref{subsec:back-mzn-expr}.
@ -180,7 +180,7 @@ The type of a \variable{} is preceded by the \mzninline{var} keyword.
The type of a \parameter{} can similarly be marked by the \mzninline{par} keyword, but this is not required. The type of a \parameter{} can similarly be marked by the \mzninline{par} keyword, but this is not required.
These types are used both as normal \parameters{} and as \variables{}. These types are used both as normal \parameters{} and as \variables{}.
To better structure models, \minizinc{} allows collections of these types to be contained in \glspl{array}. To better structure models, \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. 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{}. For example, the following declaration declares an array going from 5 to 10 of new Boolean \variables{}.
\begin{mzn} \begin{mzn}

View File

@ -30,7 +30,7 @@ As an alternative, the authors introduce \gls{half-reif}.
\begin{enumerate} \begin{enumerate}
\item \Gls{rewriting} using \glspl{half-reif} naturally produces \glspl{rel-sem}. \item \Gls{rewriting} using \glspl{half-reif} naturally produces \glspl{rel-sem}.
\item \Glspl{propagator} for a \glspl{half-reif} can often be constructed by merely altering a \gls{propagator} implementation for its regular \constraint{}. \item \Glspl{propagator} for a \glspl{half-reif} can usually be constructed by merely altering a \gls{propagator} implementation for its regular \constraint{}.
\item \Gls{half-reif} does not often interact with its \gls{cvar}, limiting the amount of triggered \glspl{propagator} that are known to be unable to prune any \domains{}. \item \Gls{half-reif} does not often interact with its \gls{cvar}, limiting the amount of triggered \glspl{propagator} that are known to be unable to prune any \domains{}.
\end{enumerate} \end{enumerate}
@ -76,7 +76,7 @@ Now, we will categorise the latter into:
\end{description} \end{description}
As previously explained, \gls{half-reif} can be used for expressions in \posc{} context. As previously explained, \gls{half-reif} can be used for expressions in \posc{} context.
Although expressions in a \negc{} context cannot be directly \gls{half-reified}, the negation of a expression in a \negc{} context can be \gls{half-reified}. Although expressions in a \negc{} context cannot be directly \gls{half-reified}, the negation of an expression in a \negc{} context can be \gls{half-reified}.
\begin{example} \begin{example}
Consider, for example, the following \constraint{}. Consider, for example, the following \constraint{}.
@ -98,7 +98,7 @@ Although expressions in a \negc{} context cannot be directly \gls{half-reified},
\end{example} \end{example}
Expressions in a \mixc{} context are in a position where \gls{half-reif} is impossible. Expressions in a \mixc{} context are in a position where \gls{half-reif} is impossible.
Only full \gls{reif} can be used for expressions in that are in this context. Only full \gls{reif} can be used for expressions that are in this context.
This occurs, for example, when using an exclusive or expression in a \constraint{}. This occurs, for example, when using an exclusive or expression in a \constraint{}.
The value that one side must take directly depends on the value that the other side takes. The value that one side must take directly depends on the value that the other side takes.
Each side can thus be forced to be true or false. Each side can thus be forced to be true or false.
@ -122,7 +122,7 @@ Given \texttt{b}, \(check\), and \(prune\), \cref{alg:half-prop} shows pseudo co
\begin{algorithm} \begin{algorithm}
\KwIn{A function \(check\), that returns false when the \constraint{} \(c\) cannot be satisfied, a function \(prune\), that eliminates values from \variable{} \glspl{domain} that violate\glsadd{violated} the \constraint{} \(c\), and a Boolean control \variable{} \texttt{b}. \KwIn{A function \(check\), that returns false when the \constraint{} \(c\) cannot be satisfied, a function \(prune\), that eliminates values from \variable{} \glspl{domain} that violate\glsadd{violated} the \constraint{} \(c\), and a Boolean \gls{cvar} \texttt{b}.
} }
\KwResult{This pseudo code propagates the \gls{half-reif} of \(c\) (\ie{} \(\texttt{b} \implies\ c\)).} \KwResult{This pseudo code propagates the \gls{half-reif} of \(c\) (\ie{} \(\texttt{b} \implies\ c\)).}
@ -191,7 +191,7 @@ Ultimately, \minizinc{}'s \gls{linearisation} library rewrites most \glspl{reif}
For all these \glspl{reif}, its replacement by a \gls{half-reif} can remove half of the implications required for the \gls{reif}. For all these \glspl{reif}, its replacement by a \gls{half-reif} can remove half of the implications required for the \gls{reif}.
For \gls{sat} solvers, a \gls{decomp} for a \gls{half-reif} can be created from its regular \gls{decomp}. For \gls{sat} solvers, a \gls{decomp} for a \gls{half-reif} can be created from its regular \gls{decomp}.
Any \constraint{} \(c\) will \gls{decomp} into \gls{cnf}. Any \constraint{} \(c\) will \gls{decomp} into \gls{cnf} of the following form.
\[ c = \forall_{i} \exists_{j} lit_{ij} \] \[ c = \forall_{i} \exists_{j} lit_{ij} \]
The \gls{half-reif}, with \gls{cvar} \texttt{b}, could take the following encoding. The \gls{half-reif}, with \gls{cvar} \texttt{b}, could take the following encoding.
\[ \texttt{b} \implies c = \forall_{i} \neg \texttt{b} \lor \exists_{j} lit_{ij} \] \[ \texttt{b} \implies c = \forall_{i} \neg \texttt{b} \lor \exists_{j} lit_{ij} \]
@ -340,17 +340,14 @@ If a function argument is not annotated, then the argument is evaluated in \mixc
\begin{example} \begin{example}
Consider the following user-defined \minizinc{} implementation of a logical implication. Consider the user-defined \minizinc{} implementation of a logical implication in \cref{lst:half-impli}.
\begin{mzn} \begin{listing}
predicate impli( \mznfile{assets/listing/half_impli.mzn}
var bool: x ::promise_ctx_antitone, \caption{\label{lst:half-impli} A user-defined predicate of a logical implication using \glspl{annotation} to define the context usage of its arguments.}
var bool: y ::promise_ctx_monotone \end{listing}
) =
not x \/ y;
\end{mzn}
The annotations placed on the argument of the \mzninline{impli} function will apply the same context transformations as the \mzninline{->} operator shown in \cref{alg:arg-ctx}. The \glspl{annotation} placed on the argument of the \mzninline{impli} function will apply the same context transformations as the \mzninline{->} operator shown in \cref{alg:arg-ctx}.
In term of context analysis, this function now is equivalent to the \minizinc{} operator. In term of context analysis, this function now is equivalent to the \minizinc{} operator.
\end{example} \end{example}
@ -373,7 +370,7 @@ If a function argument is not annotated, then the argument is evaluated in \mixc
\Case{\mzninline{ > }, \mzninline{>= }, \mzninline{- }}{ \Case{\mzninline{ > }, \mzninline{>= }, \mzninline{- }}{
\Return{\tuple{\changepos{}ctx, \changeneg{}ctx}} \Return{\tuple{\changepos{}ctx, \changeneg{}ctx}}
} }
\Case{\mzninline{ <-> }, \mzninline{xor }, \mzninline{* }}{ \Case{\mzninline{ <-> }, \mzninline{= }, \mzninline{xor }, \mzninline{* }}{
\Return{\tuple{\mixc, \mixc}} \Return{\tuple{\mixc, \mixc}}
} }
\Case{\mzninline{ /\ }}{ \Case{\mzninline{ /\ }}{
@ -409,7 +406,7 @@ Otherwise, the \compiler{} follows the following steps to try to introduce the m
Note that this can only occur when a \gls{native} \constraint{} does not define a \gls{reif}. Note that this can only occur when a \gls{native} \constraint{} does not define a \gls{reif}.
\end{enumerate} \end{enumerate}
The (Access) and (ITE) rules show the context inference for \gls{array} access and if-then-else expressions respectively. The (Access) and (ITE) rules show the context inference for \gls{array} access and \gls{conditional} expressions respectively.
Their inner expressions are evaluated in \(\changepos{}ctx\). Their inner expressions are evaluated in \(\changepos{}ctx\).
The inner expressions cannot be simply be evaluated in \(ctx\), because it is not yet certain which expression will be chosen. The inner expressions cannot be simply be evaluated in \(ctx\), because it is not yet certain which expression will be chosen.
This is important for when \(ctx\) is \rootc{}, since we, at compile time, cannot say which expression will hold globally. This is important for when \(ctx\) is \rootc{}, since we, at compile time, cannot say which expression will hold globally.
@ -505,7 +502,7 @@ The (Item0) rule is triggered when there are no more inner items in the let-expr
\label{subsec:half-?root} \label{subsec:half-?root}
In the previous section, we briefly discussed the context transformations for the (Access) and (ITE) rules in \cref{fig:half-analysis-expr}. In the previous section, we briefly discussed the context transformations for the (Access) and (ITE) rules in \cref{fig:half-analysis-expr}.
Different from the rules described, when an \gls{array} access or if-then-else expression is found in \rootc{} context, it often makes sense to evaluate its sub-expression in \rootc{} context as well. Different from the rules described, when an \gls{array} access or \gls{conditional} expression is found in \rootc{} context, it often makes sense to evaluate its sub-expression in \rootc{} context as well.
It is, however, not always safe to do so. It is, however, not always safe to do so.
\begin{example} \begin{example}
@ -514,14 +511,16 @@ It is, however, not always safe to do so.
For example, consider the following \microzinc{} fragment. For example, consider the following \microzinc{} fragment.
\begin{mzn} \begin{mzn}
\constraint{} if b then constraint if b then
F(x, y, z) F(x, y, z)
else else
G(x, y, z) G(x, y, z)
endif; endif;
\end{mzn} \end{mzn}
In this case, since only one side of the if-then-else expression is evaluated, the compiler can output calls to the \rootc{} variant of the functions. Let us assume that \mzninline{b} is a \parameter{}, but that its value is not known during the compilation from \minizinc{} to \microzinc{}.
In this case, we know that only one side of the \gls{conditional} expression will evaluated, and that call will then globally constrain the \cmodel{}.
As such, the \compiler{} can output calls to the \rootc{} variant of the functions.
This will enforce the \constraint{} in the most efficient way. This will enforce the \constraint{} in the most efficient way.
Things, however, change when the situation gets more complex. Things, however, change when the situation gets more complex.
@ -536,16 +535,16 @@ It is, however, not always safe to do so.
} in ret; } in ret;
\end{mzn} \end{mzn}
One side of the if-then-else expression is also used in a disjunction. One side of the \gls{conditional} expression is also used in a disjunction.
If \mzninline{b} evaluates to \mzninline{true}, then \mzninline{p} is evaluated in \rootc{} context, and \mzninline{p} can take the value \mzninline{true} in the disjunction. If \mzninline{b} evaluates to \mzninline{true}, then \mzninline{p} is evaluated in \rootc{} context, and \mzninline{p} can take the value \mzninline{true} in the disjunction.
Otherwise, \mzninline{q} is evaluated in \rootc{} context, and \mzninline{p} in the disjunction must be evaluated in \posc{} context. Otherwise, \mzninline{q} is evaluated in \rootc{} context, and \mzninline{p} in the disjunction must be evaluated in \posc{} context.
It is, therefore, not safe to assume that all sides of the if-then-else expressions are evaluated in \rootc{} context. In this situation, it is not safe for the \compiler{} to output calls for the \rootc{} variants of these calls.
\end{example} \end{example}
Using the \changepos{} transformation for sub-expression contexts is safe, but it places a large burden on the \solver{}. Using the \changepos{} transformation for sub-expression contexts is safe, but it places a large burden on the \solver{}.
The solver performs better when the no \gls{reif} has to be used. The solver performs better when the no \gls{reif} has to be used.
To detect situation where the sub-expression are only used in an \gls{array} access or if-then-else expression we introduce the \mayberootc{} context. To detect situation where the sub-expression are only used in an \gls{array} access or \gls{conditional} expression we introduce the \mayberootc{} context.
This context functions as a ``weak'' \rootc{} context. This context functions as a ``weak'' \rootc{} context.
If it is joined with any other context, then it acts as \posc{}. If it is joined with any other context, then it acts as \posc{}.
The extended join operation is shown in \cref{fig:half-maybe-join}. The extended join operation is shown in \cref{fig:half-maybe-join}.
@ -580,7 +579,7 @@ Otherwise, it will act as a normal \rootc{} context.
\caption{\label{fig:half-analysis-maybe-root} Updated context inference rules for \mayberootc{}.} \caption{\label{fig:half-analysis-maybe-root} Updated context inference rules for \mayberootc{}.}
\end{figure*} \end{figure*}
\Cref{fig:half-analysis-maybe-root} shows the additional inference rules for \gls{array} access and if-then-else expressions. \Cref{fig:half-analysis-maybe-root} shows the additional inference rules for \gls{array} access and \gls{conditional} expressions.
Looking back at \cref{ex:half-maybe-root}, these additional rules and updated join operation will ensure that the first case will correctly use \rootc{} context calls. Looking back at \cref{ex:half-maybe-root}, these additional rules and updated join operation will ensure that the first case will correctly use \rootc{} context calls.
For the second case, however, it detects that \mzninline{p} is used in both \posc{} and \mayberootc{} context. For the second case, however, it detects that \mzninline{p} is used in both \posc{} and \mayberootc{} context.
Therefore, it will output the \posc{} call for the right hand side of \mzninline{p}, even when \mzninline{b} evaluates to \mzninline{true}. Therefore, it will output the \posc{} call for the right hand side of \mzninline{p}, even when \mzninline{b} evaluates to \mzninline{true}.
@ -590,7 +589,7 @@ We will, however, discuss the dynamically adjusting of contexts during \gls{rewr
\section{Rewriting and Half Reification}% \section{Rewriting and Half Reification}%
\label{sec:half-rewriting} \label{sec:half-rewriting}
During the \gls{rewriting} process the contexts assigned to the different expressions can be used directly to determine if and how a expression has to be \gls{reified}. During the \gls{rewriting} process the contexts assigned to the different expressions can be used directly to determine if and how an expression has to be \gls{reified}.
\begin{example} \begin{example}
\label{ex:half-rewriting} \label{ex:half-rewriting}
@ -626,7 +625,7 @@ During the \gls{rewriting} process the contexts assigned to the different expres
constraint bool_clause([b1, b2], []); % b1 \/ b2 constraint bool_clause([b1, b2], []); % b1 \/ b2
constraint bool_clause_imp([b3], [y], b1); % b1 -> (y -> b3) constraint bool_clause_imp([b3], [y], b1); % b1 -> (y -> b3)
constraint f_imp(x, b3); % b3 -> f(x) constraint f_imp(x, b3); % b3 -> f(x)
constraint int_ne_imp(x, 5, b4); % b4 -> x != 5 constraint int_ne_imp(x, 5, b2); % b2 -> x != 5
\end{mzn} \end{mzn}
We are able to replace the two \glspl{reif} and push the negation inwards, transforming the equals \constraint{} into a not equals \constraint{}. We are able to replace the two \glspl{reif} and push the negation inwards, transforming the equals \constraint{} into a not equals \constraint{}.
@ -634,9 +633,9 @@ During the \gls{rewriting} process the contexts assigned to the different expres
If the Boolean \mzninline{y} was not further \constraint{} in the problem, then we could further reduce it to the following \constraints{}. If the Boolean \mzninline{y} was not further \constraint{} in the problem, then we could further reduce it to the following \constraints{}.
\begin{mzn} \begin{mzn}
constraint bool_clause([y, b2], []); % y \/ b2 constraint bool_clause([y, b2], []); % y \/ b2
constraint f_imp(x, y); % b3 -> f(x) constraint f_imp(x, y); % y -> f(x)
constraint int_ne_imp(x, 5, b4); % b4 -> x != 5 constraint int_ne_imp(x, 5, b2); % b2 -> x != 5
\end{mzn} \end{mzn}
\end{example} \end{example}
@ -650,7 +649,7 @@ It can be used to eliminate these \glspl{implication-chain}.
The \gls{rewriting} with \gls{half-reif} also interacts with some of the optimisation methods used during the \gls{rewriting} process. The \gls{rewriting} with \gls{half-reif} also interacts with some of the optimisation methods used during the \gls{rewriting} process.
Most importantly, \gls{half-reif} has to be considered when using \gls{cse} and \gls{propagation} can change the context of expression. Most importantly, \gls{half-reif} has to be considered when using \gls{cse} and \gls{propagation} can change the context of expression.
In \cref{subsec:half-cse} we will discuss how \gls{cse} can be adjusted to handle \gls{half-reif}. In \cref{subsec:half-cse} we will discuss how \gls{cse} can be adjusted to handle \gls{half-reif}.
Finally, in \cref{subsec:half-dyn-context} we will discuss how the context in which a expression is executed can be adjusted during the \gls{rewriting} process. Finally, in \cref{subsec:half-dyn-context} we will discuss how the context in which an expression is executed can be adjusted during the \gls{rewriting} process.
\subsection{Chain compression}% \subsection{Chain compression}%
\label{subsec:half-compress} \label{subsec:half-compress}
@ -660,13 +659,13 @@ In this case the conjunction can be replaced by \mzninline{b1 -> c} and \texttt{
The case shown in the example can be generalised to The case shown in the example can be generalised to
\begin{mzn} \begin{mzn}
b1 -> b2 /\ forall(i in N)(b2 -> c[i]) constraint b1 -> b2 /\ forall(i in N)(b2 -> c[i])
\end{mzn} \end{mzn}
\noindent{}which, if \texttt{b2} has no other usage in the instance, can be resolved to \noindent{}which, if \texttt{b2} has no other usage in the instance, can be resolved to
\begin{mzn} \begin{mzn}
forall(i in N)(b1 -> c[i]) constraint forall(i in N)(b1 -> c[i])
\end{mzn} \end{mzn}
\noindent{}after which \texttt{b2} can be removed from the model. \noindent{}after which \texttt{b2} can be removed from the model.
@ -708,21 +707,21 @@ The algorithm can be further improved by considering implied conjunctions.
These can be split up into multiple implications. These can be split up into multiple implications.
\begin{mzn} \begin{mzn}
b -> forall(x in N)(x) constraint b -> forall(x in N)(x)
\end{mzn} \end{mzn}
The expression above is logically equivalent to the following expression. The expression above is logically equivalent to the following expression.
\begin{mzn} \begin{mzn}
forall(x in N)(b -> x) constraint forall(x in N)(b -> x)
\end{mzn} \end{mzn}
Adopting this transformation both simplifies a complicated \constraint{} and possibly allows for the further compression of \glspl{implication-chain}. Adopting this transformation both simplifies a complicated \constraint{} and possibly allows for the further compression of \glspl{implication-chain}.
It should however be noted that although this transformation can increase the number of \constraints{} in the \gls{slv-mod}, it generally increases the \gls{propagation} efficiency. It should however be noted that although this transformation can increase the number of \constraints{} in the \gls{slv-mod}, it generally increases the \gls{propagation} efficiency.
To adjust the algorithm to simplify implied conjunctions more introspection from the \minizinc{} \compiler{} is required. 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 \variable{} is (only) a \gls{cvar} of a reified conjunction and what the elements of that conjunction are. The \compiler{} must be able to tell if a \variable{} is (only) a \gls{cvar} of a reified conjunction and what the elements of that conjunction are.
In the case where a \variable{} has one incoming edge, but it is marked as used in other \constraint{}, we can now check if it is only a \gls{cvar} for a \gls{reified} conjunction and perform the transformation in this case. In the case where a \variable{} has one incoming edge, but it is marked as used in another \constraint{}, we can now check if it is only a \gls{cvar} for a \gls{reified} conjunction and perform the transformation in this case.
\subsection{Common Sub-expression Elimination}% \subsection{Common Sub-expression Elimination}%
\label{subsec:half-cse} \label{subsec:half-cse}
@ -759,7 +758,7 @@ All usages of the previously recorded result is replaced by the new result.
The dependency tracking through the use of \constraints{} attached to \variables{} ensures no defining \constraints are left in the model. The dependency tracking through the use of \constraints{} attached to \variables{} ensures no defining \constraints are left in the model.
This ensures that all \variables{} and \constraints{} created the earlier version are correctly removed. This ensures that all \variables{} and \constraints{} created the earlier version are correctly removed.
Because the expression itself is changed when a negation is moved inwards, it may not always be clear when the same expression is used in both \posc{} and negc{} context. Because the expression itself is changed when a negation is moved inwards, it may not always be clear when the same expression is used in both \posc{} and \negc{} context.
This problem is solved by introducing a canonical form for expressions where negations can be pushed inwards. This problem is solved by introducing a canonical form for expressions where negations can be pushed inwards.
In this form the result of \gls{rewriting} an expression and its negation are collected in the same place within the \gls{cse} table. In this form the result of \gls{rewriting} an expression and its negation are collected in the same place within the \gls{cse} table.
If it is found that for an expression that is about to be \gls{half-reified} there already exists an \gls{half-reif} for its negation, then we instead evaluate the expression in \mixc{} context. If it is found that for an expression that is about to be \gls{half-reified} there already exists an \gls{half-reif} for its negation, then we instead evaluate the expression in \mixc{} context.
@ -771,7 +770,7 @@ The expression is \gls{reified} and replaces the existing \gls{half-reified} exp
When its expression is negated, pushing the negation inwards will result in a \mzninline{!=} operator, a \mzninline{int_ne} call. When its expression is negated, pushing the negation inwards will result in a \mzninline{!=} operator, a \mzninline{int_ne} call.
The opposite happens when a negation is pushed inwards for an expression using \mzninline{!=} operator. The opposite happens when a negation is pushed inwards for an expression using \mzninline{!=} operator.
So to ensure that a \negc{} occurrence of \mzninline{int_eq} and a \posc{} occurrence of \mzninline{int_ne} use the same \gls{cvar} they are both mapped to \mzninline{int_eq} in the \gls{cse} table. So to ensure that a \negc{} occurrence of \mzninline{int_eq} and a \posc{} occurrence of \mzninline{int_ne} use the same \gls{cvar} they are both mapped to \mzninline{int_eq} in the \gls{cse} table.
The mapping ensures that the context is correctly transformed when accessing the \gls{cse} table for a \mzninline{int_ne} call. The mapping ensures that the context is correctly transformed when accessing the \gls{cse} table for an \mzninline{int_ne} call.
\end{example} \end{example}
@ -799,7 +798,7 @@ The same situation can be caused by \gls{propagation}.
constraint b -> (2*x = y); constraint b -> (2*x = y);
\end{mzn} \end{mzn}
Since the \domain{} of \mzninline{x} is strictly smaller than the \domain{} of \mzninline{y}, \gls{propagation} of \mzninline{b} will set it to the value \mzninline{true}. Since the values in the \domain{} of \mzninline{x} are strictly smaller than the values in the \domain{} of \mzninline{y}, \gls{propagation} of \mzninline{b} will set it to the value \mzninline{true}.
This however means that the \constraint{} is equivalent to the following \constraint{}. This however means that the \constraint{} is equivalent to the following \constraint{}.
\begin{mzn} \begin{mzn}
@ -811,14 +810,14 @@ The same situation can be caused by \gls{propagation}.
\end{example} \end{example}
The situation shown in the example is the most common change of context. The situation shown in the example is the most common change of context.
If the control \variable{} of a \gls{reif} is fixed, the context often changes to either \rootc{} or a negated \rootc{} context. If the \gls{cvar} of a \gls{reif} is fixed, the context often changes to either \rootc{} or a negated \rootc{} context.
If, on the other hand, the \gls{cvar} of a \gls{half-reif} is fixed, then either the context becomes \rootc{} or the \constraint{} already holds. If, on the other hand, the \gls{cvar} of a \gls{half-reif} is fixed, then either the context becomes \rootc{} or the \constraint{} already holds.
Since direct \constraints{} are strongly preferred over any form of \gls{reif}, it is important to dynamically pick the correct form during the \gls{rewriting} process. Since direct \constraints{} are strongly preferred over any form of \gls{reif}, it is important to dynamically pick the correct form during the \gls{rewriting} process.
This problem can be solved by the \compiler{}. This problem can be solved by the \compiler{}.
For each \gls{reif} and \gls{half-reif} the \compiler{} introduces another layer of \gls{decomp}. For each \gls{reif} and \gls{half-reif} the \compiler{} introduces another layer of \gls{decomp}.
In this layer, it checks its \gls{cvar}. In this layer, it checks its \gls{cvar}.
If the control \variable{} is already fixed, then it rewrites itself into its form in another context. If the \gls{cvar} is already fixed, then it rewrites itself into its form in another context.
Otherwise, it behaves as it would have done normally. Otherwise, it behaves as it would have done normally.
The \gls{cvar} is thus used to communicate the change in context. The \gls{cvar} is thus used to communicate the change in context.
@ -826,27 +825,14 @@ The \gls{cvar} is thus used to communicate the change in context.
Let's assume the \compiler{} finds a call to \mzninline{int_lin_le} in \posc{} context. Let's assume the \compiler{} finds a call to \mzninline{int_lin_le} in \posc{} context.
Instead of outputting the call to \mzninline{int_lin_le_imp} directly, it will instead output a call to \mzninline{_int_lin_le_imp}. Instead of outputting the call to \mzninline{int_lin_le_imp} directly, it will instead output a call to \mzninline{_int_lin_le_imp}.
This predicate is then generated as follows: This predicate is then generated as shown in \cref{lst:half-check-reif}.
This new predicate (calls) can then be rewritten normally.
\begin{mzn} \begin{listing}
predicate _int_lin_le_imp( \mznfile{assets/listing/half_reif_check.mzn}
array[int] of int: c, \caption{\label{lst:half-check-reif}A generated predicate for \mzninline{int_lin_le_imp} that checks its \gls{cvar} to ensure a \gls{half-reif} is still required.}
array[int] of var int: x, \end{listing}
int: d,
var bool: b
) =
if is_fixed(b) then
if fix(b) then
int_lin_le(c, x, d)
else
true
endif
else
int_lin_le_reif(c, x, d, b)
endif;
\end{mzn}
This new predicate can then be compiled using the normal methods.
\end{example} \end{example}
\section{Experiments} \section{Experiments}
@ -889,18 +875,16 @@ Therefore, the transformation described in \cref{sec:half-propagation} can be di
Since \gls{chuffed} is a \gls{lcg} \solver{}, the explanations created by the \gls{propagator} have to be adjusted as well. Since \gls{chuffed} is a \gls{lcg} \solver{}, the explanations created by the \gls{propagator} have to be adjusted as well.
These adjustments happen in a similar fashion to the adjustments of the general algorithm: explanations used for the violation of the \constraint{} can now be used to set the \gls{cvar} to \mzninline{false} and the explanations given to prune a variable are appended by requirement that the \gls{cvar} is \mzninline{true}. These adjustments happen in a similar fashion to the adjustments of the general algorithm: explanations used for the violation of the \constraint{} can now be used to set the \gls{cvar} to \mzninline{false} and the explanations given to prune a variable are appended by requirement that the \gls{cvar} is \mzninline{true}.
In our second configuration the \mzninline{all_different} \constraint{} is enforced using the following \gls{decomp}. In our second configuration the \mzninline{all_different} \constraint{} is enforced using the \gls{decomp} shown in \cref{lst:half-alldiff}.
\begin{mzn}
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}
The \mzninline{!=} \constraints{} produced by this redefinition are \gls{reified}. The \mzninline{!=} \constraints{} produced by this redefinition are \gls{reified}.
Their conjunction, then represent the \gls{reif} of the \mzninline{all_different} \constraint{}. Their conjunction, then represent the \gls{reif} of the \mzninline{all_different} \constraint{}.
\begin{listing}
\mznfile{assets/listing/half_alldiff.mzn}
\caption{\label{lst:half-alldiff}The standard \gls{decomp} for \mzninline{all_different} in the \minizinc{} standard library.}
\end{listing}
\begin{table} \begin{table}
\begin{center} \begin{center}
\input{assets/table/half_qcp} \input{assets/table/half_qcp}
@ -1039,7 +1023,7 @@ In general, we would expect the reduction of \constraints{} in a \gls{mip} insta
However, we can imagine that the removed \constraints{} in some cases help the \gls{mip} solver. However, we can imagine that the removed \constraints{} in some cases help the \gls{mip} solver.
An important technique used by \gls{mip} solvers is to detect certain pattern, such as cliques, during the pre-processing of the \gls{mip} instance. An important technique used by \gls{mip} solvers is to detect certain pattern, such as cliques, during the pre-processing of the \gls{mip} instance.
Some patterns can only be detected when using a full \gls{reif}. Some patterns can only be detected when using a full \gls{reif}.
Furthermore, the performance of \gls{mip} solvers is often dependent on the order and form in which the \constraints{} are given \autocite{fischetti-2014-erratiscism}. Furthermore, the performance of \gls{mip} solvers is often dependent on the order and form in which the \constraints{} are given \autocite{lodi-2013-variability,fischetti-2014-erratiscism}.
With the usage of the \gls{aggregation} and \gls{del-rew}, these can be exceedingly different when using \gls{half-reif}. With the usage of the \gls{aggregation} and \gls{del-rew}, these can be exceedingly different when using \gls{half-reif}.
The solving statistics for \gls{openwbo} might be most positive. The solving statistics for \gls{openwbo} might be most positive.

View File

@ -1,14 +1,14 @@
\noindent{}Whether a \constraint{} has to be \gls{reified} is a crucial decision made during \gls{rewriting}. \noindent{}Whether a \constraint{} has to be \gls{reified} is a crucial decision made during \gls{rewriting}.
\minizinc{} allows us to write complex \constraints{} that in the process of \gls{rewriting} result in multiple \constraints{} that reason about each other. \minizinc{} allows us to write complex \constraints{} that in the process of \gls{rewriting} result in multiple \constraints{} that reason about each other.
However, determining whether a \constraint{} holds, \(\texttt{b} \leftrightarrow{} c\), requires significantly more effort from the \solver{} than merely enforcing the \constraint{} \(c\). However, determining whether a \constraint{} holds, \(b \leftrightarrow{} c\), requires significantly more effort from the \solver{} than merely enforcing the \constraint{} \(c\).
A \gls{reified} \constraint{} results in a larger \gls{decomp} or a more complex \gls{native} \constraint{}. A \gls{reified} \constraint{} results in a larger \gls{decomp} or a more complex \gls{native} \constraint{}.
It it thus important that \gls{rewriting} avoids the use of \gls{reif}, whenever it is not required. It it thus important that \gls{rewriting} avoids the use of \gls{reif}, whenever it is not required.
In this chapter we present an extended \gls{reif} analysis to help minimise the required \solver{} effort. In this chapter we present an extended \gls{reif} analysis to help minimise the required \solver{} effort.
Not only does it consider if a \constraint{} must be \gls{reified} or not, but it also considers whether it could instead be \gls{half-reified}. Not only does it consider if a \constraint{} must be \gls{reified} or not, but it also considers whether it could instead be \gls{half-reified}.
The notion of \gls{half-reif} \(\texttt{b} \rightarrow{} c\) for a \constraint{} \(c\) was introduced by \textcite{feydy-2011-half-reif}. The notion of \gls{half-reif} \(b \rightarrow{} c\) for a \constraint{} \(c\) was introduced by \textcite{feydy-2011-half-reif}.
It is shown \gls{half-reif} can relief some of the problems and expenses of the use of \gls{reif}. It is shown \gls{half-reif} can relieve some of the problems and expenses of the use of \gls{reif}.
It is not always possible, however, to use a \gls{half-reif} instead of a \gls{reif}. It is not always possible, however, to use a \gls{half-reif} instead of a \gls{reif}.
The authors identify the conditions required for its usage and provide an algorithm that rewrites a subset of the \minizinc{} language using the technique. The authors identify the conditions required for its usage and provide an algorithm that rewrites a subset of the \minizinc{} language using the technique.
Crucially, because of the omission of \gls{let} and multiple occurrences of identifiers, this algorithm does not directly generalise to the full \minizinc{} language. Crucially, because of the omission of \gls{let} and multiple occurrences of identifiers, this algorithm does not directly generalise to the full \minizinc{} language.