Compare commits

...

2 commits

Author SHA1 Message Date
Théophile Bastian 0bf014766a Explain Ullmann 2017-08-22 01:37:07 +02:00
Théophile Bastian 34458bcc44 Start explaining Ullmann 2017-08-21 23:35:21 +02:00

View file

@ -420,7 +420,114 @@ VF2 algorithm~\cite{cordella2004sub}, published in 2004. This algorithm is
mostly Ullmann's algorithm, transcribed in a recursive writing, with the
addition of five heuristics. \qtodo{Why not use it then?}
\todo{Describe Ullmann}
Ullmann is a widely used and fast algorithm for this problem. It makes an
extensive use of adjacency matrix description of the graph, and the initial
article takes advantage of the representation of those matrices as bitsets to
make extensive use of bitwise operations.
The to-be-built permutation matrix is a $\card{needle} \times \card{haystack}$
matrix. Each $1$ in a cell $(i, j)$ indicates that the $i$-th needle part is a
possible match with the $j$-th haystack part. This matrix is called $perm$
thereafter.
The algorithm, left apart the \textsc{refine} function (detailed just after),
is described in Figure~\ref{alg:ullmann}.
\begin{figure}[h]
\begin{algorithmic}
\Function{find\_at\_depth}{depth, perm, freeVert}
\If{no 1s on \lstc{perm[depth]}}
\State{} \Return{}
\EndIf{}
\State{} Save perm
\For{$0 \leq$ chosen $< \card{\text{haystack}}$ such that
\lstc{perm[depth][chosen]} $ = 1$ and \lstc{freeVert[chosen]}}
\State{} Put $0$s everywhere on \lstc{perm[depth]}, but on
\lstc{chosen}
\State{} Refine perm
\If{a row of perm has only $0$s}
\State{} \Return{}
\EndIf{}
\If{depth $=$ $\card{\text{needle}} - 1$}
\State{} Store perm as a result
\Else{}
\State{} \Call{find\_at\_depth}{depth$+1$, perm, freeVert with
freeVert[chosen] $= 0$}
\EndIf{}
\State{} Restore perm
\EndFor{}
\EndFunction{}
\vspace{1em}
\Function{find}{perm}
\State{} \Return{} \Call{find\_at\_depth}{0, perm, [$1, \ldots, 1$]}
\EndFunction{}
\end{algorithmic}
\caption{Ullmann's algorithm (without refining)}\label{alg:ullmann}
\end{figure}
The refining process is the actual keystone of the algorithm. It is the
mechanism allowing the algorithm to cut down many exploration branches, by
removing ones from the matrix.
The idea is that a match between a needle's vertex $i$ and a haystack's vertex
$j$ is only possible if, for each neighbour $k$ of $i$, $j$ has a neighbour
$k'$ such that the permutation matrix has a one in position $(k, k')$. In other
words, a match between $i$ and $j$ is only possible if every neighbour $k$ of
$i$ (in needle) has a possibly matching (\wrt{} $perm$) vertex $k'$ (in
haystack) which is a neighbour of $j$.
This condition is checked on every $1$ in the permutation matrix. If it is not
met, the cell is nulled. This, though, potentially creates new ones not
matching the condition: the process must be run again, until no new zeroes
appear.
In the initial article~\cite{ullmann1976algorithm}, Ullmann advocates for
bitwise tricks to complete this expensive step: indeed, checking the existence
of such a $k'$ can be done by checking the nullity of the bitwise \textsc{and}
of the adjacency of $j$ and the permutation matrix row of $k$.
The refining function is detailed in Figure~\ref{alg:ullmann_refine}.
\todo{Insert explaining figure}
\begin{figure}[h]
\begin{algorithmic}
\Function{refine}{perm}
\While{changes during last run}
\For{each needle vertex $i$}
\For{each haystack vertex $j$}
\For{each neighbour $k$ of $i$ in needle}
\If{\lstc{perm[k] & haystack\_adjacency[j]}
$= 0$}
\State{} \lstc{perm[i][j]} $\gets 0$
\EndIf{}
\EndFor{}
\EndFor{}
\EndFor{}
\EndWhile{}
\EndFunction{}
\end{algorithmic}
\caption{Ullmann's refining function}\label{alg:ullmann_refine}
\end{figure}
\subsection{Implementation optimisations}
The matrix is first filled according to the signatures matches. It is then
refined a bit more, by making sure that for every match, every potentially
matching gate has the same ``wire kinds''. Indeed, a gate needle's wire must
have at least the same inbound adjacent signatures as its matching haystack
wire, and same goes for outbound adjacent signatures. Thus, two circuits cannot
be matched if this condition is not respected for each pair of corresponding
wires of those circuits, and their corresponding cell in the permutation matrix
can be nulled.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Performance}
@ -432,6 +539,8 @@ addition of five heuristics. \qtodo{Why not use it then?}
There were a few observed cases where the algorithm tends to be slower on
certain configurations.
\todo{More corner cases}
\paragraph{Split/merge trees.} A common pattern that tends to slow down the
algorithm is split/merge trees. Those patterns occur when one wants to merge
$n$ one bit wires into a single $n$ bits wire, or the other way around.
@ -444,9 +553,16 @@ tree of depth \eg{} 8, a node just below the root will need a signature of
order 7 to have a different signature than another one at the same depth. With
a signature of order up to 6, only other gates from the tree will be included
in the signature when going down in the tree; the exact same gates will be
included above the tree's root. Thus, nothing will differentiate one gate from another while
the boundary of the tree is not reached (assuming the gates below the tree's
leaves are not all the same; if so, more levels will be needed).
included above the tree's root. Thus, nothing will differentiate one gate from
another while the boundary of the tree is not reached (assuming the gates below
the tree's leaves are not all the same; if so, more levels will be needed).
As the notion of ``left child'' and ``right child'' cannot be used (since it
would rely on the order or description of the graph), there seems to be no good
way to discriminate those two nodes. Furthermore, the nodes are not totally
interchangeable: indeed, when checking for an equality between two such trees,
it does not matter which node is the left one; but once this is fixed, the
nodes on the layer below cannot be freely exchanged.
\todo{Figure describing the problem}