[Lemon-commits] Peter Madarasi: VF2 algorithm added (#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/a037254714b3
changeset: 1350:a037254714b3
user:      Peter Madarasi <madarasip [at] caesar.elte.hu>
date:      Mon Mar 30 17:42:30 2015 +0200
description:
	VF2 algorithm added (#597)

	The implementation of this feature was sponsored by QuantumBio Inc.

diffstat:

 lemon/vf2.h         |  523 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 test/CMakeLists.txt |    1 +
 test/vf2_test.cc    |  361 +++++++++++++++++++++++++++++++++++
 3 files changed, 885 insertions(+), 0 deletions(-)

diffs (truncated from 905 to 300 lines):

diff --git a/lemon/vf2.h b/lemon/vf2.h
new file mode 100644
--- /dev/null
+++ b/lemon/vf2.h
@@ -0,0 +1,523 @@
+/* -*- mode: C++; indent-tabs-mode: nil; -*-
+ *
+ * This file is a part of LEMON, a generic C++ optimization library.
+ *
+ * Copyright (C) 2015
+ * EMAXA Kutato-fejleszto Kft. (EMAXA Research Ltd.)
+ *
+ * Permission to use, modify and distribute this software is granted
+ * provided that this copyright notice appears in all copies. For
+ * precise terms see the accompanying LICENSE file.
+ *
+ * This software is provided "AS IS" with no warranty of any kind,
+ * express or implied, and with no claim as to its suitability for any
+ * purpose.
+ *
+ */
+
+#ifndef LEMON_VF2_H
+#define LEMON_VF2_H
+
+#include <lemon/core.h>
+#include <lemon/concepts/graph.h>
+#include <lemon/dfs.h>
+#include <lemon/bfs.h>
+#include <test/test_tools.h>
+
+#include <vector>
+#include <set>
+
+namespace lemon
+{
+  namespace bits
+  {
+    namespace vf2
+    {
+      class AlwaysEq
+      {
+      public:
+        template<class T1, class T2>
+        bool operator()(T1, T2) const
+        {
+          return true;
+        }
+      };
+
+      template<class M1, class M2>
+      class MapEq
+      {
+        const M1 &_m1;
+        const M2 &_m2;
+      public:
+        MapEq(const M1 &m1, const M2 &m2) : _m1(m1), _m2(m2) {}
+        bool operator()(typename M1::Key k1, typename M2::Key k2) const
+        {
+          return _m1[k1] == _m2[k2];
+        }
+      };
+
+      template <class G>
+      class DfsLeaveOrder : public DfsVisitor<G>
+      {
+        const G &_g;
+        std::vector<typename G::Node> &_order;
+        int i;
+      public:
+        DfsLeaveOrder(const G &g, std::vector<typename G::Node> &order)
+          : i(countNodes(g)), _g(g), _order(order)
+        {}
+        void leave(const typename G::Node &node)
+        {
+          _order[--i]=node;
+        }
+      };
+
+      template <class G>
+      class BfsLeaveOrder : public BfsVisitor<G>
+      {
+        int i;
+        const G &_g;
+        std::vector<typename G::Node> &_order;
+      public:
+        BfsLeaveOrder(const G &g, std::vector<typename G::Node> &order)
+          : i(0), _g(g), _order(order)
+        {}
+        void process(const typename G::Node &node)
+        {
+          _order[i++]=node;
+        }
+      };
+    }
+  }
+
+  enum MappingType {
+    SUBGRAPH = 0,
+    INDUCED = 1,
+    ISOMORPH = 2
+  };
+
+  template<class G1, class G2, class I, class NEq = bits::vf2::AlwaysEq >
+  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;
+
+    typename G2::template NodeMap<int> _conn;
+    //Current matching. We index it by the nodes of g1, and match[v] is
+    //a node of g2.
+    I &_match;
+    //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
+    //depth to find a pair for order[i].
+    std::vector<typename G2::IncEdgeIt> currEdgeIts;
+    //The small graph.
+    const G1 &_g1;
+    //The big graph.
+    const G2 &_g2;
+    //lookup tables for cut the searchtree
+    typename G1::template NodeMap<int> rNew1t,rInOut1t;
+
+    MappingType _mapping_type;
+
+    //cut the search tree
+    template<MappingType MT>
+    bool cut(const typename G1::Node n1,const typename G2::Node n2) const
+    {
+      int rNew2=0,rInOut2=0;
+      for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2)
+        {
+          const typename G2::Node currNode=_g2.oppositeNode(n2,e2);
+          if(_conn[currNode]>0)
+            ++rInOut2;
+          else if(MT!=SUBGRAPH&&_conn[currNode]==0)
+            ++rNew2;
+        }
+      switch(MT)
+        {
+        case INDUCED:
+          return rInOut1t[n1]<=rInOut2&&rNew1t[n1]<=rNew2;
+        case SUBGRAPH:
+          return rInOut1t[n1]<=rInOut2;
+        case ISOMORPH:
+          return rInOut1t[n1]==rInOut2&&rNew1t[n1]==rNew2;
+        default:
+          return false;
+        }
+    }
+
+    template<MappingType MT>
+    bool feas(const typename G1::Node n1,const typename G2::Node n2)
+    {
+      if(!_nEq(n1,n2))
+        return 0;
+
+      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]];
+        }
+      bool isIso=1;
+      for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2)
+        {
+          const typename G2::Node currNode=_g2.oppositeNode(n2,e2);
+          if(_conn[currNode]<-1)
+            ++_conn[currNode];
+          else if(MT!=SUBGRAPH&&_conn[currNode]==-1)
+            {
+              isIso=0;
+              break;
+            }
+        }
+
+      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)
+            {
+              switch(MT)
+                {
+                case INDUCED:
+                case ISOMORPH:
+                  isIso=0;
+                  break;
+                case SUBGRAPH:
+                  if(_conn[_match[currNode]]<-1)
+                    isIso=0;
+                  break;
+                }
+              _conn[_match[currNode]]=-1;
+            }
+        }
+      return isIso&&cut<MT>(n1,n2);
+    }
+
+    void addPair(const typename G1::Node n1,const typename G2::Node n2)
+    {
+      _conn[n2]=-1;
+      _match.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)];
+    }
+
+    void subPair(const typename G1::Node n1,const typename G2::Node n2)
+    {
+      _conn[n2]=0;
+      _match.set(n1,INVALID);
+      for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2)
+        {
+          const typename G2::Node currNode=_g2.oppositeNode(n2,e2);
+          if(_conn[currNode]>0)
+            --_conn[currNode];
+          else if(_conn[currNode]==-1)
+            ++_conn[n2];
+        }
+    }
+
+    void setOrder()//we will find pairs for the nodes of g1 in this order
+    {
+      // bits::vf2::DfsLeaveOrder<G1> v(_g1,order);
+      //   DfsVisit<G1,bits::vf2::DfsLeaveOrder<G1> >dfs(_g1, v);
+      //   dfs.run();
+
+      //it is more efficient in practice than DFS
+      bits::vf2::BfsLeaveOrder<G1> v(_g1,order);
+      BfsVisit<G1,bits::vf2::BfsLeaveOrder<G1> >bfs(_g1, v);
+      bfs.run();
+    }
+
+  public:
+
+    template<MappingType MT>
+    bool extMatch()
+    {
+      while(_depth>=0)
+        {
+          //there are not nodes in g1, which has not pair in g2.
+          if(_depth==static_cast<int>(order.size()))
+            {
+              --_depth;
+              return true;
+            }
+          //the node of g2, which neighbours are the candidates for
+          //the pair of order[_depth]
+          typename G2::Node currPNode;
+          if(currEdgeIts[_depth]==INVALID)
+            {
+              typename G1::IncEdgeIt fstMatchedE(_g1,order[_depth]);
+              //if _match[order[_depth]]!=INVALID, we dont use
+              //fstMatchedE
+              if(_match[order[_depth]]==INVALID)
+                for(; fstMatchedE!=INVALID &&
+                      _match[_g1.oppositeNode(order[_depth],
+                                              fstMatchedE)]==INVALID;
+                    ++fstMatchedE) ; //find fstMatchedE
+              if(fstMatchedE==INVALID||_match[order[_depth]]!=INVALID)
+                {
+                  //We did not find an covered neighbour, this means
+                  //the graph is not connected(or _depth==0).  Every
+                  //uncovered(and there are some other properties due
+                  //to the spec. problem types) node of g2 is
+                  //candidate.  We can read the iterator of the last
+                  //tryed node from the match if it is not the first
+                  //try(match[order[_depth]]!=INVALID)
+                  typename G2::NodeIt n2(_g2);
+                  //if its not the first try
+                  if(_match[order[_depth]]!=INVALID)
+                    {
+                      n2=++typename G2::NodeIt(_g2,_match[order[_depth]]);
+                      subPair(order[_depth],_match[order[_depth]]);
+                    }
+                  for(; n2!=INVALID; ++n2)
+                    if(MT!=SUBGRAPH&&_conn[n2]==0)
+                      {
+                        if(feas<MT>(order[_depth],n2))
+                          break;
+                      }
+                    else if(MT==SUBGRAPH&&_conn[n2]>=0)
+                      if(feas<MT>(order[_depth],n2))
+                        break;
+                  // n2 is the next candidate
+                  if(n2!=INVALID)
+                    {
+                      addPair(order[_depth],n2);
+                      ++_depth;
+                    }
+                  else // there is no more candidate
+                    --_depth;
+                  continue;
+                }
+              else


More information about the Lemon-commits mailing list