[Lemon-commits] Alpar Juttner: Documentation for VF2 (#597)

Lemon HG hg at lemon.cs.elte.hu
Fri May 15 12:10:01 CEST 2015


details:   http://lemon.cs.elte.hu/hg/lemon/rev/2f479109a71d
changeset: 1351:2f479109a71d
user:      Alpar Juttner <alpar [at] cs.elte.hu>
date:      Thu May 14 16:07:38 2015 +0200
description:
	Documentation for VF2 (#597)

	The implementation of this feature was sponsored by QuantumBio Inc.

diffstat:

 doc/groups.dox      |   29 +++++++
 doc/named-param.dox |    2 +-
 doc/references.bib  |   14 +++
 lemon/vf2.h         |  210 +++++++++++++++++++++++++++++++++++++++++++--------
 4 files changed, 219 insertions(+), 36 deletions(-)

diffs (truncated from 487 to 300 lines):

diff --git a/doc/groups.dox b/doc/groups.dox
--- a/doc/groups.dox
+++ b/doc/groups.dox
@@ -561,6 +561,35 @@
 */
 
 /**
+ at defgroup graph_isomorphism Graph Isomorphism
+ at ingroup algs
+\brief Algorithms for testing (sub)graph isomorphism
+
+This group contains algorithms for finding isomorph copies of a
+given graph in another one, or simply check whether two graphs are isomorphic.
+
+The formal definition of subgraph isomorphism is as follows.
+
+We are given two graphs, \f$G_1=(V_1,E_1)\f$ and \f$G_2=(V_2,E_2)\f$. A
+function \f$f:V_1\longrightarrow V_2\f$ is called \e mapping or \e
+embedding if \f$f(u)\neq f(v)\f$ whenever \f$u\neq v\f$.
+
+The standard <em>Subgraph Isomorphism Problem (SIP)</em> looks for a
+mapping with the property that whenever \f$(u,v)\in E_1\f$, then
+\f$(f(u),f(v))\in E_2\f$.
+
+In case of <em>Induced Subgraph Isomorphism Problem (ISIP)</em> one
+also requires that if \f$(u,v)\not\in E_1\f$, then \f$(f(u),f(v))\not\in
+E_2\f$
+
+In addition, the graph nodes may be \e labeled, i.e. we are given two
+node labelings \f$l_1:V_1\longrightarrow L\f$ and \f$l_2:V_2\longrightarrow
+L\f$ and we require that \f$l_1(u)=l_2(f(u))\f$ holds for all nodes \f$u \in
+G\f$.
+
+*/
+
+/**
 @defgroup planar Planar Embedding and Drawing
 @ingroup algs
 \brief Algorithms for planarity checking, embedding and drawing
diff --git a/doc/named-param.dox b/doc/named-param.dox
--- a/doc/named-param.dox
+++ b/doc/named-param.dox
@@ -25,7 +25,7 @@
 Several modern languages provide a convenient way to refer the
 function parameters by name also when you call the function. It is
 especially comfortable in case of a function having tons of parameters
-with natural default values. Sadly, C++ lack this amenity.
+with natural default values. Sadly, C++ lacks this amenity.
 
 However, with a crafty trick and with some little
 inconvenience, it is possible to emulate is.
diff --git a/doc/references.bib b/doc/references.bib
--- a/doc/references.bib
+++ b/doc/references.bib
@@ -354,3 +354,17 @@
   number =       6,
   pages =        {587--612}
 }
+
+ at article{cordella2004sub,
+  title =	 {A (sub) graph isomorphism algorithm for matching
+                  large graphs},
+  author =	 {Cordella, Luigi P and Foggia, Pasquale and Sansone,
+                  Carlo and Vento, Mario},
+  journal =	 {Pattern Analysis and Machine Intelligence, IEEE
+                  Transactions on},
+  volume =	 26,
+  number =	 10,
+  pages =	 {1367--1372},
+  year =	 2004,
+  publisher =	 {IEEE}
+}
diff --git a/lemon/vf2.h b/lemon/vf2.h
--- a/lemon/vf2.h
+++ b/lemon/vf2.h
@@ -18,6 +18,10 @@
 #ifndef LEMON_VF2_H
 #define LEMON_VF2_H
 
+///\ingroup graph_properties
+///\file
+///\brief VF2 algorithm \cite cordella2004sub.
+
 #include <lemon/core.h>
 #include <lemon/concepts/graph.h>
 #include <lemon/dfs.h>
@@ -90,25 +94,69 @@
     }
   }
 
-  enum MappingType {
+  ///Graph mapping types.
+
+  ///\ingroup graph_isomorphism
+  ///The \ref Vf2 "VF2" algorithm is capable of finding different kind of
+  ///embeddings, this enum specifies its type.
+  ///
+  ///See \ref graph_isomorphism for a more detailed description.
+  enum Vf2MappingType {
+    /// Subgraph isomorphism
     SUBGRAPH = 0,
+    /// Induced subgraph isomorphism
     INDUCED = 1,
+    /// Graph isomorphism
+
+    /// If the two graph has the same number of nodes, than it is
+    /// equivalent to \ref INDUCED, and if they also have the same
+    /// number of edges, then it is also equivalent to \ref SUBGRAPH.
+    ///
+    /// However, using this setting is faster than the other two
+    /// options.
     ISOMORPH = 2
   };
 
-  template<class G1, class G2, class I, class NEq = bits::vf2::AlwaysEq >
+  ///%VF2 algorithm class.
+
+  ///\ingroup graph_isomorphism This class provides an efficient
+  ///implementation of the %VF2 algorithm \cite cordella2004sub
+  ///for variants of the (Sub)graph Isomorphism problem.
+  ///
+  ///There is also a \ref vf2() "function-type interface" called \ref vf2()
+  ///for the %VF2 algorithm, which is probably more convenient in most
+  ///use-cases.
+  ///
+  ///\tparam G1 The type of the graph to be embedded.
+  ///The default type is \ref ListDigraph.
+  ///\tparam G2 The type of the graph g1 will be embedded into.
+  ///The default type is \ref ListDigraph.
+  ///\tparam M The type of the NodeMap storing the mapping.
+  ///By default, it is G1::NodeMap<G2::Node>
+  ///\tparam NEQ A bool-valued binary functor determinining whether a node is
+  ///mappable to another. By default it is an always true operator.
+  ///
+  ///\sa vf2()
+#ifdef DOXYGEN
+  template<class G1, class G2, class M, class NEQ >
+#else
+  template<class G1=ListDigraph,
+           class G2=ListDigraph,
+           class M = typename G1::template NodeMap<G2::Node>,
+           class NEQ = bits::vf2::AlwaysEq >
+#endif
   class Vf2
   {
     //Current depth in the DFS tree.
     int _depth;
     //Functor with bool operator()(G1::Node,G2::Node), which returns 1
     //if and only if the 2 nodes are equivalent.
-    NEq _nEq;
+    NEQ _nEq;
 
     typename G2::template NodeMap<int> _conn;
-    //Current matching. We index it by the nodes of g1, and match[v] is
+    //Current mapping. We index it by the nodes of g1, and match[v] is
     //a node of g2.
-    I &_match;
+    M &_mapping;
     //order[i] is the node of g1, for which we find a pair if depth=i
     std::vector<typename G1::Node> order;
     //currEdgeIts[i] is an edge iterator, witch is last used in the ith
@@ -121,10 +169,10 @@
     //lookup tables for cut the searchtree
     typename G1::template NodeMap<int> rNew1t,rInOut1t;
 
-    MappingType _mapping_type;
+    Vf2MappingType _mapping_type;
 
     //cut the search tree
-    template<MappingType MT>
+    template<Vf2MappingType MT>
     bool cut(const typename G1::Node n1,const typename G2::Node n2) const
     {
       int rNew2=0,rInOut2=0;
@@ -149,7 +197,7 @@
         }
     }
 
-    template<MappingType MT>
+    template<Vf2MappingType MT>
     bool feas(const typename G1::Node n1,const typename G2::Node n2)
     {
       if(!_nEq(n1,n2))
@@ -158,8 +206,8 @@
       for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1)
         {
           const typename G1::Node currNode=_g1.oppositeNode(n1,e1);
-          if(_match[currNode]!=INVALID)
-            --_conn[_match[currNode]];
+          if(_mapping[currNode]!=INVALID)
+            --_conn[_mapping[currNode]];
         }
       bool isIso=1;
       for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2)
@@ -177,7 +225,7 @@
       for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1)
         {
           const typename G1::Node currNode=_g1.oppositeNode(n1,e1);
-          if(_match[currNode]!=INVALID&&_conn[_match[currNode]]!=-1)
+          if(_mapping[currNode]!=INVALID&&_conn[_mapping[currNode]]!=-1)
             {
               switch(MT)
                 {
@@ -186,11 +234,11 @@
                   isIso=0;
                   break;
                 case SUBGRAPH:
-                  if(_conn[_match[currNode]]<-1)
+                  if(_conn[_mapping[currNode]]<-1)
                     isIso=0;
                   break;
                 }
-              _conn[_match[currNode]]=-1;
+              _conn[_mapping[currNode]]=-1;
             }
         }
       return isIso&&cut<MT>(n1,n2);
@@ -199,7 +247,7 @@
     void addPair(const typename G1::Node n1,const typename G2::Node n2)
     {
       _conn[n2]=-1;
-      _match.set(n1,n2);
+      _mapping.set(n1,n2);
       for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2)
         if(_conn[_g2.oppositeNode(n2,e2)]!=-1)
           ++_conn[_g2.oppositeNode(n2,e2)];
@@ -208,7 +256,7 @@
     void subPair(const typename G1::Node n1,const typename G2::Node n2)
     {
       _conn[n2]=0;
-      _match.set(n1,INVALID);
+      _mapping.set(n1,INVALID);
       for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2)
         {
           const typename G2::Node currNode=_g2.oppositeNode(n2,e2);
@@ -231,9 +279,7 @@
       bfs.run();
     }
 
-  public:
-
-    template<MappingType MT>
+    template<Vf2MappingType MT>
     bool extMatch()
     {
       while(_depth>=0)
@@ -250,14 +296,14 @@
           if(currEdgeIts[_depth]==INVALID)
             {
               typename G1::IncEdgeIt fstMatchedE(_g1,order[_depth]);
-              //if _match[order[_depth]]!=INVALID, we dont use
+              //if _mapping[order[_depth]]!=INVALID, we dont use
               //fstMatchedE
-              if(_match[order[_depth]]==INVALID)
+              if(_mapping[order[_depth]]==INVALID)
                 for(; fstMatchedE!=INVALID &&
-                      _match[_g1.oppositeNode(order[_depth],
+                      _mapping[_g1.oppositeNode(order[_depth],
                                               fstMatchedE)]==INVALID;
                     ++fstMatchedE) ; //find fstMatchedE
-              if(fstMatchedE==INVALID||_match[order[_depth]]!=INVALID)
+              if(fstMatchedE==INVALID||_mapping[order[_depth]]!=INVALID)
                 {
                   //We did not find an covered neighbour, this means
                   //the graph is not connected(or _depth==0).  Every
@@ -268,10 +314,10 @@
                   //try(match[order[_depth]]!=INVALID)
                   typename G2::NodeIt n2(_g2);
                   //if its not the first try
-                  if(_match[order[_depth]]!=INVALID)
+                  if(_mapping[order[_depth]]!=INVALID)
                     {
-                      n2=++typename G2::NodeIt(_g2,_match[order[_depth]]);
-                      subPair(order[_depth],_match[order[_depth]]);
+                      n2=++typename G2::NodeIt(_g2,_mapping[order[_depth]]);
+                      subPair(order[_depth],_mapping[order[_depth]]);
                     }
                   for(; n2!=INVALID; ++n2)
                     if(MT!=SUBGRAPH&&_conn[n2]==0)
@@ -294,15 +340,16 @@
                 }
               else
                 {
-                  currPNode=_match[_g1.oppositeNode(order[_depth],fstMatchedE)];
+                  currPNode=_mapping[_g1.oppositeNode(order[_depth],
+                                                      fstMatchedE)];
                   currEdgeIts[_depth]=typename G2::IncEdgeIt(_g2,currPNode);
                 }
             }
           else
             {
-              currPNode=_g2.oppositeNode(_match[order[_depth]],
+              currPNode=_g2.oppositeNode(_mapping[order[_depth]],
                                          currEdgeIts[_depth]);
-              subPair(order[_depth],_match[order[_depth]]);
+              subPair(order[_depth],_mapping[order[_depth]]);
               ++currEdgeIts[_depth];
             }
           for(; currEdgeIts[_depth]!=INVALID; ++currEdgeIts[_depth])
@@ -345,8 +392,18 @@


More information about the Lemon-commits mailing list