[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