madarasip@1350: /* -*- mode: C++; indent-tabs-mode: nil; -*- madarasip@1350: * madarasip@1350: * This file is a part of LEMON, a generic C++ optimization library. madarasip@1350: * madarasip@1405: * Copyright (C) 2015-2017 madarasip@1350: * EMAXA Kutato-fejleszto Kft. (EMAXA Research Ltd.) madarasip@1350: * madarasip@1350: * Permission to use, modify and distribute this software is granted madarasip@1350: * provided that this copyright notice appears in all copies. For madarasip@1350: * precise terms see the accompanying LICENSE file. madarasip@1350: * madarasip@1350: * This software is provided "AS IS" with no warranty of any kind, madarasip@1350: * express or implied, and with no claim as to its suitability for any madarasip@1350: * purpose. madarasip@1350: * madarasip@1350: */ madarasip@1350: madarasip@1350: #ifndef LEMON_VF2_H madarasip@1350: #define LEMON_VF2_H madarasip@1350: alpar@1351: ///\ingroup graph_properties alpar@1351: ///\file alpar@1351: ///\brief VF2 algorithm \cite cordella2004sub. alpar@1351: madarasip@1350: #include madarasip@1350: #include madarasip@1350: #include madarasip@1350: #include madarasip@1405: #include madarasip@1350: madarasip@1350: #include madarasip@1350: madarasip@1405: namespace lemon { madarasip@1405: namespace bits { madarasip@1405: namespace vf2 { madarasip@1405: madarasip@1405: class AlwaysEq { madarasip@1350: public: madarasip@1350: template madarasip@1405: bool operator()(T1&, T2&) const { madarasip@1350: return true; madarasip@1350: } madarasip@1350: }; madarasip@1350: madarasip@1350: template madarasip@1405: class MapEq{ madarasip@1350: const M1 &_m1; madarasip@1350: const M2 &_m2; madarasip@1350: public: madarasip@1405: MapEq(const M1 &m1, const M2 &m2) : _m1(m1), _m2(m2) { } madarasip@1405: bool operator()(typename M1::Key k1, typename M2::Key k2) const { madarasip@1350: return _m1[k1] == _m2[k2]; madarasip@1350: } madarasip@1350: }; madarasip@1350: madarasip@1350: template madarasip@1405: class DfsLeaveOrder : public DfsVisitor { madarasip@1350: const G &_g; madarasip@1350: std::vector &_order; madarasip@1350: int i; madarasip@1350: public: madarasip@1350: DfsLeaveOrder(const G &g, std::vector &order) madarasip@1405: : i(countNodes(g)), _g(g), _order(order) { } madarasip@1405: void leave(const typename G::Node &node) { madarasip@1350: _order[--i]=node; madarasip@1350: } madarasip@1350: }; madarasip@1350: madarasip@1350: template madarasip@1405: class BfsLeaveOrder : public BfsVisitor { madarasip@1350: int i; madarasip@1350: const G &_g; madarasip@1350: std::vector &_order; madarasip@1350: public: madarasip@1350: BfsLeaveOrder(const G &g, std::vector &order) madarasip@1405: : i(0), _g(g), _order(order){ madarasip@1405: } madarasip@1405: void process(const typename G::Node &node) { madarasip@1350: _order[i++]=node; madarasip@1350: } madarasip@1350: }; madarasip@1350: } madarasip@1350: } madarasip@1350: madarasip@1350: alpar@1351: ///%VF2 algorithm class. alpar@1351: alpar@1351: ///\ingroup graph_isomorphism This class provides an efficient alpar@1351: ///implementation of the %VF2 algorithm \cite cordella2004sub alpar@1351: ///for variants of the (Sub)graph Isomorphism problem. alpar@1351: /// alpar@1351: ///There is also a \ref vf2() "function-type interface" called \ref vf2() alpar@1351: ///for the %VF2 algorithm, which is probably more convenient in most kpeter@1407: ///use cases. alpar@1351: /// alpar@1351: ///\tparam G1 The type of the graph to be embedded. kpeter@1410: ///The default type is \ref ListGraph. alpar@1351: ///\tparam G2 The type of the graph g1 will be embedded into. kpeter@1410: ///The default type is \ref ListGraph. alpar@1351: ///\tparam M The type of the NodeMap storing the mapping. alpar@1351: ///By default, it is G1::NodeMap alpar@1351: ///\tparam NEQ A bool-valued binary functor determinining whether a node is kpeter@1407: ///mappable to another. By default, it is an always-true operator. alpar@1351: /// alpar@1351: ///\sa vf2() alpar@1351: #ifdef DOXYGEN alpar@1351: template alpar@1351: #else kpeter@1410: template, alpar@1351: class NEQ = bits::vf2::AlwaysEq > alpar@1351: #endif madarasip@1405: class Vf2 { kpeter@1407: //The graph to be embedded madarasip@1350: const G1 &_g1; kpeter@1407: kpeter@1407: //The graph into which g1 is to be embedded madarasip@1350: const G2 &_g2; kpeter@1407: kpeter@1408: //Functor with bool operator()(G1::Node,G2::Node), which returns 1 kpeter@1408: //if and only if the two nodes are equivalent kpeter@1408: NEQ _nEq; kpeter@1408: kpeter@1408: //Current depth in the DFS tree. kpeter@1408: int _depth; kpeter@1408: kpeter@1408: //The current mapping. _mapping[v1]=v2 iff v1 has been mapped to v2, kpeter@1408: //where v1 is a node of G1 and v2 is a node of G2 kpeter@1408: M &_mapping; kpeter@1408: kpeter@1408: //_order[i] is the node of g1 for which a pair is searched if depth=i kpeter@1408: std::vector _order; kpeter@1408: kpeter@1408: //_conn[v2] = number of covered neighbours of v2 kpeter@1408: typename G2::template NodeMap _conn; kpeter@1408: kpeter@1408: //_currEdgeIts[i] is the last used edge iterator in the i-th kpeter@1408: //depth to find a pair for node _order[i] kpeter@1408: std::vector _currEdgeIts; kpeter@1408: madarasip@1405: //lookup tables for cutting the searchtree kpeter@1408: typename G1::template NodeMap _rNew1t, _rInOut1t; madarasip@1350: madarasip@1405: MappingType _mapping_type; madarasip@1405: madarasip@1405: bool _deallocMappingAfterUse; madarasip@1350: madarasip@1350: //cut the search tree madarasip@1405: template madarasip@1405: bool cut(const typename G1::Node n1,const typename G2::Node n2) const { madarasip@1350: int rNew2=0,rInOut2=0; madarasip@1405: for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { madarasip@1405: const typename G2::Node currNode=_g2.oppositeNode(n2,e2); madarasip@1405: if(_conn[currNode]>0) madarasip@1405: ++rInOut2; madarasip@1405: else if(MT!=SUBGRAPH&&_conn[currNode]==0) madarasip@1405: ++rNew2; madarasip@1405: } madarasip@1405: switch(MT) { madarasip@1405: case INDUCED: kpeter@1408: return _rInOut1t[n1]<=rInOut2&&_rNew1t[n1]<=rNew2; madarasip@1405: case SUBGRAPH: kpeter@1408: return _rInOut1t[n1]<=rInOut2; madarasip@1405: case ISOMORPH: kpeter@1408: return _rInOut1t[n1]==rInOut2&&_rNew1t[n1]==rNew2; madarasip@1405: default: madarasip@1405: return false; madarasip@1405: } madarasip@1350: } madarasip@1350: madarasip@1405: template madarasip@1405: bool feas(const typename G1::Node n1,const typename G2::Node n2) { madarasip@1350: if(!_nEq(n1,n2)) madarasip@1350: return 0; madarasip@1350: madarasip@1405: for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1) { madarasip@1405: const typename G1::Node& currNode=_g1.oppositeNode(n1,e1); madarasip@1405: if(_mapping[currNode]!=INVALID) madarasip@1405: --_conn[_mapping[currNode]]; madarasip@1405: } madarasip@1405: bool isIso=1; madarasip@1405: for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { madarasip@1405: int& connCurrNode = _conn[_g2.oppositeNode(n2,e2)]; madarasip@1405: if(connCurrNode<-1) madarasip@1405: ++connCurrNode; madarasip@1405: else if(MT!=SUBGRAPH&&connCurrNode==-1) { madarasip@1405: isIso=0; madarasip@1405: break; madarasip@1350: } madarasip@1405: } madarasip@1405: madarasip@1405: for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1) { madarasip@1405: const typename G2::Node& currNodePair=_mapping[_g1.oppositeNode(n1,e1)]; madarasip@1405: int& connCurrNodePair=_conn[currNodePair]; madarasip@1405: if(currNodePair!=INVALID&&connCurrNodePair!=-1) { madarasip@1405: switch(MT) { madarasip@1405: case INDUCED: madarasip@1405: case ISOMORPH: madarasip@1405: isIso=0; madarasip@1405: break; madarasip@1405: case SUBGRAPH: madarasip@1405: if(connCurrNodePair<-1) madarasip@1350: isIso=0; madarasip@1405: break; madarasip@1405: } madarasip@1405: connCurrNodePair=-1; madarasip@1350: } madarasip@1405: } madarasip@1350: return isIso&&cut(n1,n2); madarasip@1350: } madarasip@1350: madarasip@1405: void addPair(const typename G1::Node n1,const typename G2::Node n2) { madarasip@1350: _conn[n2]=-1; alpar@1351: _mapping.set(n1,n2); madarasip@1405: for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { madarasip@1405: int& currConn = _conn[_g2.oppositeNode(n2,e2)]; madarasip@1405: if(currConn!=-1) madarasip@1405: ++currConn; madarasip@1405: } madarasip@1350: } madarasip@1350: madarasip@1405: void subPair(const typename G1::Node n1,const typename G2::Node n2) { madarasip@1350: _conn[n2]=0; alpar@1351: _mapping.set(n1,INVALID); madarasip@1405: for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { madarasip@1405: int& currConn = _conn[_g2.oppositeNode(n2,e2)]; madarasip@1405: if(currConn>0) madarasip@1405: --currConn; madarasip@1405: else if(currConn==-1) madarasip@1405: ++_conn[n2]; madarasip@1405: } madarasip@1350: } madarasip@1350: kpeter@1409: void initOrder() { madarasip@1405: // we will find pairs for the nodes of g1 in this order madarasip@1405: kpeter@1408: // bits::vf2::DfsLeaveOrder v(_g1,_order); madarasip@1350: // DfsVisit >dfs(_g1, v); madarasip@1350: // dfs.run(); madarasip@1350: madarasip@1350: //it is more efficient in practice than DFS kpeter@1408: bits::vf2::BfsLeaveOrder v(_g1,_order); madarasip@1350: BfsVisit >bfs(_g1, v); madarasip@1350: bfs.run(); madarasip@1350: } madarasip@1350: madarasip@1405: template madarasip@1405: bool extMatch() { madarasip@1405: while(_depth>=0) { kpeter@1408: if(_depth==static_cast(_order.size())) { kpeter@1407: //all nodes of g1 are mapped to nodes of g2 madarasip@1405: --_depth; madarasip@1405: return true; madarasip@1405: } kpeter@1408: typename G1::Node& nodeOfDepth = _order[_depth]; madarasip@1405: const typename G2::Node& pairOfNodeOfDepth = _mapping[nodeOfDepth]; kpeter@1408: typename G2::IncEdgeIt &edgeItOfDepth = _currEdgeIts[_depth]; kpeter@1407: //the node of g2 whose neighbours are the candidates for madarasip@1405: //the pair of nodeOfDepth madarasip@1405: typename G2::Node currPNode; madarasip@1405: if(edgeItOfDepth==INVALID) { madarasip@1405: typename G1::IncEdgeIt fstMatchedE(_g1,nodeOfDepth); kpeter@1407: //if pairOfNodeOfDepth!=INVALID, we don't use fstMatchedE kpeter@1407: if(pairOfNodeOfDepth==INVALID) { madarasip@1405: for(; fstMatchedE!=INVALID && madarasip@1405: _mapping[_g1.oppositeNode(nodeOfDepth, madarasip@1405: fstMatchedE)]==INVALID; madarasip@1405: ++fstMatchedE) ; //find fstMatchedE kpeter@1407: } madarasip@1405: if(fstMatchedE==INVALID||pairOfNodeOfDepth!=INVALID) { kpeter@1407: //We found no covered neighbours, this means that kpeter@1407: //the graph is not connected (or _depth==0). Each kpeter@1407: //uncovered (and there are some other properties due madarasip@1405: //to the spec. problem types) node of g2 is kpeter@1407: //candidate. We can read the iterator of the last madarasip@1405: //tried node from the match if it is not the first kpeter@1407: //try (match[nodeOfDepth]!=INVALID) madarasip@1405: typename G2::NodeIt n2(_g2); madarasip@1405: //if it's not the first try madarasip@1405: if(pairOfNodeOfDepth!=INVALID) { madarasip@1405: n2=++typename G2::NodeIt(_g2,pairOfNodeOfDepth); madarasip@1405: subPair(nodeOfDepth,pairOfNodeOfDepth); madarasip@1405: } madarasip@1405: for(; n2!=INVALID; ++n2) madarasip@1405: if(MT!=SUBGRAPH) { madarasip@1405: if(_conn[n2]==0&&feas(nodeOfDepth,n2)) madarasip@1405: break; madarasip@1405: } madarasip@1405: else if(_conn[n2]>=0&&feas(nodeOfDepth,n2)) madarasip@1405: break; madarasip@1405: // n2 is the next candidate madarasip@1405: if(n2!=INVALID){ madarasip@1405: addPair(nodeOfDepth,n2); madarasip@1405: ++_depth; madarasip@1405: } madarasip@1405: else // there are no more candidates madarasip@1350: --_depth; madarasip@1405: continue; madarasip@1405: } madarasip@1405: else { madarasip@1405: currPNode=_mapping[_g1.oppositeNode(nodeOfDepth, madarasip@1405: fstMatchedE)]; madarasip@1405: edgeItOfDepth=typename G2::IncEdgeIt(_g2,currPNode); madarasip@1405: } madarasip@1350: } madarasip@1405: else { madarasip@1405: currPNode=_g2.oppositeNode(pairOfNodeOfDepth, madarasip@1405: edgeItOfDepth); madarasip@1405: subPair(nodeOfDepth,pairOfNodeOfDepth); madarasip@1405: ++edgeItOfDepth; madarasip@1405: } madarasip@1405: for(; edgeItOfDepth!=INVALID; ++edgeItOfDepth) { madarasip@1405: const typename G2::Node currNode = madarasip@1405: _g2.oppositeNode(currPNode, edgeItOfDepth); madarasip@1405: if(_conn[currNode]>0&&feas(nodeOfDepth,currNode)) { madarasip@1405: addPair(nodeOfDepth,currNode); madarasip@1405: break; madarasip@1405: } madarasip@1405: } madarasip@1405: edgeItOfDepth==INVALID?--_depth:++_depth; madarasip@1405: } madarasip@1350: return false; madarasip@1350: } madarasip@1350: kpeter@1407: //calculate the lookup table for cutting the search tree kpeter@1409: void initRNew1tRInOut1t() { madarasip@1350: typename G1::template NodeMap tmp(_g1,0); kpeter@1408: for(unsigned int i=0; i<_order.size(); ++i) { kpeter@1408: const typename G1::Node& orderI = _order[i]; madarasip@1405: tmp[orderI]=-1; madarasip@1405: for(typename G1::IncEdgeIt e1(_g1,orderI); e1!=INVALID; ++e1) { madarasip@1405: const typename G1::Node currNode=_g1.oppositeNode(orderI,e1); madarasip@1405: if(tmp[currNode]>0) kpeter@1408: ++_rInOut1t[orderI]; madarasip@1405: else if(tmp[currNode]==0) kpeter@1408: ++_rNew1t[orderI]; madarasip@1350: } madarasip@1405: for(typename G1::IncEdgeIt e1(_g1,orderI); e1!=INVALID; ++e1) { madarasip@1405: const typename G1::Node currNode=_g1.oppositeNode(orderI,e1); madarasip@1405: if(tmp[currNode]!=-1) madarasip@1405: ++tmp[currNode]; madarasip@1405: } madarasip@1405: } madarasip@1350: } madarasip@1350: public: alpar@1351: ///Constructor alpar@1351: alpar@1351: ///Constructor alpar@1351: alpar@1351: ///\param g1 The graph to be embedded into \e g2. alpar@1351: ///\param g2 The graph \e g1 will be embedded into. alpar@1351: ///\param m \ref concepts::ReadWriteMap "read-write" NodeMap alpar@1351: ///storing the found mapping. madarasip@1405: ///\param neq A bool-valued binary functor determining whether a node is alpar@1351: ///mappable to another. By default it is an always true operator. madarasip@1405: Vf2(const G1 &g1, const G2 &g2, M &m, const NEQ &neq = NEQ() ) : kpeter@1408: _g1(g1), _g2(g2), _nEq(neq), _depth(0), _mapping(m), kpeter@1408: _order(countNodes(g1)), _conn(g2,0), kpeter@1408: _currEdgeIts(countNodes(g1),INVALID), _rNew1t(g1,0), _rInOut1t(g1,0), kpeter@1408: _mapping_type(SUBGRAPH), _deallocMappingAfterUse(0) kpeter@1407: { kpeter@1409: initOrder(); kpeter@1409: initRNew1tRInOut1t(); alpar@1352: for(typename G1::NodeIt n(g1);n!=INVALID;++n) alpar@1352: m[n]=INVALID; madarasip@1350: } madarasip@1350: madarasip@1405: ///Destructor madarasip@1405: madarasip@1405: ///Destructor. madarasip@1405: /// madarasip@1405: madarasip@1405: ~Vf2(){ madarasip@1405: if(_deallocMappingAfterUse) madarasip@1405: delete &_mapping; madarasip@1405: } madarasip@1405: alpar@1351: ///Returns the current mapping type madarasip@1350: alpar@1351: ///Returns the current mapping type alpar@1351: /// madarasip@1405: MappingType mappingType() const { madarasip@1405: return _mapping_type; madarasip@1405: } alpar@1351: ///Sets mapping type alpar@1351: alpar@1351: ///Sets mapping type. alpar@1351: /// alpar@1351: ///The mapping type is set to \ref SUBGRAPH by default. alpar@1351: /// madarasip@1405: ///\sa See \ref MappingType for the possible values. madarasip@1405: void mappingType(MappingType m_type) { madarasip@1405: _mapping_type = m_type; madarasip@1405: } alpar@1351: kpeter@1366: ///Finds a mapping alpar@1351: madarasip@1405: ///It finds a mapping from g1 into g2 according to the mapping madarasip@1405: ///type set by \ref mappingType(MappingType) "mappingType()". alpar@1351: /// alpar@1351: ///By subsequent calls, it returns all possible mappings one-by-one. alpar@1351: /// alpar@1351: ///\retval true if a mapping is found. alpar@1351: ///\retval false if there is no (more) mapping. madarasip@1405: bool find() { madarasip@1405: switch(_mapping_type) { madarasip@1405: case SUBGRAPH: madarasip@1405: return extMatch(); madarasip@1405: case INDUCED: madarasip@1405: return extMatch(); madarasip@1405: case ISOMORPH: madarasip@1405: return extMatch(); madarasip@1405: default: madarasip@1405: return false; madarasip@1405: } madarasip@1350: } madarasip@1350: }; madarasip@1350: madarasip@1350: template madarasip@1405: class Vf2WizardBase { madarasip@1350: protected: madarasip@1350: typedef G1 Graph1; madarasip@1350: typedef G2 Graph2; madarasip@1350: madarasip@1350: const G1 &_g1; madarasip@1350: const G2 &_g2; madarasip@1350: madarasip@1405: MappingType _mapping_type; madarasip@1350: madarasip@1350: typedef typename G1::template NodeMap Mapping; madarasip@1350: bool _local_mapping; madarasip@1350: void *_mapping; madarasip@1405: void createMapping() { madarasip@1350: _mapping = new Mapping(_g1); madarasip@1350: } madarasip@1350: madarasip@1405: void *myVf2; //used in Vf2Wizard::find madarasip@1405: madarasip@1405: madarasip@1350: typedef bits::vf2::AlwaysEq NodeEq; madarasip@1350: NodeEq _node_eq; madarasip@1350: madarasip@1350: Vf2WizardBase(const G1 &g1,const G2 &g2) madarasip@1405: : _g1(g1), _g2(g2), _mapping_type(SUBGRAPH), _local_mapping(true) { } madarasip@1350: }; madarasip@1350: madarasip@1405: alpar@1351: /// Auxiliary class for the function-type interface of %VF2 algorithm. kpeter@1407: /// alpar@1351: /// This auxiliary class implements the named parameters of alpar@1351: /// \ref vf2() "function-type interface" of \ref Vf2 algorithm. alpar@1351: /// kpeter@1407: /// \warning This class is not to be used directly. alpar@1351: /// alpar@1351: /// \tparam TR The traits class that defines various types used by the alpar@1351: /// algorithm. madarasip@1350: template madarasip@1405: class Vf2Wizard : public TR { madarasip@1350: typedef TR Base; madarasip@1350: typedef typename TR::Graph1 Graph1; madarasip@1350: typedef typename TR::Graph2 Graph2; madarasip@1350: madarasip@1350: typedef typename TR::Mapping Mapping; madarasip@1350: typedef typename TR::NodeEq NodeEq; madarasip@1350: madarasip@1350: using TR::_g1; madarasip@1350: using TR::_g2; madarasip@1350: using TR::_mapping_type; madarasip@1350: using TR::_mapping; madarasip@1350: using TR::_node_eq; madarasip@1350: madarasip@1350: public: alpar@1351: ///Constructor kpeter@1407: Vf2Wizard(const Graph1 &g1,const Graph2 &g2) : Base(g1,g2) {} madarasip@1350: madarasip@1350: ///Copy constructor kpeter@1407: Vf2Wizard(const Base &b) : Base(b) {} madarasip@1405: madarasip@1405: ///Copy constructor madarasip@1405: Vf2Wizard(const Vf2Wizard &b) : Base(b) {} madarasip@1350: madarasip@1350: madarasip@1350: template madarasip@1405: struct SetMappingBase : public Base{ madarasip@1350: typedef T Mapping; madarasip@1350: SetMappingBase(const Base &b) : Base(b) {} madarasip@1350: }; madarasip@1350: madarasip@1350: ///\brief \ref named-templ-param "Named parameter" for setting madarasip@1350: ///the mapping. madarasip@1350: /// madarasip@1350: ///\ref named-templ-param "Named parameter" function for setting madarasip@1350: ///the map that stores the found embedding. madarasip@1350: template madarasip@1405: Vf2Wizard< SetMappingBase > mapping(const T &t) { madarasip@1350: Base::_mapping=reinterpret_cast(const_cast(&t)); madarasip@1350: Base::_local_mapping = false; madarasip@1350: return Vf2Wizard >(*this); madarasip@1350: } madarasip@1350: madarasip@1350: template madarasip@1350: struct SetNodeEqBase : public Base { madarasip@1350: typedef NE NodeEq; madarasip@1350: NodeEq _node_eq; madarasip@1350: SetNodeEqBase(const Base &b, const NE &node_eq) madarasip@1405: : Base(b), _node_eq(node_eq){ madarasip@1405: } madarasip@1350: }; madarasip@1350: madarasip@1350: ///\brief \ref named-templ-param "Named parameter" for setting madarasip@1350: ///the node equivalence relation. madarasip@1350: /// madarasip@1350: ///\ref named-templ-param "Named parameter" function for setting madarasip@1350: ///the equivalence relation between the nodes. alpar@1351: /// alpar@1351: ///\param node_eq A bool-valued binary functor determinining alpar@1351: ///whether a node is mappable to another. By default it is an alpar@1351: ///always true operator. madarasip@1350: template madarasip@1405: Vf2Wizard< SetNodeEqBase > nodeEq(const T &node_eq) { madarasip@1350: return Vf2Wizard >(SetNodeEqBase(*this,node_eq)); madarasip@1350: } madarasip@1350: madarasip@1350: ///\brief \ref named-templ-param "Named parameter" for setting madarasip@1350: ///the node labels. madarasip@1350: /// madarasip@1350: ///\ref named-templ-param "Named parameter" function for setting madarasip@1350: ///the node labels defining equivalence relation between them. alpar@1351: /// madarasip@1405: ///\param m1 An arbitrary \ref concepts::ReadMap "readable node map" alpar@1353: ///of g1. madarasip@1405: ///\param m2 An arbitrary \ref concepts::ReadMap "readable node map" alpar@1353: ///of g2. alpar@1351: /// alpar@1351: ///The value type of these maps must be equal comparable. madarasip@1350: template madarasip@1350: Vf2Wizard< SetNodeEqBase > > madarasip@1405: nodeLabels(const M1 &m1,const M2 &m2){ madarasip@1350: return nodeEq(bits::vf2::MapEq(m1,m2)); madarasip@1350: } madarasip@1350: alpar@1351: ///\brief \ref named-templ-param "Named parameter" for setting alpar@1351: ///the mapping type. alpar@1351: /// alpar@1351: ///\ref named-templ-param "Named parameter" for setting alpar@1351: ///the mapping type. alpar@1351: /// alpar@1351: ///The mapping type is set to \ref SUBGRAPH by default. alpar@1351: /// madarasip@1405: ///\sa See \ref MappingType for the possible values. madarasip@1405: Vf2Wizard &mappingType(MappingType m_type) { madarasip@1350: _mapping_type = m_type; madarasip@1350: return *this; madarasip@1350: } madarasip@1350: alpar@1351: ///\brief \ref named-templ-param "Named parameter" for setting alpar@1351: ///the mapping type to \ref INDUCED. alpar@1351: /// alpar@1351: ///\ref named-templ-param "Named parameter" for setting alpar@1351: ///the mapping type to \ref INDUCED. madarasip@1405: Vf2Wizard &induced() { madarasip@1350: _mapping_type = INDUCED; madarasip@1350: return *this; madarasip@1350: } madarasip@1350: alpar@1351: ///\brief \ref named-templ-param "Named parameter" for setting alpar@1351: ///the mapping type to \ref ISOMORPH. alpar@1351: /// alpar@1351: ///\ref named-templ-param "Named parameter" for setting alpar@1351: ///the mapping type to \ref ISOMORPH. madarasip@1405: Vf2Wizard &iso() { madarasip@1350: _mapping_type = ISOMORPH; madarasip@1350: return *this; madarasip@1350: } madarasip@1350: madarasip@1405: alpar@1351: ///Runs VF2 algorithm. alpar@1351: alpar@1351: ///This method runs VF2 algorithm. alpar@1351: /// alpar@1351: ///\retval true if a mapping is found. madarasip@1405: ///\retval false if there is no mapping. madarasip@1405: bool run(){ madarasip@1350: if(Base::_local_mapping) madarasip@1350: Base::createMapping(); madarasip@1350: madarasip@1350: Vf2 madarasip@1350: alg(_g1, _g2, *reinterpret_cast(_mapping), _node_eq); madarasip@1350: madarasip@1350: alg.mappingType(_mapping_type); madarasip@1350: madarasip@1350: bool ret = alg.find(); madarasip@1350: madarasip@1350: if(Base::_local_mapping) madarasip@1350: delete reinterpret_cast(_mapping); madarasip@1350: madarasip@1350: return ret; madarasip@1350: } madarasip@1405: madarasip@1405: ///Get a pointer to the generated Vf2 object. madarasip@1405: madarasip@1405: ///Gives a pointer to the generated Vf2 object. madarasip@1405: /// madarasip@1405: ///\return Pointer to the generated Vf2 object. madarasip@1405: ///\warning Don't forget to delete the referred Vf2 object after use. madarasip@1405: Vf2* getPtrToVf2Object() { madarasip@1405: if(Base::_local_mapping) madarasip@1405: Base::createMapping(); madarasip@1405: Vf2* ptr = madarasip@1405: new Vf2 madarasip@1405: (_g1, _g2, *reinterpret_cast(_mapping), _node_eq); madarasip@1405: ptr->mappingType(_mapping_type); madarasip@1405: if(Base::_local_mapping) madarasip@1405: ptr->_deallocMappingAfterUse = true; madarasip@1405: return ptr; madarasip@1405: } madarasip@1405: madarasip@1405: ///Counts the number of mappings. madarasip@1405: madarasip@1405: ///This method counts the number of mappings. madarasip@1405: /// madarasip@1405: /// \return The number of mappings. madarasip@1405: int count() { madarasip@1405: if(Base::_local_mapping) madarasip@1405: Base::createMapping(); madarasip@1405: madarasip@1405: Vf2 madarasip@1405: alg(_g1, _g2, *reinterpret_cast(_mapping), _node_eq); madarasip@1405: if(Base::_local_mapping) madarasip@1405: alg._deallocMappingAfterUse = true; madarasip@1405: alg.mappingType(_mapping_type); madarasip@1405: madarasip@1405: int ret = 0; madarasip@1405: while(alg.find()) madarasip@1405: ++ret; madarasip@1405: madarasip@1405: return ret; madarasip@1405: } madarasip@1350: }; madarasip@1350: alpar@1351: ///Function-type interface for VF2 algorithm. alpar@1351: alpar@1351: /// \ingroup graph_isomorphism alpar@1351: ///Function-type interface for VF2 algorithm \cite cordella2004sub. alpar@1351: /// alpar@1351: ///This function has several \ref named-func-param "named parameters" alpar@1351: ///declared as the members of class \ref Vf2Wizard. alpar@1351: ///The following examples show how to use these parameters. alpar@1351: ///\code madarasip@1405: /// // Find an embedding of graph g1 into graph g2 alpar@1351: /// ListGraph::NodeMap m(g); madarasip@1405: /// vf2(g1,g2).mapping(m).run(); alpar@1351: /// madarasip@1405: /// // Check whether graphs g1 and g2 are isomorphic madarasip@1405: /// bool is_iso = vf2(g1,g2).iso().run(); madarasip@1405: /// madarasip@1405: /// // Count the number of isomorphisms madarasip@1405: /// int num_isos = vf2(g1,g2).iso().count(); madarasip@1405: /// madarasip@1405: /// // Iterate through all the induced subgraph mappings of graph g1 into g2 madarasip@1405: /// auto* myVf2 = vf2(g1,g2).mapping(m).nodeLabels(c1,c2) madarasip@1405: /// .induced().getPtrToVf2Object(); madarasip@1405: /// while(myVf2->find()){ madarasip@1405: /// //process the current mapping m madarasip@1405: /// } madarasip@1405: /// delete myVf22; alpar@1351: ///\endcode madarasip@1405: ///\warning Don't forget to put the \ref Vf2Wizard::run() "run()", madarasip@1405: ///\ref Vf2Wizard::count() "count()" or madarasip@1405: ///the \ref Vf2Wizard::getPtrToVf2Object() "getPtrToVf2Object()" alpar@1351: ///to the end of the expression. alpar@1351: ///\sa Vf2Wizard alpar@1351: ///\sa Vf2 madarasip@1350: template madarasip@1405: Vf2Wizard > vf2(const G1 &g1, const G2 &g2) { madarasip@1350: return Vf2Wizard >(g1,g2); madarasip@1350: } madarasip@1350: madarasip@1350: } madarasip@1350: madarasip@1350: #endif