1 /* -*- mode: C++; indent-tabs-mode: nil; -*-
3 * This file is a part of LEMON, a generic C++ optimization library.
5 * Copyright (C) 2015-2017
6 * EMAXA Kutato-fejleszto Kft. (EMAXA Research Ltd.)
8 * Permission to use, modify and distribute this software is granted
9 * provided that this copyright notice appears in all copies. For
10 * precise terms see the accompanying LICENSE file.
12 * This software is provided "AS IS" with no warranty of any kind,
13 * express or implied, and with no claim as to its suitability for any
21 ///\ingroup graph_properties
23 ///\brief VF2 algorithm \cite cordella2004sub.
25 #include <lemon/core.h>
26 #include <lemon/concepts/graph.h>
27 #include <lemon/dfs.h>
28 #include <lemon/bfs.h>
29 #include <lemon/bits/vf2_internals.h>
39 template<class T1, class T2>
40 bool operator()(T1&, T2&) const {
45 template<class M1, class M2>
50 MapEq(const M1 &m1, const M2 &m2) : _m1(m1), _m2(m2) { }
51 bool operator()(typename M1::Key k1, typename M2::Key k2) const {
52 return _m1[k1] == _m2[k2];
59 class DfsLeaveOrder : public DfsVisitor<G> {
61 std::vector<typename G::Node> &_order;
64 DfsLeaveOrder(const G &g, std::vector<typename G::Node> &order)
65 : i(countNodes(g)), _g(g), _order(order) { }
66 void leave(const typename G::Node &node) {
72 class BfsLeaveOrder : public BfsVisitor<G> {
75 std::vector<typename G::Node> &_order;
77 BfsLeaveOrder(const G &g, std::vector<typename G::Node> &order)
78 : i(0), _g(g), _order(order){
80 void process(const typename G::Node &node) {
88 ///%VF2 algorithm class.
90 ///\ingroup graph_isomorphism This class provides an efficient
91 ///implementation of the %VF2 algorithm \cite cordella2004sub
92 ///for variants of the (Sub)graph Isomorphism problem.
94 ///There is also a \ref vf2() "function-type interface" called \ref vf2()
95 ///for the %VF2 algorithm, which is probably more convenient in most
98 ///\tparam G1 The type of the graph to be embedded.
99 ///The default type is \ref ListDigraph.
100 ///\tparam G2 The type of the graph g1 will be embedded into.
101 ///The default type is \ref ListDigraph.
102 ///\tparam M The type of the NodeMap storing the mapping.
103 ///By default, it is G1::NodeMap<G2::Node>
104 ///\tparam NEQ A bool-valued binary functor determinining whether a node is
105 ///mappable to another. By default it is an always true operator.
109 template<class G1, class G2, class M, class NEQ >
111 template<class G1=ListDigraph,
112 class G2=ListDigraph,
113 class M = typename G1::template NodeMap<G2::Node>,
114 class NEQ = bits::vf2::AlwaysEq >
117 //Current depth in the DFS tree.
119 //Functor with bool operator()(G1::Node,G2::Node), which returns 1
120 //ifff the two nodes are equivalent.
123 typename G2::template NodeMap<int> _conn;
124 //Current mapping. We index it by the nodes of g1, and match[v] is
127 //order[i] is the node of g1, for which we search a pair if depth=i
128 std::vector<typename G1::Node> order;
129 //currEdgeIts[i] is an edge iterator, witch is last used in the ith
130 //depth to find a pair for order[i].
131 std::vector<typename G2::IncEdgeIt> currEdgeIts;
136 //lookup tables for cutting the searchtree
137 typename G1::template NodeMap<int> rNew1t,rInOut1t;
139 MappingType _mapping_type;
141 bool _deallocMappingAfterUse;
143 //cut the search tree
144 template<MappingType MT>
145 bool cut(const typename G1::Node n1,const typename G2::Node n2) const {
146 int rNew2=0,rInOut2=0;
147 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) {
148 const typename G2::Node currNode=_g2.oppositeNode(n2,e2);
149 if(_conn[currNode]>0)
151 else if(MT!=SUBGRAPH&&_conn[currNode]==0)
156 return rInOut1t[n1]<=rInOut2&&rNew1t[n1]<=rNew2;
158 return rInOut1t[n1]<=rInOut2;
160 return rInOut1t[n1]==rInOut2&&rNew1t[n1]==rNew2;
166 template<MappingType MT>
167 bool feas(const typename G1::Node n1,const typename G2::Node n2) {
171 for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1) {
172 const typename G1::Node& currNode=_g1.oppositeNode(n1,e1);
173 if(_mapping[currNode]!=INVALID)
174 --_conn[_mapping[currNode]];
177 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) {
178 int& connCurrNode = _conn[_g2.oppositeNode(n2,e2)];
181 else if(MT!=SUBGRAPH&&connCurrNode==-1) {
187 for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1) {
188 const typename G2::Node& currNodePair=_mapping[_g1.oppositeNode(n1,e1)];
189 int& connCurrNodePair=_conn[currNodePair];
190 if(currNodePair!=INVALID&&connCurrNodePair!=-1) {
197 if(connCurrNodePair<-1)
204 return isIso&&cut<MT>(n1,n2);
207 void addPair(const typename G1::Node n1,const typename G2::Node n2) {
210 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) {
211 int& currConn = _conn[_g2.oppositeNode(n2,e2)];
217 void subPair(const typename G1::Node n1,const typename G2::Node n2) {
219 _mapping.set(n1,INVALID);
220 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) {
221 int& currConn = _conn[_g2.oppositeNode(n2,e2)];
224 else if(currConn==-1)
230 // we will find pairs for the nodes of g1 in this order
232 // bits::vf2::DfsLeaveOrder<G1> v(_g1,order);
233 // DfsVisit<G1,bits::vf2::DfsLeaveOrder<G1> >dfs(_g1, v);
236 //it is more efficient in practice than DFS
237 bits::vf2::BfsLeaveOrder<G1> v(_g1,order);
238 BfsVisit<G1,bits::vf2::BfsLeaveOrder<G1> >bfs(_g1, v);
242 template<MappingType MT>
245 //there are not nodes in g1, which has not pair in g2.
246 if(_depth==static_cast<int>(order.size())) {
250 typename G1::Node& nodeOfDepth = order[_depth];
251 const typename G2::Node& pairOfNodeOfDepth = _mapping[nodeOfDepth];
252 typename G2::IncEdgeIt &edgeItOfDepth = currEdgeIts[_depth];
253 //the node of g2, which neighbours are the candidates for
254 //the pair of nodeOfDepth
255 typename G2::Node currPNode;
256 if(edgeItOfDepth==INVALID) {
257 typename G1::IncEdgeIt fstMatchedE(_g1,nodeOfDepth);
258 //if pairOfNodeOfDepth!=INVALID, we dont use
260 if(pairOfNodeOfDepth==INVALID)
261 for(; fstMatchedE!=INVALID &&
262 _mapping[_g1.oppositeNode(nodeOfDepth,
263 fstMatchedE)]==INVALID;
264 ++fstMatchedE) ; //find fstMatchedE
265 if(fstMatchedE==INVALID||pairOfNodeOfDepth!=INVALID) {
266 //We found no covered neighbours, this means
267 //the graph is not connected(or _depth==0). Each
268 //uncovered(and there are some other properties due
269 //to the spec. problem types) node of g2 is
270 //candidate. We can read the iterator of the last
271 //tried node from the match if it is not the first
272 //try(match[nodeOfDepth]!=INVALID)
273 typename G2::NodeIt n2(_g2);
274 //if it's not the first try
275 if(pairOfNodeOfDepth!=INVALID) {
276 n2=++typename G2::NodeIt(_g2,pairOfNodeOfDepth);
277 subPair(nodeOfDepth,pairOfNodeOfDepth);
279 for(; n2!=INVALID; ++n2)
281 if(_conn[n2]==0&&feas<MT>(nodeOfDepth,n2))
284 else if(_conn[n2]>=0&&feas<MT>(nodeOfDepth,n2))
286 // n2 is the next candidate
288 addPair(nodeOfDepth,n2);
291 else // there are no more candidates
296 currPNode=_mapping[_g1.oppositeNode(nodeOfDepth,
298 edgeItOfDepth=typename G2::IncEdgeIt(_g2,currPNode);
302 currPNode=_g2.oppositeNode(pairOfNodeOfDepth,
304 subPair(nodeOfDepth,pairOfNodeOfDepth);
307 for(; edgeItOfDepth!=INVALID; ++edgeItOfDepth) {
308 const typename G2::Node currNode =
309 _g2.oppositeNode(currPNode, edgeItOfDepth);
310 if(_conn[currNode]>0&&feas<MT>(nodeOfDepth,currNode)) {
311 addPair(nodeOfDepth,currNode);
315 edgeItOfDepth==INVALID?--_depth:++_depth;
320 //calc. the lookup table for cut the searchtree
321 void setRNew1tRInOut1t() {
322 typename G1::template NodeMap<int> tmp(_g1,0);
323 for(unsigned int i=0; i<order.size(); ++i) {
324 const typename G1::Node& orderI = order[i];
326 for(typename G1::IncEdgeIt e1(_g1,orderI); e1!=INVALID; ++e1) {
327 const typename G1::Node currNode=_g1.oppositeNode(orderI,e1);
330 else if(tmp[currNode]==0)
333 for(typename G1::IncEdgeIt e1(_g1,orderI); e1!=INVALID; ++e1) {
334 const typename G1::Node currNode=_g1.oppositeNode(orderI,e1);
335 if(tmp[currNode]!=-1)
345 ///\param g1 The graph to be embedded into \e g2.
346 ///\param g2 The graph \e g1 will be embedded into.
347 ///\param m \ref concepts::ReadWriteMap "read-write" NodeMap
348 ///storing the found mapping.
349 ///\param neq A bool-valued binary functor determining whether a node is
350 ///mappable to another. By default it is an always true operator.
351 Vf2(const G1 &g1, const G2 &g2, M &m, const NEQ &neq = NEQ() ) :
352 _nEq(neq), _conn(g2,0), _mapping(m), order(countNodes(g1)),
353 currEdgeIts(countNodes(g1),INVALID), _g1(g1), _g2(g2), rNew1t(g1,0),
354 rInOut1t(g1,0), _mapping_type(SUBGRAPH), _deallocMappingAfterUse(0) {
358 for(typename G1::NodeIt n(g1);n!=INVALID;++n)
368 if(_deallocMappingAfterUse)
372 ///Returns the current mapping type
374 ///Returns the current mapping type
376 MappingType mappingType() const {
377 return _mapping_type;
381 ///Sets mapping type.
383 ///The mapping type is set to \ref SUBGRAPH by default.
385 ///\sa See \ref MappingType for the possible values.
386 void mappingType(MappingType m_type) {
387 _mapping_type = m_type;
392 ///It finds a mapping from g1 into g2 according to the mapping
393 ///type set by \ref mappingType(MappingType) "mappingType()".
395 ///By subsequent calls, it returns all possible mappings one-by-one.
397 ///\retval true if a mapping is found.
398 ///\retval false if there is no (more) mapping.
400 switch(_mapping_type) {
402 return extMatch<SUBGRAPH>();
404 return extMatch<INDUCED>();
406 return extMatch<ISOMORPH>();
413 template<class G1, class G2>
414 class Vf2WizardBase {
422 MappingType _mapping_type;
424 typedef typename G1::template NodeMap<typename G2::Node> Mapping;
427 void createMapping() {
428 _mapping = new Mapping(_g1);
431 void *myVf2; //used in Vf2Wizard::find
434 typedef bits::vf2::AlwaysEq NodeEq;
437 Vf2WizardBase(const G1 &g1,const G2 &g2)
438 : _g1(g1), _g2(g2), _mapping_type(SUBGRAPH), _local_mapping(true) { }
442 /// Auxiliary class for the function-type interface of %VF2 algorithm.
444 /// This auxiliary class implements the named parameters of
445 /// \ref vf2() "function-type interface" of \ref Vf2 algorithm.
447 /// \warning This class should only be used through the function \ref vf2().
449 /// \tparam TR The traits class that defines various types used by the
452 class Vf2Wizard : public TR {
454 typedef typename TR::Graph1 Graph1;
455 typedef typename TR::Graph2 Graph2;
457 typedef typename TR::Mapping Mapping;
458 typedef typename TR::NodeEq NodeEq;
462 using TR::_mapping_type;
468 Vf2Wizard(const Graph1 &g1,const Graph2 &g2) : Base(g1,g2) {
472 Vf2Wizard(const Base &b) : Base(b) { }
475 Vf2Wizard(const Vf2Wizard &b) : Base(b) {}
479 struct SetMappingBase : public Base{
481 SetMappingBase(const Base &b) : Base(b) {}
484 ///\brief \ref named-templ-param "Named parameter" for setting
487 ///\ref named-templ-param "Named parameter" function for setting
488 ///the map that stores the found embedding.
490 Vf2Wizard< SetMappingBase<T> > mapping(const T &t) {
491 Base::_mapping=reinterpret_cast<void*>(const_cast<T*>(&t));
492 Base::_local_mapping = false;
493 return Vf2Wizard<SetMappingBase<T> >(*this);
497 struct SetNodeEqBase : public Base {
500 SetNodeEqBase(const Base &b, const NE &node_eq)
501 : Base(b), _node_eq(node_eq){
505 ///\brief \ref named-templ-param "Named parameter" for setting
506 ///the node equivalence relation.
508 ///\ref named-templ-param "Named parameter" function for setting
509 ///the equivalence relation between the nodes.
511 ///\param node_eq A bool-valued binary functor determinining
512 ///whether a node is mappable to another. By default it is an
513 ///always true operator.
515 Vf2Wizard< SetNodeEqBase<T> > nodeEq(const T &node_eq) {
516 return Vf2Wizard<SetNodeEqBase<T> >(SetNodeEqBase<T>(*this,node_eq));
519 ///\brief \ref named-templ-param "Named parameter" for setting
522 ///\ref named-templ-param "Named parameter" function for setting
523 ///the node labels defining equivalence relation between them.
525 ///\param m1 An arbitrary \ref concepts::ReadMap "readable node map"
527 ///\param m2 An arbitrary \ref concepts::ReadMap "readable node map"
530 ///The value type of these maps must be equal comparable.
531 template<class M1, class M2>
532 Vf2Wizard< SetNodeEqBase<bits::vf2::MapEq<M1,M2> > >
533 nodeLabels(const M1 &m1,const M2 &m2){
534 return nodeEq(bits::vf2::MapEq<M1,M2>(m1,m2));
537 ///\brief \ref named-templ-param "Named parameter" for setting
540 ///\ref named-templ-param "Named parameter" for setting
543 ///The mapping type is set to \ref SUBGRAPH by default.
545 ///\sa See \ref MappingType for the possible values.
546 Vf2Wizard<Base> &mappingType(MappingType m_type) {
547 _mapping_type = m_type;
551 ///\brief \ref named-templ-param "Named parameter" for setting
552 ///the mapping type to \ref INDUCED.
554 ///\ref named-templ-param "Named parameter" for setting
555 ///the mapping type to \ref INDUCED.
556 Vf2Wizard<Base> &induced() {
557 _mapping_type = INDUCED;
561 ///\brief \ref named-templ-param "Named parameter" for setting
562 ///the mapping type to \ref ISOMORPH.
564 ///\ref named-templ-param "Named parameter" for setting
565 ///the mapping type to \ref ISOMORPH.
566 Vf2Wizard<Base> &iso() {
567 _mapping_type = ISOMORPH;
572 ///Runs VF2 algorithm.
574 ///This method runs VF2 algorithm.
576 ///\retval true if a mapping is found.
577 ///\retval false if there is no mapping.
579 if(Base::_local_mapping)
580 Base::createMapping();
582 Vf2<Graph1, Graph2, Mapping, NodeEq >
583 alg(_g1, _g2, *reinterpret_cast<Mapping*>(_mapping), _node_eq);
585 alg.mappingType(_mapping_type);
587 bool ret = alg.find();
589 if(Base::_local_mapping)
590 delete reinterpret_cast<Mapping*>(_mapping);
595 ///Get a pointer to the generated Vf2 object.
597 ///Gives a pointer to the generated Vf2 object.
599 ///\return Pointer to the generated Vf2 object.
600 ///\warning Don't forget to delete the referred Vf2 object after use.
601 Vf2<Graph1, Graph2, Mapping, NodeEq >* getPtrToVf2Object() {
602 if(Base::_local_mapping)
603 Base::createMapping();
604 Vf2<Graph1, Graph2, Mapping, NodeEq >* ptr =
605 new Vf2<Graph1, Graph2, Mapping, NodeEq>
606 (_g1, _g2, *reinterpret_cast<Mapping*>(_mapping), _node_eq);
607 ptr->mappingType(_mapping_type);
608 if(Base::_local_mapping)
609 ptr->_deallocMappingAfterUse = true;
613 ///Counts the number of mappings.
615 ///This method counts the number of mappings.
617 /// \return The number of mappings.
619 if(Base::_local_mapping)
620 Base::createMapping();
622 Vf2<Graph1, Graph2, Mapping, NodeEq>
623 alg(_g1, _g2, *reinterpret_cast<Mapping*>(_mapping), _node_eq);
624 if(Base::_local_mapping)
625 alg._deallocMappingAfterUse = true;
626 alg.mappingType(_mapping_type);
636 ///Function-type interface for VF2 algorithm.
638 /// \ingroup graph_isomorphism
639 ///Function-type interface for VF2 algorithm \cite cordella2004sub.
641 ///This function has several \ref named-func-param "named parameters"
642 ///declared as the members of class \ref Vf2Wizard.
643 ///The following examples show how to use these parameters.
645 /// // Find an embedding of graph g1 into graph g2
646 /// ListGraph::NodeMap<ListGraph::Node> m(g);
647 /// vf2(g1,g2).mapping(m).run();
649 /// // Check whether graphs g1 and g2 are isomorphic
650 /// bool is_iso = vf2(g1,g2).iso().run();
652 /// // Count the number of isomorphisms
653 /// int num_isos = vf2(g1,g2).iso().count();
655 /// // Iterate through all the induced subgraph mappings of graph g1 into g2
656 /// auto* myVf2 = vf2(g1,g2).mapping(m).nodeLabels(c1,c2)
657 /// .induced().getPtrToVf2Object();
658 /// while(myVf2->find()){
659 /// //process the current mapping m
663 ///\warning Don't forget to put the \ref Vf2Wizard::run() "run()",
664 ///\ref Vf2Wizard::count() "count()" or
665 ///the \ref Vf2Wizard::getPtrToVf2Object() "getPtrToVf2Object()"
666 ///to the end of the expression.
669 template<class G1, class G2>
670 Vf2Wizard<Vf2WizardBase<G1,G2> > vf2(const G1 &g1, const G2 &g2) {
671 return Vf2Wizard<Vf2WizardBase<G1,G2> >(g1,g2);