[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