Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/libs/graph/doc/isomorphism-impl.w @ 12

Last change on this file since 12 was 12, checked in by landauf, 17 years ago

added boost

File size: 44.4 KB
Line 
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 there
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
304function_requires< VertexListGraphConcept<Graph1> >();
305function_requires< EdgeListGraphConcept<Graph1> >();
306function_requires< VertexListGraphConcept<Graph2> >();
307function_requires< BidirectionalGraphConcept<Graph2> >();
308
309// Property map requirements
310function_requires< ReadWritePropertyMapConcept<IndexMapping, vertex1_t> >();
311typedef typename property_traits<IndexMapping>::value_type IndexMappingValue;
312BOOST_STATIC_ASSERT((is_same<IndexMappingValue, vertex2_t>::value));
313
314function_requires< ReadablePropertyMapConcept<IndexMap1, vertex1_t> >();
315typedef typename property_traits<IndexMap1>::value_type IndexMap1Value;
316BOOST_STATIC_ASSERT((is_convertible<IndexMap1Value, size_type>::value));
317
318function_requires< 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.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
Note: See TracBrowser for help on using the repository browser.