Work on half-reif experiments

This commit is contained in:
Jip J. Dekker 2021-06-10 12:12:28 +10:00
parent 6fd413b2ed
commit cfd29fc909
No known key found for this signature in database
GPG Key ID: 517DF4A00618C9C3
5 changed files with 217 additions and 36 deletions

View File

@ -300,6 +300,12 @@
description={},
}
\newglossaryentry{qcp-max}{
name={QCP-max},
description={},
}
\newglossaryentry{reification}{
name={reification},
description={},

View File

@ -2,6 +2,8 @@
\chapter{Background}\label{ch:background}
%************************************************
\jip{TODO:\ Mention something about LCG.}
\noindent{}A goal shared between all programming languages is to provide a certain level
of abstraction: an assembly language allows you to abstract from the binary
instructions and memory positions; Low-level imperial languages, like FORTRAN,

View File

@ -1023,38 +1023,21 @@ of model output expressions and annotations. We will release our implementation
under an open-source license and can make it available to the reviewers upon
request.
The implementation is not optimised for performance yet, but was created as a
faithful implementation of the developed concepts, in order to evaluate their
suitability and provide a solid baseline for future improvements. In the
following we present experimental results on basic flattening performance as
well as incremental flattening and solving that demonstrate the efficiency
gains that are possible thanks to the new architecture.
The implementation is not optimised for performance yet, but was created as a faithful implementation of the developed concepts, in order to evaluate their suitability and provide a solid baseline for future improvements.
In the following we present experimental results on basic flattening performance as well as incremental flattening and solving that demonstrate the efficiency gains that are possible thanks to the new architecture.
We selected 20 models from the annual \minizinc\ challenge and compiled 5
instances of each model to \flatzinc{}, using the current \minizinc\ release
version 2.4.3 and the new prototype system. In both cases we use the standard
\minizinc\ library of global constraints (\ie\ we decompose those constraints
rather than using solver built-ins, in order to stress-test the flattening). We
measured pure flattening time, \ie\ without time required to parse and type check
in version 2.4.3, and without time required for compilation to \microzinc\ in
the new system (compilation is usually very fast). Times are averages of 10
runs.\footnote{All models obtained from
\url{https://github.com/minizinc/minizinc-benchmarks}:
\texttt{\justify{}accap, amaze, city-position, community-detection,
depot-placement, freepizza, groupsplitter, kidney-exchange, median-string,
multi-knapsack, nonogram, nside, problem, rcpsp-wet, road-cons, roster,
stack-cuttingstock, steelmillslab, train, triangular, zephyrus}.}
We selected 20 models from the annual \minizinc\ challenge and compiled 5 instances of each model to \flatzinc{}, using the current \minizinc\ release version 2.5.5 and the new prototype system.
In both cases we use the standard \minizinc\ library of global constraints (\ie\ we decompose those constraints rather than using solver built-ins, in order to stress-test the flattening).
We measured pure flattening time, \ie\ without time required to parse and type check in version 2.5.5, and without time required for compilation to \microzinc\ in the new system (compilation is usually very fast).
Times are averages of 10 runs.\footnote{All models obtained from \url{https://github.com/minizinc/minizinc-benchmarks}: \texttt{\justify{}accap, amaze, city-position, community-detection, depot-placement, freepizza, groupsplitter, kidney-exchange, median-string, multi-knapsack, nonogram, nside, problem, rcpsp-wet, road-cons, roster, stack-cuttingstock, steelmillslab, train, triangular, zephyrus}.}
\Cref{sfig:4-compareruntime} compares the flattening time for each of the 100
instances. Points below the line indicate that the new system is faster. On
average, the new system achieves a speed-up of \(2.3\), with very few instances
not achieving any speedup. In terms of memory performance
(\cref{sfig:4-comparemem}), version 2.4.3 can sometimes still outperform the new
prototype. We have identified that the main memory bottlenecks are our currently
unoptimised implementations of \gls{cse} lookup tables and argument vectors.
\Cref{sfig:4-compareruntime} compares the flattening time for each of the 100 instances.
Points below the line indicate that the new system is faster.
On average, the new system achieves a speed-up of \(2.3\), with very few instances not achieving any speedup.
In terms of memory performance (\cref{sfig:4-comparemem}), version 2.4.3 can sometimes still outperform the new prototype.
We have identified that the main memory bottlenecks are our currently unoptimised implementations of \gls{cse} lookup tables and argument vectors.
These are very encouraging results, given that we are comparing a largely
unoptimised prototype to a mature piece of software.
These are very encouraging results, given that we are comparing a largely unoptimised prototype to a mature piece of software.
\begin{figure}[t]
\centering

View File

@ -222,6 +222,9 @@ This adds a new binary clause for every literal in the original \gls{cnf}.
This overhead can be avoided when using \gls{half-reif}.
\jip{TODO: it would be great to conclude here that all these clauses are unnecessary in a positive context, since \(b\) would never be set to false.}
According to the principles above, decomposition libraries for the full \minizinc{} language have been implemented for \gls{mip} and \gls{sat} solvers.
In \cref{sec:half-experiments} we asses the effects when flattening with \gls{half-reif}.
\section{Context Analysis}%
\label{sec:half-context}
@ -791,5 +794,129 @@ In the case where a variable has one incoming edge, but it is marked as used in
\section{Experiments}
\label{sec:half-experiments}
We now present experimental evaluation of the presented \gls{half-reif} techniques.
First, to show the benefit of implementing propagators for half-reified constraint, we compare their performance against their decompositions.
To do this, we recreate two experiments presented by Feydy et al.\ in the original \gls{half-reif} paper in a modern \gls{cp} solver, \gls{chuffed}.
In the experiment, we use propagators implemented according to the principles described in this paper.
No new algorithm has been devised to perform the propagation.
The propagator of the original constraint is merely adjusted to influence and watch a control \variable{}.
Additionally, we assess the effects of automatically detecting and introducing \glspl{half-reif} during the flattening process. We flatten and solve
A description of the used computational environment, \minizinc{} instances, and versioned software has been included in \cref{ch:benchmarks}.
\subsection{Propagators}
\label{sec:half-exp-prop}
Our first experiment considers the \gls{qcp-max} quasi-group completion problem.
In this problem, we need to decide the value of an \((n \times n)\) matrix of integer \variables, with domains \mzninline{1..n}.
The aim of the problem is to create as many rows and columns where all \variables{} take a unique value.
In each instance certain values have already been fixed.
It is, thus, not always possible for all rows and columns to contain only distinct values.
In \minizinc{} counting the number of rows/columns with all different values can be accomplished by reifying the \mzninline{all_different} constraint.
Since the goal of the problem is to maximise the number of \mzninline{all_different} \constraints{} that hold, these constraints are never forced to be \mzninline{false}.
This means these constraints in a \posc{} context and can be half-reified.
\Cref{tab:half-qcp} shows the comparison of two solving configurations in \gls{chuffed} for the \gls{qcp-max} problem.
The results are grouped based on their size of the instance.
For each group we show the number of instances solved by the configuration and the average time used for this process.
The first configuration uses a newly created propagator for half-reified \mzninline{all_different} \constraints{}.
This propagator is an adjusted version from the existing bounds consistent \mzninline{all_different} propagator in chuffed.
The implementation of the propagator was already split into parts that \emph{check} the violation of the constraint and parts that \emph{prune} the \glspl{domain} of \variables{}.
Therefore, the transformation described in \cref{sec:half-propagation} can be directly applied.
Since \gls{chuffed} is a \gls{lcg} \solver{}, the explanations created by the 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 control variable to \mzninline{false} and the explanations given to prune a variable are appended by requirement that the control variable is \mzninline{true}.
The second configuration uses the following decomposition for the \mzninline{all_different} constraint.
\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 reified.
Their conjunction, then represent the reification of the \mzninline{all_different} constraint.
\begin{table}
\begin{center}
\input{assets/table/half_qcp}
\caption{\label{tab:half-qcp} \gls{qcp-max} problems: number of solved instances and average time (in seconds) with a 300s timeout.}
\end{center}
\end{table}
The results in \cref{tab:half-qcp} show that the usage of the specialised propagator has a significant advantage over the use of the decomposition.
Although it only allows us to solve a single extra instance, there is a significant reduction in solving time for most instances.
Note that the qcp-15 instances are the only exception.
It appears that none of the instances in this group proved to be a real challenge to either method and we see similar solve times between the two methods.
For our second experiment we consider a variation on the prize collecting travelling salesman problem \autocite{balas-1989-pctsp} referred to as \emph{prize collecting path}.
In the problem we are given a graph with weighted edges, both positive and negative.
The aim of the problem is to find the optimal acyclic path from a given start node that maximises the weights on the path.
It is not required to visit every node.
In this experiment we can show how \gls{half-reif} can reduce the overhead of handling partial functions correctly.
The \minizinc{} model for this problem contains a unsafe array lookup \mzninline{pos[next[i]]}, where the domain of \mzninline{next[i]} is larger than the index set of \mzninline{pos}.
We compare safe decomposition of this \mzninline{element} constraint against a propagator of its \gls{half-reif}.
The decomposition creates a new variable that takes the value of the index only when it is within the index set of the array.
Otherwise, it will set its surrounding context to \mzninline{false}.
The \gls{half-reif} implicitly performs the same task by setting its control \variable{} to \mzninline{false} whenever the result of the \mzninline{element} constraint does not match the value of the index variable.
Again, for the implementation of the propagator of the \gls{half-reif} constraint we adjust the regular propagator as described above.
\begin{table}[tb]
\begin{center}
\input{assets/table/half_prize}
\caption{\label{tab:half-prize} Prize collecting paths: number of solved instances and average time (in seconds) and with a 300s timeout.}
\end{center}
\end{table}
The results of the experiment are shown in \cref{tab:half-prize}.
Although the performance on smaller instances is similar, the dedicated propagator consistently outperforms the usage of the decomposition.
The difference in performance becomes more pronounced in the bigger instances.
In the 32-4-8 group, we even see that usage of the propagator allows us to solve an additional three instances.
\subsection{Decomposition experiments}
\label{sec:half-exp-decomp}
To verify the effectiveness of the half reification implementation within the \minizinc{} distribution we compare the results of the current version of 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 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:hr-flat-results} Flattening results: Average changes in the
generated the \flatzinc{} models}
\begin{center}
\begin{tabular}{|l|r||r|r|r||r|}
\hline
Library & Nr. Instances & Full Reifications
& Constraints & Variables & Chains Compressed \\
\hline
Gecode & 274 & -38.19\% & -6.29\% & -8.66\% & 21.35\% \\
\hline
Linear & 346 & - 33.11\% & -13.77\% & -3.70\% & 11.63\% \\
\hline
\end{tabular}
\end{center}
\end{table}
\jip{Better headers for \cref{tab:hr-flat-results} and update with current results}
\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.
Together with the chain compression mechanism this has a positive effect on the number of constraints and the number of variables.
\jip{Add something regarding the chain compression. Is this the right
statistic? (Compressions in terms of reifications)}
We ran our experiments on a computer with a 3.40GHz Intel i7-3770 CPU and 16Gb of memory running the Ubuntu 16.04.3 LTS operating system.
\jip{Scatter plot of the runtimes + discussion!}
\section{Summary}
\label{sec:half-experiments}
\label{sec:half-summary}

View File

@ -3,17 +3,80 @@
\label{ch:benchmarks}
%************************************************
\noindent{}All experiments included in this thesis were conducted on a dedicated node in a
computation cluster. The machine operates using a \textbf{Intel Xeon 8260}
\gls{cpu}, which has 24 non-hyperthreaded cores, and has access to
\textbf{268.55 GB} of \gls{ram}. Each experimental test was given exclusive
access to a single \gls{cpu} core and access to sufficient \gls{ram}.
\noindent{}All experiments included in this thesis were conducted on a dedicated node in a computational cluster.
The machine operates using a \textbf{Intel Xeon 8260} \gls{cpu}, which has 24 non-hyperthreaded cores, and has access to \textbf{268.55 GB} of \gls{ram}.
Each experimental test was given exclusive access to a single \gls{cpu} core and access to sufficient \gls{ram}.
\section{Software}%
\label{sec:bench-soft}
\subsection{MiniZinc Flattener}
In this thesis we use three different versions of the \minizinc\ flattening tool
\subsection{MiniZinc Solvers}
\paragraph{Gecode}
\paragraph{Chuffed}
\paragraph{IBM CPLEX}
\paragraph{Gurobi}
\section{MiniZinc Models}%
\label{sec:bench-models}
\section{Other Programs}%
\paragraph{QCP-Max} This problem was introduced in by Feydy et al. in the original paper on \gls{half-reif} \autocite{feydy-2011-half-reif}. \jip{...}
\begin{mzn}
include "all_different.mzn";
int: n; % size
array[1..n,1..n] of 0..n: s; % 0 = unfixed 1..n = fixed
array[1..n,1..n] of var 1..n: q; % qcp array;
constraint forall(i,j in 1..n where s[i,j] > 0)(q[i,j] = s[i,j]);
solve maximize
sum(i in 1..n)(all_different([q[i,j] | j in 1..n])) +
sum(j in 1..n)(all_different([q[i,j] | i in 1..n]));
\end{mzn}
\paragraph{Prize Collecting Path} This problem was introduced in by Feydy et al. in the original paper on \gls{half-reif} \autocite{feydy-2011-half-reif}. \jip{...}
\begin{mzn}
include "alldifferent_except_0.mzn";
int: n; % size
array[1..n,0..n] of int: p; % prize for edge (i,j), p[i,0] = 0
array[1..n] of var 0..n: next; % next posn in tour
array[1..n] of var 0..n: pos; % posn on node i in path, 0=notin
array[1..n] of var int: prize = [p[i,next[i]] | i in 1..n];
% prize for outgoing edge
constraint forall(i in 1..n)(
(pos[i] = 0 <-> next[i] = 0) /\
(next[i] > 1 -> pos[next[i]] = pos[i] + 1)
);
constraint alldifferent_except_0(next) /\ pos[1] = 1;
solve minimize sum(i in 1..n)(prize[i]);
\end{mzn}
\begin{mzn}
include "subcircuit.mzn";
int: n; % size
array[1..n,1..n] of int: p; % prize for edge (i,j), p[i,i] = 0
array[1..n] of var 0..n: next; % next posn in tour
array[1..n] of var int: prize = [p[i,next[i]] | i in 1..n];
% prize for outgoing edge
constraint subcircuit(next);
solve minimize sum(i in 1..n)(prize[i]);
\end{mzn}
\section{Benchmark Environments}%
\label{sec:bench-programs}