]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/graph/doc/isomorphism-impl.w
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / graph / doc / isomorphism-impl.w
CommitLineData
7c673cae
FG
1\documentclass[11pt]{report}
2
3%\input{defs}
4\usepackage{math}
5\usepackage{jweb}
6\usepackage{lgrind}
7\usepackage{times}
8\usepackage{fullpage}
9\usepackage{graphicx}
10
11\newif\ifpdf
12\ifx\pdfoutput\undefined
13 \pdffalse
14\else
15 \pdfoutput=1
16 \pdftrue
17\fi
18
19\ifpdf
20 \usepackage[
21 pdftex,
22 colorlinks=true, %change to true for the electronic version
23 linkcolor=blue,filecolor=blue,pagecolor=blue,urlcolor=blue
24 ]{hyperref}
25\fi
26
27\ifpdf
28 \newcommand{\stlconcept}[1]{\href{http://www.sgi.com/tech/stl/#1.html}{{\small \textsf{#1}}}}
29 \newcommand{\bglconcept}[1]{\href{http://www.boost.org/libs/graph/doc/#1.html}{{\small \textsf{#1}}}}
30 \newcommand{\pmconcept}[1]{\href{http://www.boost.org/libs/property_map/#1.html}{{\small \textsf{#1}}}}
31 \newcommand{\myhyperref}[2]{\hyperref[#1]{#2}}
32 \newcommand{\vizfig}[2]{\begin{figure}[htbp]\centerline{\includegraphics*{#1.pdf}}\caption{#2}\label{fig:#1}\end{figure}}
33\else
34 \newcommand{\myhyperref}[2]{#2}
35 \newcommand{\bglconcept}[1]{{\small \textsf{#1}}}
36 \newcommand{\pmconcept}[1]{{\small \textsf{#1}}}
37 \newcommand{\stlconcept}[1]{{\small \textsf{#1}}}
38 \newcommand{\vizfig}[2]{\begin{figure}[htbp]\centerline{\includegraphics*{#1.eps}}\caption{#2}\label{fig:#1}\end{figure}}
39\fi
40
41\newcommand{\code}[1]{{\small{\em \textbf{#1}}}}
42
43
44% jweb -np isomorphism-impl.w; dot -Tps out.dot -o out.eps; dot -Tps in.dot -o in.eps; latex isomorphism-impl.tex; dvips isomorphism-impl.dvi -o isomorphism-impl.ps
45
46\setlength\overfullrule{5pt}
47\tolerance=10000
48\sloppy
49\hfuzz=10pt
50
51\makeindex
52
53\newcommand{\isomorphic}{\cong}
54
55\begin{document}
56
57\title{An Implementation of Isomorphism Testing}
58\author{Jeremy G. Siek}
59
60\maketitle
61
62\section{Introduction}
63
64This paper documents the implementation of the \code{isomorphism()}
65function of the Boost Graph Library. The implementation was by Jeremy
66Siek with algorithmic improvements and test code from Douglas Gregor.
67The \code{isomorphism()} function answers the question, ``are these
68two graphs equal?'' By \emph{equal}, we mean the two graphs have the
69same structure---the vertices and edges are connected in the same
70way. The mathematical name for this kind of equality is
71\emph{isomorphic}.
72
73An \emph{isomorphism} is a one-to-one mapping of the vertices in one
74graph to the vertices of another graph such that adjacency is
75preserved. Another words, given graphs $G_{1} = (V_{1},E_{1})$ and
76$G_{2} = (V_{2},E_{2})$, an isomorphism is a function $f$ such that
77for all pairs of vertices $a,b$ in $V_{1}$, edge $(a,b)$ is in $E_{1}$
78if and only if edge $(f(a),f(b))$ is in $E_{2}$.
79
80Both graphs must be the same size, so let $N = |V_1| = |V_2|$. The
81graph $G_1$ is \emph{isomorphic} to $G_2$ if an isomorphism exists
82between the two graphs, which we denote by $G_1 \isomorphic G_2$.
83
84In the following discussion we will need to use several notions from
85graph theory. The graph $G_s=(V_s,E_s)$ is a \emph{subgraph} of graph
86$G=(V,E)$ if $V_s \subseteq V$ and $E_s \subseteq E$. An
87\emph{induced subgraph}, denoted by $G[V_s]$, of a graph $G=(V,E)$
88consists of the vertices in $V_s$, which is a subset of $V$, and every
89edge $(u,v)$ in $E$ such that both $u$ and $v$ are in $V_s$. We use
90the notation $E[V_s]$ to mean the edges in $G[V_s]$.
91
92In some places we express a function as a set of pairs, so the set $f
93= \{ \pair{a_1}{b_1}, \ldots, \pair{a_n}{b_n} \}$
94means $f(a_i) = b_i$ for $i=1,\ldots,n$.
95
96\section{Exhaustive Backtracking Search}
97
98The algorithm used by the \code{isomorphism()} function is, at
99first approximation, an exhaustive search implemented via
100backtracking. The backtracking algorithm is a recursive function. At
101each stage we will try to extend the match that we have found so far.
102So suppose that we have already determined that some subgraph of $G_1$
103is isomorphic to a subgraph of $G_2$. We then try to add a vertex to
104each subgraph such that the new subgraphs are still isomorphic to one
105another. At some point we may hit a dead end---there are no vertices
106that can be added to extend the isomorphic subgraphs. We then
107backtrack to previous smaller matching subgraphs, and try extending
108with a different vertex choice. The process ends by either finding a
109complete mapping between $G_1$ and $G_2$ and return true, or by
110exhausting all possibilities and returning false.
111
112We are going to consider the vertices of $G_1$ in a specific order
113(more about this later), so assume that the vertices of $G_1$ are
114labeled $1,\ldots,N$ according to the order that we plan to add them
115to the subgraph. Let $G_1[k]$ denote the subgraph of $G_1$ induced by
116the first $k$ vertices, with $G_1[0]$ being an empty graph. At each
117stage of the recursion we start with an isomorphism $f_{k-1}$ between
118$G_1[k-1]$ and a subgraph of $G_2$, which we denote by $G_2[S]$, so
119$G_1[k-1] \isomorphic G_2[S]$. The vertex set $S$ is the subset of
120$V_2$ that corresponds via $f_{k-1}$ to the first $k-1$ vertices in
121$G_1$. We try to extend the isomorphism by finding a vertex $v \in V_2
122- S$ that matches with vertex $k$. If a matching vertex is found, we
123have a new isomorphism $f_k$ with $G_1[k] \isomorphic G_2[S \union \{
124v \}]$.
125
126\begin{tabbing}
127IS\=O\=M\=O\=RPH($k$, $S$, $f_{k-1}$) $\equiv$ \\
128\>\textbf{if} ($k = |V_1|+1$) \\
129\>\>\textbf{return} true \\
130\>\textbf{for} each vertex $v \in V_2 - S$ \\
131\>\>\textbf{if} (MATCH($k$, $v$)) \\
132\>\>\>$f_k = f_{k-1} \union \pair{k}{v}$ \\
133\>\>\>ISOMORPH($k+1$, $S \union \{ v \}$, $f_k$)\\
134\>\>\textbf{else}\\
135\>\>\>\textbf{return} false \\
136\\
137ISOMORPH($0$, $G_1$, $\emptyset$, $G_2$)
138\end{tabbing}
139
140The basic idea of the match operation is to check whether $G_1[k]$ is
141isomorphic to $G_2[S \union \{ v \}]$. We already know that $G_1[k-1]
142\isomorphic G_2[S]$ with the mapping $f_{k-1}$, so all we need to do
143is verify that the edges in $E_1[k] - E_1[k-1]$ connect vertices that
144correspond to the vertices connected by the edges in $E_2[S \union \{
145v \}] - E_2[S]$. The edges in $E_1[k] - E_1[k-1]$ are all the
146out-edges $(k,j)$ and in-edges $(j,k)$ of $k$ where $j$ is less than
147or equal to $k$ according to the ordering. The edges in $E_2[S \union
148\{ v \}] - E_2[S]$ consists of all the out-edges $(v,u)$ and
149in-edges $(u,v)$ of $v$ where $u \in S$.
150
151\begin{tabbing}
152M\=ATCH($k$, $v$) $\equiv$ \\
153\>$out \leftarrow \forall (k,j) \in E_1[k] - E_1[k-1] \Big( (v,f(j)) \in E_2[S \union \{ v \}] - E_2[S] \Big)$ \\
154\>$in \leftarrow \forall (j,k) \in E_1[k] - E_1[k-1] \Big( (f(j),v) \in E_2[S \union \{ v \}] - E_2[S] \Big)$ \\
155\>\textbf{return} $out \Land in$
156\end{tabbing}
157
158The problem with the exhaustive backtracking algorithm is that there
159are $N!$ possible vertex mappings, and $N!$ gets very large as $N$
160increases, so we need to prune the search space. We use the pruning
161techniques described in
162\cite{deo77:_new_algo_digraph_isomorph,fortin96:_isomorph,reingold77:_combin_algo}
163that originated in
164\cite{sussenguth65:_isomorphism,unger64:_isomorphism}.
165
166\section{Vertex Invariants}
167\label{sec:vertex-invariants}
168
169One way to reduce the search space is through the use of \emph{vertex
170invariants}. The idea is to compute a number for each vertex $i(v)$
171such that $i(v) = i(v')$ if there exists some isomorphism $f$ where
172$f(v) = v'$. Then when we look for a match to some vertex $v$, we only
173need to consider those vertices that have the same vertex invariant
174number. The number of vertices in a graph with the same vertex
175invariant number $i$ is called the \emph{invariant multiplicity} for
176$i$. In this implementation, by default we use the out-degree of the
177vertex as the vertex invariant, though the user can also supply their
178own invariant function. The ability of the invariant function to prune
179the search space varies widely with the type of graph.
180
181As a first check to rule out graphs that have no possibility of
182matching, one can create a list of computed vertex invariant numbers
183for the vertices in each graph, sort the two lists, and then compare
184them. If the two lists are different then the two graphs are not
185isomorphic. If the two lists are the same then the two graphs may be
186isomorphic.
187
188Also, we extend the MATCH operation to use the vertex invariants to
189help rule out vertices.
190
191\begin{tabbing}
192M\=A\=T\=C\=H-INVAR($k$, $v$) $\equiv$ \\
193\>$out \leftarrow \forall (k,j) \in E_1[k] - E_1[k-1] \Big( (v,f(j)) \in E_2[S \union \{ v \}] - E_2[S] \Land i(v) = i(k) \Big)$ \\
194\>$in \leftarrow \forall (j,k) \in E_1[k] - E_1[k-1] \Big( (f(j),v) \in E_2[S \union \{ v \}] - E_2[S] \Land i(v) = i(k) \Big)$ \\
195\>\textbf{return} $out \Land in$
196\end{tabbing}
197
198\section{Vertex Order}
199
200A good choice of the labeling for the vertices (which determines the
201order in which the subgraph $G_1[k]$ is grown) can also reduce the
202search space. In the following we discuss two labeling heuristics.
203
204\subsection{Most Constrained First}
205
206Consider the most constrained vertices first. That is, examine
207lower-degree vertices before higher-degree vertices. This reduces the
208search space because it chops off a trunk before the trunk has a
209chance to blossom out. We can generalize this to use vertex
210invariants. We examine vertices with low invariant multiplicity
211before examining vertices with high invariant multiplicity.
212
213\subsection{Adjacent First}
214
215The MATCH operation only considers edges when the other vertex already
216has a mapping defined. This means that the MATCH operation can only
217weed out vertices that are adjacent to vertices that have already been
218matched. Therefore, when choosing the next vertex to examine, it is
219desirable to choose one that is adjacent a vertex already in $S_1$.
220
221\subsection{DFS Order, Starting with Lowest Multiplicity}
222
223For this implementation, we combine the above two heuristics in the
224following way. To implement the ``adjacent first'' heuristic we apply
225DFS to the graph, and use the DFS discovery order as our vertex
226order. To comply with the ``most constrained first'' heuristic we
227order the roots of our DFS trees by invariant multiplicity.
228
229
230\section{Implementation}
231
232The following is the public interface for the \code{isomorphism}
233function. The input to the function is the two graphs $G_1$ and $G_2$,
234mappings from the vertices in the graphs to integers (in the range
235$[0,|V|)$), and a vertex invariant function object. The output of the
236function is an isomorphism $f$ if there is one. The \code{isomorphism}
237function returns true if the graphs are isomorphic and false
238otherwise. The requirements on type template parameters are described
239below in the section ``Concept checking''.
240
241@d Isomorphism Function Interface
242@{
243template <typename Graph1, typename Graph2,
244 typename IndexMapping,
245 typename VertexInvariant1, typename VertexInvariant2,
246 typename IndexMap1, typename IndexMap2>
247bool isomorphism(const Graph1& g1, const Graph2& g2,
248 IndexMapping f,
249 VertexInvariant1 invariant1, VertexInvariant2 invariant2,
250 IndexMap1 index_map1, IndexMap2 index_map2)
251@}
252
253The main outline of the \code{isomorphism} function is as
254follows. Most of the steps in this function are for setting up the
255vertex ordering, first ordering the vertices by invariant multiplicity
256and then by DFS order. The last step is the call to the
257\code{isomorph} function which starts the backtracking search.
258
259@d Isomorphism Function Body
260@{
261{
262 @<Some type definitions and iterator declarations@>
263 @<Concept checking@>
264 @<Quick return with false if $|V_1| \neq |V_2|$@>
265 @<Compute vertex invariants@>
266 @<Quick return if the graph's invariants do not match@>
267 @<Compute invariant multiplicity@>
268 @<Sort vertices by invariant multiplicity@>
269 @<Order the vertices by DFS discover time@>
270 @<Order the edges by DFS discover time@>
271 @<Invoke recursive \code{isomorph} function@>
272}
273@}
274
275There are some types that will be used throughout the function, which
276we create shortened names for here. We will also need vertex
277iterators for \code{g1} and \code{g2} in several places, so we define
278them here.
279
280@d Some type definitions and iterator declarations
281@{
282typedef typename graph_traits<Graph1>::vertex_descriptor vertex1_t;
283typedef typename graph_traits<Graph2>::vertex_descriptor vertex2_t;
284typedef typename graph_traits<Graph1>::vertices_size_type size_type;
285typename graph_traits<Graph1>::vertex_iterator i1, i1_end;
286typename graph_traits<Graph2>::vertex_iterator i2, i2_end;
287@}
288
289We use the Boost Concept Checking Library to make sure that the type
290arguments to the function fulfill there requirements. The
291\code{Graph1} type must be a \bglconcept{VertexListGraph} and a
292\bglconcept{EdgeListGraph}. The \code{Graph2} type must be a
293\bglconcept{VertexListGraph} and a
294\bglconcept{BidirectionalGraph}. The \code{IndexMapping} type that
295represents the isomorphism $f$ must be a
296\pmconcept{ReadWritePropertyMap} that maps from vertices in $G_1$ to
297vertices in $G_2$. The two other index maps are
298\pmconcept{ReadablePropertyMap}s from vertices in $G_1$ and $G_2$ to
299unsigned integers.
300
301@d Concept checking
302@{
303// Graph requirements
304BOOST_CONCEPT_ASSERT(( VertexListGraphConcept<Graph1> ));
305BOOST_CONCEPT_ASSERT(( EdgeListGraphConcept<Graph1> ));
306BOOST_CONCEPT_ASSERT(( VertexListGraphConcept<Graph2> ));
307BOOST_CONCEPT_ASSERT(( BidirectionalGraphConcept<Graph2> ));
308
309// Property map requirements
310BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept<IndexMapping, vertex1_t> ));
311typedef typename property_traits<IndexMapping>::value_type IndexMappingValue;
312BOOST_STATIC_ASSERT((is_same<IndexMappingValue, vertex2_t>::value));
313
314BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept<IndexMap1, vertex1_t> ));
315typedef typename property_traits<IndexMap1>::value_type IndexMap1Value;
316BOOST_STATIC_ASSERT((is_convertible<IndexMap1Value, size_type>::value));
317
318BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept<IndexMap2, vertex2_t> ));
319typedef typename property_traits<IndexMap2>::value_type IndexMap2Value;
320BOOST_STATIC_ASSERT((is_convertible<IndexMap2Value, size_type>::value));
321@}
322
323
324\noindent If there are no vertices in either graph, then they are trivially
325isomorphic.
326
327@d Quick return with false if $|V_1| \neq |V_2|$
328@{
329if (num_vertices(g1) != num_vertices(g2))
330 return false;
331@}
332
333
334\subsection{Ordering by Vertex Invariant Multiplicity}
335
336The user can supply the vertex invariant functions as a
337\stlconcept{AdaptableUnaryFunction} (with the addition of the
338\code{max} function) in the \code{invariant1} and \code{invariant2}
339parameters. We also define a default which uses the out-degree and
340in-degree of a vertex. The following is the definition of the function
341object for the default vertex invariant. User-defined vertex invariant
342function objects should follow the same pattern.
343
344@d Degree vertex invariant
345@{
346template <typename InDegreeMap, typename Graph>
347class degree_vertex_invariant
348{
349public:
350 typedef typename graph_traits<Graph>::vertex_descriptor argument_type;
351 typedef typename graph_traits<Graph>::degree_size_type result_type;
352
353 degree_vertex_invariant(const InDegreeMap& in_degree_map, const Graph& g)
354 : m_in_degree_map(in_degree_map), m_g(g) { }
355
356 result_type operator()(argument_type v) const {
357 return (num_vertices(m_g) + 1) * out_degree(v, m_g)
358 + get(m_in_degree_map, v);
359 }
360 // The largest possible vertex invariant number
361 result_type max() const {
362 return num_vertices(m_g) * num_vertices(m_g) + num_vertices(m_g);
363 }
364private:
365 InDegreeMap m_in_degree_map;
366 const Graph& m_g;
367};
368@}
369
370Since the invariant function may be expensive to compute, we
371pre-compute the invariant numbers for every vertex in the two
372graphs. The variables \code{invar1} and \code{invar2} are property
373maps for accessing the stored invariants, which are described next.
374
375@d Compute vertex invariants
376@{
377@<Setup storage for vertex invariants@>
378for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
379 invar1[*i1] = invariant1(*i1);
380for (tie(i2, i2_end) = vertices(g2); i2 != i2_end; ++i2)
381 invar2[*i2] = invariant2(*i2);
382@}
383
384\noindent We store the invariants in two vectors, indexed by the vertex indices
385of the two graphs. We then create property maps for accessing these
386two vectors in a more convenient fashion (they go directly from vertex
387to invariant, instead of vertex to index to invariant).
388
389@d Setup storage for vertex invariants
390@{
391typedef typename VertexInvariant1::result_type InvarValue1;
392typedef typename VertexInvariant2::result_type InvarValue2;
393typedef std::vector<InvarValue1> invar_vec1_t;
394typedef std::vector<InvarValue2> invar_vec2_t;
395invar_vec1_t invar1_vec(num_vertices(g1));
396invar_vec2_t invar2_vec(num_vertices(g2));
397typedef typename invar_vec1_t::iterator vec1_iter;
398typedef typename invar_vec2_t::iterator vec2_iter;
399iterator_property_map<vec1_iter, IndexMap1, InvarValue1, InvarValue1&>
400 invar1(invar1_vec.begin(), index_map1);
401iterator_property_map<vec2_iter, IndexMap2, InvarValue2, InvarValue2&>
402 invar2(invar2_vec.begin(), index_map2);
403@}
404
405As discussed in \S\ref{sec:vertex-invariants}, we can quickly rule out
406the possibility of any isomorphism between two graphs by checking to
407see if the vertex invariants can match up. We sort both vectors of vertex
408invariants, and then check to see if they are equal.
409
410@d Quick return if the graph's invariants do not match
411@{
412{ // check if the graph's invariants do not match
413 invar_vec1_t invar1_tmp(invar1_vec);
414 invar_vec2_t invar2_tmp(invar2_vec);
415 std::sort(invar1_tmp.begin(), invar1_tmp.end());
416 std::sort(invar2_tmp.begin(), invar2_tmp.end());
417 if (! std::equal(invar1_tmp.begin(), invar1_tmp.end(),
418 invar2_tmp.begin()))
419 return false;
420}
421@}
422
423Next we compute the invariant multiplicity, the number of vertices
424with the same invariant number. The \code{invar\_mult} vector is
425indexed by invariant number. We loop through all the vertices in the
426graph to record the multiplicity.
427
428@d Compute invariant multiplicity
429@{
430std::vector<std::size_t> invar_mult(invariant1.max(), 0);
431for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
432 ++invar_mult[invar1[*i1]];
433@}
434
435\noindent We then order the vertices by their invariant multiplicity.
436This will allow us to search the more constrained vertices first.
437Since we will need to know the permutation from the original order to
438the new order, we do not sort the vertices directly. Instead we sort
439the vertex indices, creating the \code{perm} array. Once sorted, this
440array provides a mapping from the new index to the old index.
441We then use the \code{permute} function to sort the vertices of
442the graph, which we store in the \code{g1\_vertices} vector.
443
444@d Sort vertices by invariant multiplicity
445@{
446std::vector<size_type> perm;
447integer_range<size_type> range(0, num_vertices(g1));
448std::copy(range.begin(), range.end(), std::back_inserter(perm));
449std::sort(perm.begin(), perm.end(),
450 detail::compare_invariant_multiplicity(invar1_vec.begin(),
451 invar_mult.begin()));
452
453std::vector<vertex1_t> g1_vertices;
454for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
455 g1_vertices.push_back(*i1);
456permute(g1_vertices.begin(), g1_vertices.end(), perm.begin());
457@}
458
459\noindent The definition of the \code{compare\_multiplicity} predicate
460is shown below. This predicate provides the glue that binds
461\code{std::sort} to our current purpose.
462
463@d Compare multiplicity predicate
464@{
465namespace detail {
466 template <typename InvarMap, typename MultMap>
467 struct compare_invariant_multiplicity_predicate
468 {
469 compare_invariant_multiplicity_predicate(InvarMap i, MultMap m)
470 : m_invar(i), m_mult(m) { }
471
472 template <typename Vertex>
473 bool operator()(const Vertex& x, const Vertex& y) const
474 { return m_mult[m_invar[x]] < m_mult[m_invar[y]]; }
475
476 InvarMap m_invar;
477 MultMap m_mult;
478 };
479 template <typename InvarMap, typename MultMap>
480 compare_invariant_multiplicity_predicate<InvarMap, MultMap>
481 compare_invariant_multiplicity(InvarMap i, MultMap m) {
482 return compare_invariant_multiplicity_predicate<InvarMap, MultMap>(i,m);
483 }
484} // namespace detail
485@}
486
487
488\subsection{Ordering by DFS Discover Time}
489
490To implement the ``visit adjacent vertices first'' heuristic, we order
491the vertices according to DFS discover time. We replace the ordering
492in \code{perm} with the new DFS ordering. Again, we use \code{permute}
493to sort the vertices of graph \code{g1}.
494
495@d Order the vertices by DFS discover time
496@{
497{
498 perm.clear();
499 @<Compute DFS discover times@>
500 g1_vertices.clear();
501 for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
502 g1_vertices.push_back(*i1);
503 permute(g1_vertices.begin(), g1_vertices.end(), perm.begin());
504}
505@}
506
507We implement the outer-loop of the DFS here, instead of calling the
508\code{depth\_first\_search} function, because we want the roots of the
509DFS tree's to be ordered by invariant multiplicity. We call
510\code{depth\_\-first\_\-visit} to implement the recursive portion of
511the DFS. The \code{record\_dfs\_order} adapts the DFS to record
512the order in which DFS discovers the vertices.
513
514@d Compute DFS discover times
515@{
516std::vector<default_color_type> color_vec(num_vertices(g1));
517for (typename std::vector<vertex1_t>::iterator ui = g1_vertices.begin();
518 ui != g1_vertices.end(); ++ui) {
519 if (color_vec[get(index_map1, *ui)]
520 == color_traits<default_color_type>::white()) {
521 depth_first_visit
522 (g1, *ui, detail::record_dfs_order<Graph1, IndexMap1>(perm,
523 index_map1),
524 make_iterator_property_map(&color_vec[0], index_map1,
525 color_vec[0]));
526 }
527}
528@}
529
530\noindent The definition of the \code{record\_dfs\_order} visitor
531class is as follows. The index of each vertex is recorded in the
532\code{dfs\_order} vector (which is the \code{perm} vector) in the
533\code{discover\_vertex} event point.
534
535@d Record DFS ordering visitor
536@{
537namespace detail {
538 template <typename Graph1, typename IndexMap1>
539 struct record_dfs_order : public default_dfs_visitor {
540 typedef typename graph_traits<Graph1>::vertices_size_type size_type;
541 typedef typename graph_traits<Graph1>::vertex_descriptor vertex;
542
543 record_dfs_order(std::vector<size_type>& dfs_order, IndexMap1 index)
544 : dfs_order(dfs_order), index(index) { }
545
546 void discover_vertex(vertex v, const Graph1& g) const {
547 dfs_order.push_back(get(index, v));
548 }
549 std::vector<size_type>& dfs_order;
550 IndexMap1 index;
551 };
552} // namespace detail
553@}
554
555
556In the MATCH operation, we need to examine all the edges in the set
557$E_1[k] - E_1[k-1]$. That is, we need to loop through all the edges of
558the form $(k,j)$ or $(j,k)$ where $j \leq k$. To do this efficiently,
559we create an array of all the edges in $G_1$ that has been sorted so
560that $E_1[k] - E_1[k-1]$ forms a contiguous range. To each edge
561$e=(u,v)$ we assign the number $\max(u,v)$, and then sort the edges by
562this number. All the edges $(u,v) \in E_1[k] - E_1[k-1]$ can then be
563identified because $\max(u,v) = k$. The following code creates an
564array of edges and then sorts them. The \code{edge\_\-ordering\_\-fun}
565function object is described next.
566
567@d Order the edges by DFS discover time
568@{
569typedef typename graph_traits<Graph1>::edge_descriptor edge1_t;
570std::vector<edge1_t> edge_set;
571std::copy(edges(g1).first, edges(g1).second, std::back_inserter(edge_set));
572
573std::sort(edge_set.begin(), edge_set.end(),
574 detail::edge_ordering
575 (make_iterator_property_map(perm.begin(), index_map1, perm[0]), g1));
576@}
577
578\noindent The \code{edge\_order} function computes the ordering number
579for an edge, which for edge $e=(u,v)$ is $\max(u,v)$. The
580\code{edge\_\-ordering\_\-fun} function object simply returns
581comparison of two edge's ordering numbers.
582
583@d Isomorph edge ordering predicate
584@{
585namespace detail {
586
587 template <typename VertexIndexMap, typename Graph>
588 std::size_t edge_order(const typename graph_traits<Graph>::edge_descriptor e,
589 VertexIndexMap index_map, const Graph& g) {
590 return std::max(get(index_map, source(e, g)), get(index_map, target(e, g)));
591 }
592
593 template <typename VertexIndexMap, typename Graph>
594 class edge_ordering_fun {
595 public:
596 edge_ordering_fun(VertexIndexMap vip, const Graph& g)
597 : m_index_map(vip), m_g(g) { }
598 template <typename Edge>
599 bool operator()(const Edge& e1, const Edge& e2) const {
600 return edge_order(e1, m_index_map, m_g) < edge_order(e2, m_index_map, m_g);
601 }
602 VertexIndexMap m_index_map;
603 const Graph& m_g;
604 };
605 template <class VertexIndexMap, class G>
606 inline edge_ordering_fun<VertexIndexMap,G>
607 edge_ordering(VertexIndexMap vip, const G& g)
608 {
609 return edge_ordering_fun<VertexIndexMap,G>(vip, g);
610 }
611} // namespace detail
612@}
613
614
615We are now ready to enter the main part of the algorithm, the
616backtracking search implemented by the \code{isomorph} function (which
617corresponds to the ISOMORPH algorithm). The set $S$ is not
618represented directly; instead we represent $V_2 - S$. Initially $S =
619\emptyset$ so $V_2 - S = V_2$. We use the permuted indices for the
620vertices of graph \code{g1}. We represent $V_2 - S$ with a bitset. We
621use \code{std::vector} instead of \code{boost::dyn\_bitset} for speed
622instead of space.
623
624@d Invoke recursive \code{isomorph} function
625@{
626std::vector<char> not_in_S_vec(num_vertices(g2), true);
627iterator_property_map<char*, IndexMap2, char, char&>
628 not_in_S(&not_in_S_vec[0], index_map2);
629
630return detail::isomorph(g1_vertices.begin(), g1_vertices.end(),
631 edge_set.begin(), edge_set.end(), g1, g2,
632 make_iterator_property_map(perm.begin(), index_map1, perm[0]),
633 index_map2, f, invar1, invar2, not_in_S);
634@}
635
636
637\subsection{Implementation of ISOMORPH}
638
639The ISOMORPH algorithm is implemented with the \code{isomorph}
640function. The vertices of $G_1$ are searched in the order specified by
641the iterator range \code{[k\_iter,last)}. The function returns true if
642a isomorphism is found between the vertices of $G_1$ in
643\code{[k\_iter,last)} and the vertices of $G_2$ in \code{not\_in\_S}.
644The mapping is recorded in the parameter \code{f}.
645
646@d Signature for the recursive isomorph function
647@{
648template <class VertexIter, class EdgeIter, class Graph1, class Graph2,
649 class IndexMap1, class IndexMap2, class IndexMapping,
650 class Invar1, class Invar2, class Set>
651bool isomorph(VertexIter k_iter, VertexIter last,
652 EdgeIter edge_iter, EdgeIter edge_iter_end,
653 const Graph1& g1, const Graph2& g2,
654 IndexMap1 index_map1,
655 IndexMap2 index_map2,
656 IndexMapping f, Invar1 invar1, Invar2 invar2,
657 const Set& not_in_S)
658@}
659
660\noindent The steps for this function are as follows.
661
662@d Body of the isomorph function
663@{
664{
665 @<Some typedefs and variable declarations@>
666 @<Return true if matching is complete@>
667 @<Create a copy of $f_{k-1}$ which will become $f_k$@>
668 @<Compute $M$, the potential matches for $k$@>
669 @<Invoke isomorph for each vertex in $M$@>
670}
671@}
672
673\noindent Here we create short names for some often-used types
674and declare some variables.
675
676@d Some typedefs and variable declarations
677@{
678typedef typename graph_traits<Graph1>::vertex_descriptor vertex1_t;
679typedef typename graph_traits<Graph2>::vertex_descriptor vertex2_t;
680typedef typename graph_traits<Graph1>::vertices_size_type size_type;
681
682vertex1_t k = *k_iter;
683@}
684
685\noindent We have completed creating an isomorphism if \code{k\_iter == last}.
686
687@d Return true if matching is complete
688@{
689if (k_iter == last)
690 return true;
691@}
692
693
694In the pseudo-code for ISOMORPH, we iterate through each vertex in $v
695\in V_2 - S$ and check if $k$ and $v$ can match. A more efficient
696approach is to directly iterate through the potential matches for $k$,
697for this often is many fewer vertices than $V_2 - S$. Let $M$ be the
698set of potential matches for $k$. $M$ consists of all the vertices $v
699\in V_2 - S$ such that if $(k,j)$ or $(j,k) \in E_1[k] - E_1[k-1]$
700then $(v,f(j)$ or $(f(j),v) \in E_2$ with $i(v) = i(k)$. Note that
701this means if there are no edges in $E_1[k] - E_1[k-1]$ then $M = V_2
702- S$. In the case where there are edges in $E_1[k] - E_1[k-1]$ we
703break the computation of $M$ into two parts, computing $out$ sets
704which are vertices that can match according to an out-edge of $k$, and
705computing $in$ sets which are vertices that can match according to an
706in-edge of $k$.
707
708The implementation consists of a loop through the edges of $E_1[k] -
709E_1[k-1]$. The straightforward implementation would initialize $M
710\leftarrow V_2 - S$, and then intersect $M$ with the $out$ or $in$ set
711for each edge. However, to reduce the cost of the intersection
712operation, we start with $M \leftarrow \emptyset$, and on the first
713iteration of the loop we do $M \leftarrow out$ or $M \leftarrow in$
714instead of an intersection operation.
715
716@d Compute $M$, the potential matches for $k$
717@{
718std::vector<vertex2_t> potential_matches;
719bool some_edges = false;
720
721for (; edge_iter != edge_iter_end; ++edge_iter) {
722 if (get(index_map1, k) != edge_order(*edge_iter, index_map1, g1))
723 break;
724 if (k == source(*edge_iter, g1)) { // (k,j)
725 @<Compute the $out$ set@>
726 if (some_edges == false) {
727 @<Perform $M \leftarrow out$@>
728 } else {
729 @<Perform $M \leftarrow M \intersect out$@>
730 }
731 some_edges = true;
732 } else { // (j,k)
733 @<Compute the $in$ set@>
734 if (some_edges == false) {
735 @<Perform $M \leftarrow in$@>
736 } else {
737 @<Perform $M \leftarrow M \intersect in$@>
738 }
739 some_edges = true;
740 }
741 if (potential_matches.empty())
742 break;
743} // for edge_iter
744if (some_edges == false) {
745 @<Perform $M \leftarrow V_2 - S$@>
746}
747@}
748
749To compute the $out$ set, we iterate through the out-edges $(k,j)$ of
750$k$, and for each $j$ we iterate through the in-edges $(v,f(j))$ of
751$f(j)$, putting all of the $v$'s in $out$ that have the same vertex
752invariant as $k$, and which are in $V_2 - S$. Figure~\ref{fig:out}
753depicts the computation of the $out$ set. The implementation is as
754follows.
755
756@d Compute the $out$ set
757@{
758vertex1_t j = target(*edge_iter, g1);
759std::vector<vertex2_t> out;
760typename graph_traits<Graph2>::in_edge_iterator ei, ei_end;
761for (tie(ei, ei_end) = in_edges(get(f, j), g2); ei != ei_end; ++ei) {
762 vertex2_t v = source(*ei, g2); // (v,f[j])
763 if (invar1[k] == invar2[v] && not_in_S[v])
764 out.push_back(v);
765}
766@}
767
768\noindent Here initialize $M$ with the $out$ set. Since we are
769representing sets with sorted vectors, we sort \code{out} before
770copying to \code{potential\_matches}.
771
772@d Perform $M \leftarrow out$
773@{
774indirect_cmp<IndexMap2,std::less<std::size_t> > cmp(index_map2);
775std::sort(out.begin(), out.end(), cmp);
776std::copy(out.begin(), out.end(), std::back_inserter(potential_matches));
777@}
778
779\noindent We use \code{std::set\_intersection} to implement $M
780\leftarrow M \intersect out$. Since there is no version of
781\code{std::set\_intersection} that works in-place, we create a
782temporary for the result and then swap.
783
784@d Perform $M \leftarrow M \intersect out$
785@{
786indirect_cmp<IndexMap2,std::less<std::size_t> > cmp(index_map2);
787std::sort(out.begin(), out.end(), cmp);
788std::vector<vertex2_t> tmp_matches;
789std::set_intersection(out.begin(), out.end(),
790 potential_matches.begin(), potential_matches.end(),
791 std::back_inserter(tmp_matches), cmp);
792std::swap(potential_matches, tmp_matches);
793@}
794
795% Shoot, there is some problem with f(j). Could have to do with the
796% change from the edge set to just using out_edges and in_edges.
797% Yes, have to visit edges in correct order to we don't hit
798% part of f that is not yet defined.
799
800\vizfig{out}{Computing the $out$ set.}
801
802@c out.dot
803@{
804digraph G {
805 node[shape=circle]
806 size="4,2"
807 ratio="fill"
808
809 subgraph cluster0 { label="G_1"
810 k -> j_1
811 k -> j_2
812 k -> j_3
813 }
814
815 subgraph cluster1 { label="G_2"
816
817 subgraph cluster2 { label="out" v_1 v_2 v_3 v_4 v_5 v_6 }
818
819 v_1 -> fj_1
820 v_2 -> fj_1
821 v_3 -> fj_1
822
823 v_4 -> fj_2
824
825 v_5 -> fj_3
826 v_6 -> fj_3
827
828 fj_1[label="f(j_1)"]
829 fj_2[label="f(j_2)"]
830 fj_3[label="f(j_3)"]
831 }
832
833 j_1 -> fj_1[style=dotted]
834 j_2 -> fj_2[style=dotted]
835 j_3 -> fj_3[style=dotted]
836}
837@}
838
839The $in$ set is is constructed by iterating through the in-edges
840$(j,k)$ of $k$, and for each $j$ we iterate through the out-edges
841$(f(j),v)$ of $f(j)$. We put all of the $v$'s in $in$ that have the
842same vertex invariant as $k$, and which are in $V_2 -
843S$. Figure~\ref{fig:in} depicts the computation of the $in$ set. The
844following code computes the $in$ set.
845
846@d Compute the $in$ set
847@{
848vertex1_t j = source(*edge_iter, g1);
849std::vector<vertex2_t> in;
850typename graph_traits<Graph2>::out_edge_iterator ei, ei_end;
851for (tie(ei, ei_end) = out_edges(get(f, j), g2); ei != ei_end; ++ei) {
852 vertex2_t v = target(*ei, g2); // (f[j],v)
853 if (invar1[k] == invar2[v] && not_in_S[v])
854 in.push_back(v);
855}
856@}
857
858\noindent Here initialize $M$ with the $in$ set. Since we are
859representing sets with sorted vectors, we sort \code{in} before
860copying to \code{potential\_matches}.
861
862@d Perform $M \leftarrow in$
863@{
864indirect_cmp<IndexMap2,std::less<std::size_t> > cmp(index_map2);
865std::sort(in.begin(), in.end(), cmp);
866std::copy(in.begin(), in.end(), std::back_inserter(potential_matches));
867@}
868
869\noindent Again we use \code{std::set\_intersection} on
870sorted vectors to implement $M \leftarrow M \intersect in$.
871
872@d Perform $M \leftarrow M \intersect in$
873@{
874indirect_cmp<IndexMap2, std::less<std::size_t> > cmp(index_map2);
875std::sort(in.begin(), in.end(), cmp);
876std::vector<vertex2_t> tmp_matches;
877std::set_intersection(in.begin(), in.end(),
878 potential_matches.begin(), potential_matches.end(),
879 std::back_inserter(tmp_matches), cmp);
880std::swap(potential_matches, tmp_matches);
881@}
882
883\vizfig{in}{Computing the $in$ set.}
884
885@c in.dot
886@{
887digraph G {
888 node[shape=circle]
889 size="3,2"
890 ratio="fill"
891 subgraph cluster0 { label="G1"
892 j_1 -> k
893 j_2 -> k
894 }
895
896 subgraph cluster1 { label="G2"
897
898 subgraph cluster2 { label="in" v_1 v_2 v_3 }
899
900 v_1 -> fj_1
901 v_2 -> fj_1
902
903 v_3 -> fj_2
904
905 fj_1[label="f(j_1)"]
906 fj_2[label="f(j_2)"]
907 }
908
909 j_1 -> fj_1[style=dotted]
910 j_2 -> fj_2[style=dotted]
911
912}
913@}
914
915In the case where there were no edges in $E_1[k] - E_1[k-1]$, then $M
916= V_2 - S$, so here we insert all the vertices from $V_2$ that are not
917in $S$.
918
919@d Perform $M \leftarrow V_2 - S$
920@{
921typename graph_traits<Graph2>::vertex_iterator vi, vi_end;
922for (tie(vi, vi_end) = vertices(g2); vi != vi_end; ++vi)
923 if (not_in_S[*vi])
924 potential_matches.push_back(*vi);
925@}
926
927For each vertex $v$ in the potential matches $M$, we will create an
928extended isomorphism $f_k = f_{k-1} \union \pair{k}{v}$. First
929we create a local copy of $f_{k-1}$.
930
931@d Create a copy of $f_{k-1}$ which will become $f_k$
932@{
933std::vector<vertex2_t> my_f_vec(num_vertices(g1));
934typedef typename std::vector<vertex2_t>::iterator vec_iter;
935iterator_property_map<vec_iter, IndexMap1, vertex2_t, vertex2_t&>
936 my_f(my_f_vec.begin(), index_map1);
937
938typename graph_traits<Graph1>::vertex_iterator i1, i1_end;
939for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
940 my_f[*i1] = get(f, *i1);
941@}
942
943Next we enter the loop through every vertex $v$ in $M$, and extend the
944isomorphism with $\pair{k}{v}$. We then update the set $S$ (by
945removing $v$ from $V_2 - S$) and make the recursive call to
946\code{isomorph}. If \code{isomorph} returns successfully, we have
947found an isomorphism for the complete graph, so we copy our local
948mapping into the mapping from the previous calling function.
949
950@d Invoke isomorph for each vertex in $M$
951@{
952for (std::size_t j = 0; j < potential_matches.size(); ++j) {
953 my_f[k] = potential_matches[j];
954 @<Perform $S' = S - \{ v \}$@>
955 if (isomorph(boost::next(k_iter), last, edge_iter, edge_iter_end, g1, g2,
956 index_map1, index_map2,
957 my_f, invar1, invar2, my_not_in_S)) {
958 for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
959 put(f, *i1, my_f[*i1]);
960 return true;
961 }
962}
963return false;
964@}
965
966We need to create the new set $S' = S - \{ v \}$, which will be the
967$S$ for the next invocation to \code{isomorph}. As before, we
968represent $V_2 - S'$ instead of $S'$ and use a bitset.
969
970@d Perform $S' = S - \{ v \}$
971@{
972std::vector<char> my_not_in_S_vec(num_vertices(g2));
973iterator_property_map<char*, IndexMap2, char, char&>
974 my_not_in_S(&my_not_in_S_vec[0], index_map2);
975typename graph_traits<Graph2>::vertex_iterator vi, vi_end;
976for (tie(vi, vi_end) = vertices(g2); vi != vi_end; ++vi)
977 my_not_in_S[*vi] = not_in_S[*vi];;
978my_not_in_S[potential_matches[j]] = false;
979@}
980
981
982\section{Appendix}
983
984Here we output the header file \code{isomorphism.hpp}. We add a
985copyright statement, include some files, and then pull the top-level
986code parts into namespace \code{boost}.
987
988@o isomorphism.hpp -d
989@{
990
991// (C) Copyright Jeremy Siek 2001. Permission to copy, use, modify,
992// sell and distribute this software is granted provided this
993// copyright notice appears in all copies. This software is provided
994// "as is" without express or implied warranty, and with no claim as
995// to its suitability for any purpose.
996
997// See http://www.boost.org/libs/graph/doc/isomorphism-impl.pdf
998// for a description of the implementation of the isomorphism function
999// defined in this header file.
1000
1001#ifndef BOOST_GRAPH_ISOMORPHISM_HPP
1002#define BOOST_GRAPH_ISOMORPHISM_HPP
1003
1004#include <algorithm>
1005#include <boost/graph/detail/set_adaptor.hpp>
1006#include <boost/pending/indirect_cmp.hpp>
1007#include <boost/graph/detail/permutation.hpp>
1008#include <boost/graph/named_function_params.hpp>
1009#include <boost/graph/graph_concepts.hpp>
1010#include <boost/property_map/property_map.hpp>
1011#include <boost/pending/integer_range.hpp>
1012#include <boost/limits.hpp>
1013#include <boost/static_assert.hpp>
1014#include <boost/graph/depth_first_search.hpp>
1015
1016namespace boost {
1017
1018 @<Degree vertex invariant@>
1019
1020 namespace detail {
1021 @<Signature for the recursive isomorph function@>
1022 @<Body of the isomorph function@>
1023 } // namespace detail
1024
1025 @<Record DFS ordering visitor@>
1026 @<Compare multiplicity predicate@>
1027 @<Isomorph edge ordering predicate@>
1028
1029 @<Isomorphism Function Interface@>
1030 @<Isomorphism Function Body@>
1031
1032 namespace detail {
1033 // Should move this, make is public
1034 template <typename Graph, typename InDegreeMap, typename Cat>
1035 void compute_in_degree(const Graph& g, const InDegreeMap& in_degree_map,
1036 Cat)
1037 {
1038 typename graph_traits<Graph>::vertex_iterator vi, vi_end;
1039 typename graph_traits<Graph>::out_edge_iterator ei, ei_end;
1040 for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
1041 for (tie(ei, ei_end) = out_edges(*vi, g); ei != ei_end; ++ei) {
1042 typename graph_traits<Graph>::vertex_descriptor v = target(*ei, g);
1043 put(in_degree_map, v, get(in_degree_map, v) + 1);
1044 }
1045 }
1046 template <typename Graph, typename InDegreeMap>
1047 void compute_in_degree(const Graph& g, const InDegreeMap& in_degree_map,
1048 edge_list_graph_tag)
1049 {
1050 typename graph_traits<Graph>::edge_iterator ei, ei_end;
1051 for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) {
1052 typename graph_traits<Graph>::vertex_descriptor v = target(*ei, g);
1053 put(in_degree_map, v, get(in_degree_map, v) + 1);
1054 }
1055 }
1056 template <typename Graph, typename InDegreeMap>
1057 void compute_in_degree(const Graph& g, const InDegreeMap& in_degree_map)
1058 {
1059 typename graph_traits<Graph>::traversal_category cat;
1060 compute_in_degree(g, in_degree_map, cat);
1061 }
1062
1063
1064 template <typename Graph1, typename Graph2,
1065 typename IndexMapping, typename IndexMap1, typename IndexMap2,
1066 typename P, typename T, typename R>
1067 bool isomorphism_impl(const Graph1& g1, const Graph2& g2,
1068 IndexMapping f,
1069 IndexMap1 index_map1, IndexMap2 index_map2,
1070 const bgl_named_params<P,T,R>& params)
1071 {
1072 typedef typename graph_traits<Graph1>::vertices_size_type size_type;
1073
1074 // Compute the in-degrees
1075 std::vector<size_type> in_degree_vec1(num_vertices(g1), 0);
1076 typedef iterator_property_map<size_type*, IndexMap1,
1077 size_type, size_type&> InDegreeMap1;
1078 InDegreeMap1 in_degree_map1(&in_degree_vec1[0], index_map1);
1079 detail::compute_in_degree(g1, in_degree_map1);
1080 degree_vertex_invariant<InDegreeMap1, Graph1>
1081 default_invar1(in_degree_map1, g1);
1082
1083 std::vector<size_type> in_degree_vec2(num_vertices(g2), 0);
1084 typedef iterator_property_map<size_type*, IndexMap2,
1085 size_type, size_type&> InDegreeMap2;
1086 InDegreeMap2 in_degree_map2(&in_degree_vec2[0], index_map2);
1087 detail::compute_in_degree(g2, in_degree_map2);
1088 degree_vertex_invariant<InDegreeMap2, Graph2>
1089 default_invar2(in_degree_map2, g2);
1090
1091 return isomorphism(g1, g2, f,
1092 choose_param(get_param(params, vertex_invariant_t()), default_invar1),
1093 choose_param(get_param(params, vertex_invariant_t()), default_invar2),
1094 index_map1, index_map2);
1095 }
1096
1097 } // namespace detail
1098
1099 // Named parameter interface
1100 template <typename Graph1, typename Graph2, class P, class T, class R>
1101 bool isomorphism(const Graph1& g1,
1102 const Graph2& g2,
1103 const bgl_named_params<P,T,R>& params)
1104 {
1105 typedef typename graph_traits<Graph2>::vertex_descriptor vertex2_t;
1106 typename std::vector<vertex2_t>::size_type
1107 n = is_default_param(get_param(params, vertex_isomorphism_t()))
1108 ? num_vertices(g1) : 1;
1109 std::vector<vertex2_t> f(n);
1110 vertex2_t x;
1111 return detail::isomorphism_impl
1112 (g1, g2,
1113 choose_param(get_param(params, vertex_isomorphism_t()),
1114 make_iterator_property_map(f.begin(),
1115 choose_const_pmap(get_param(params, vertex_index1),
1116 g1, vertex_index), x)),
1117 choose_const_pmap(get_param(params, vertex_index1),
1118 g1, vertex_index),
1119 choose_const_pmap(get_param(params, vertex_index2),
1120 g2, vertex_index),
1121 params);
1122 }
1123
1124 // All defaults interface
1125 template <typename Graph1, typename Graph2>
1126 bool isomorphism(const Graph1& g1, const Graph2& g2)
1127 {
1128 typedef typename graph_traits<Graph1>::vertices_size_type size_type;
1129 typedef typename graph_traits<Graph2>::vertex_descriptor vertex2_t;
1130 std::vector<vertex2_t> f(num_vertices(g1));
1131
1132 // Compute the in-degrees
1133 std::vector<size_type> in_degree_vec1(num_vertices(g1), 0);
1134 typedef typename property_map<Graph1,vertex_index_t>::const_type IndexMap1;
1135 typedef iterator_property_map<size_type*, IndexMap1,
1136 size_type, size_type&> InDegreeMap1;
1137 InDegreeMap1 in_degree_map1(&in_degree_vec1[0], get(vertex_index, g1));
1138 detail::compute_in_degree(g1, in_degree_map1);
1139 degree_vertex_invariant<InDegreeMap1, Graph1>
1140 invariant1(in_degree_map, g1);
1141
1142 std::vector<size_type> in_degree_vec2(num_vertices(g2), 0);
1143 typedef typename property_map<Graph2,vertex_index_t>::const_type IndexMap2;
1144 typedef iterator_property_map<size_type*, IndexMap2,
1145 size_type, size_type&> InDegreeMap2;
1146 InDegreeMap2 in_degree_map2(&in_degree_vec2[0], get(vertex_index, g2));
1147 detail::compute_in_degree(g2, in_degree_map2);
1148 degree_vertex_invariant<InDegreeMap2, Graph2>
1149 invariant2(in_degree_map, g2);
1150
1151 return isomorphism
1152 (g1, g2, make_iterator_property_map(f.begin(), get(vertex_index, g1), vertex2_t()),
1153 invariant1, invariant2, get(vertex_index, g1), get(vertex_index, g2));
1154 }
1155
1156 // Verify that the given mapping iso_map from the vertices of g1 to the
1157 // vertices of g2 describes an isomorphism.
1158 // Note: this could be made much faster by specializing based on the graph
1159 // concepts modeled, but since we're verifying an O(n^(lg n)) algorithm,
1160 // O(n^4) won't hurt us.
1161 template<typename Graph1, typename Graph2, typename IsoMap>
1162 inline bool verify_isomorphism(const Graph1& g1, const Graph2& g2,
1163 IsoMap iso_map)
1164 {
1165 if (num_vertices(g1) != num_vertices(g2) || num_edges(g1) != num_edges(g2))
1166 return false;
1167
1168 for (typename graph_traits<Graph1>::edge_iterator e1 = edges(g1).first;
1169 e1 != edges(g1).second; ++e1) {
1170 bool found_edge = false;
1171 for (typename graph_traits<Graph2>::edge_iterator e2 = edges(g2).first;
1172 e2 != edges(g2).second && !found_edge; ++e2) {
1173 if (source(*e2, g2) == get(iso_map, source(*e1, g1)) &&
1174 target(*e2, g2) == get(iso_map, target(*e1, g1))) {
1175 found_edge = true;
1176 }
1177 }
1178
1179 if (!found_edge)
1180 return false;
1181 }
1182
1183 return true;
1184 }
1185
1186} // namespace boost
1187
1188#endif // BOOST_GRAPH_ISOMORPHISM_HPP
1189@}
1190
1191\bibliographystyle{abbrv}
1192\bibliography{ggcl}
1193
1194\end{document}
1195% LocalWords: Isomorphism Siek isomorphism adjacency subgraph subgraphs OM DFS
1196% LocalWords: ISOMORPH Invariants invariants typename IndexMapping bool const
1197% LocalWords: VertexInvariant VertexIndexMap iterator typedef VertexG Idx num
1198% LocalWords: InvarValue struct invar vec iter tmp_matches mult inserter permute ui
1199% LocalWords: dfs cmp isomorph VertexIter EdgeIter IndexMap desc RPH ATCH pre
1200
1201% LocalWords: iterators VertexListGraph EdgeListGraph BidirectionalGraph tmp
1202% LocalWords: ReadWritePropertyMap VertexListGraphConcept EdgeListGraphConcept
1203% LocalWords: BidirectionalGraphConcept ReadWritePropertyMapConcept indices ei
1204% LocalWords: IndexMappingValue ReadablePropertyMapConcept namespace InvarMap
1205% LocalWords: MultMap vip inline bitset typedefs fj hpp ifndef adaptor params
1206% LocalWords: bgl param pmap endif