Changes in / [1404:c8d0179a32a2:1415:959d78f3fe0e] in lemon
- Files:
-
- 2 added
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/references.bib
r1367 r1414 43 43 pages = {67--118} 44 44 } 45 46 @article{VF2PP, 47 author = {Alp\'ar J\"uttner and P\'eter Madarasi}, 48 title = {{VF2++} â An improved subgraph isomorphism algorithm}, 49 journal = {Discrete Applied Mathematics}, 50 year = {2018}, 51 volume = {242}, 52 pages = {69--81}, 53 url = {https://doi.org/10.1016/j.dam.2018.02.018} 54 } 55 45 56 46 57 -
lemon/vf2.h
r1370 r1413 3 3 * This file is a part of LEMON, a generic C++ optimization library. 4 4 * 5 * Copyright (C) 2015 5 * Copyright (C) 2015-2017 6 6 * EMAXA Kutato-fejleszto Kft. (EMAXA Research Ltd.) 7 7 * … … 25 25 #include <lemon/core.h> 26 26 #include <lemon/concepts/graph.h> 27 #include <lemon/dfs.h>28 27 #include <lemon/bfs.h> 28 #include <lemon/bits/vf2_internals.h> 29 29 30 30 #include <vector> 31 #include <set> 32 33 namespace lemon 34 { 35 namespace bits 36 { 37 namespace vf2 38 { 39 class AlwaysEq 40 { 31 32 namespace lemon { 33 namespace bits { 34 namespace vf2 { 35 36 class AlwaysEq { 41 37 public: 42 38 template<class T1, class T2> 43 bool operator()(T1, T2) const 44 { 39 bool operator()(T1&, T2&) const { 45 40 return true; 46 41 } … … 48 43 49 44 template<class M1, class M2> 50 class MapEq 51 { 45 class MapEq{ 52 46 const M1 &_m1; 53 47 const M2 &_m2; 54 48 public: 55 MapEq(const M1 &m1, const M2 &m2) : _m1(m1), _m2(m2) {} 56 bool operator()(typename M1::Key k1, typename M2::Key k2) const 57 { 49 MapEq(const M1 &m1, const M2 &m2) : _m1(m1), _m2(m2) { } 50 bool operator()(typename M1::Key k1, typename M2::Key k2) const { 58 51 return _m1[k1] == _m2[k2]; 59 52 } … … 61 54 62 55 template <class G> 63 class DfsLeaveOrder : public DfsVisitor<G> 64 { 65 const G &_g; 66 std::vector<typename G::Node> &_order; 67 int i; 68 public: 69 DfsLeaveOrder(const G &g, std::vector<typename G::Node> &order) 70 : i(countNodes(g)), _g(g), _order(order) 71 {} 72 void leave(const typename G::Node &node) 73 { 74 _order[--i]=node; 75 } 76 }; 77 78 template <class G> 79 class BfsLeaveOrder : public BfsVisitor<G> 80 { 56 class BfsLeaveOrder : public BfsVisitor<G> { 81 57 int i; 82 58 const G &_g; … … 84 60 public: 85 61 BfsLeaveOrder(const G &g, std::vector<typename G::Node> &order) 86 : i(0), _g(g), _order(order) 87 {} 88 void process(const typename G::Node &node) 89 { 62 : i(0), _g(g), _order(order){ 63 } 64 void process(const typename G::Node &node) { 90 65 _order[i++]=node; 91 66 } … … 94 69 } 95 70 96 ///Graph mapping types.97 98 ///\ingroup graph_isomorphism99 ///The \ref Vf2 "VF2" algorithm is capable of finding different kind of100 ///embeddings, this enum specifies its type.101 ///102 ///See \ref graph_isomorphism for a more detailed description.103 enum Vf2MappingType {104 /// Subgraph isomorphism105 SUBGRAPH = 0,106 /// Induced subgraph isomorphism107 INDUCED = 1,108 /// Graph isomorphism109 110 /// If the two graph has the same number of nodes, than it is111 /// equivalent to \ref INDUCED, and if they also have the same112 /// number of edges, then it is also equivalent to \ref SUBGRAPH.113 ///114 /// However, using this setting is faster than the other two115 /// options.116 ISOMORPH = 2117 };118 71 119 72 ///%VF2 algorithm class. … … 125 78 ///There is also a \ref vf2() "function-type interface" called \ref vf2() 126 79 ///for the %VF2 algorithm, which is probably more convenient in most 127 ///use -cases.80 ///use cases. 128 81 /// 129 82 ///\tparam G1 The type of the graph to be embedded. 130 ///The default type is \ref List Digraph.83 ///The default type is \ref ListGraph. 131 84 ///\tparam G2 The type of the graph g1 will be embedded into. 132 ///The default type is \ref List Digraph.85 ///The default type is \ref ListGraph. 133 86 ///\tparam M The type of the NodeMap storing the mapping. 134 87 ///By default, it is G1::NodeMap<G2::Node> 135 88 ///\tparam NEQ A bool-valued binary functor determinining whether a node is 136 ///mappable to another. By default it is an alwaystrue operator.89 ///mappable to another. By default, it is an always-true operator. 137 90 /// 138 91 ///\sa vf2() … … 140 93 template<class G1, class G2, class M, class NEQ > 141 94 #else 142 template<class G1 =ListDigraph,143 class G2 =ListDigraph,95 template<class G1 = ListGraph, 96 class G2 = ListGraph, 144 97 class M = typename G1::template NodeMap<G2::Node>, 145 98 class NEQ = bits::vf2::AlwaysEq > 146 99 #endif 147 class Vf2 148 { 149 //Current depth in the DFS tree. 100 class Vf2 { 101 //The graph to be embedded 102 const G1 &_g1; 103 104 //The graph into which g1 is to be embedded 105 const G2 &_g2; 106 107 //Functor with bool operator()(G1::Node,G2::Node), which returns 1 108 //if and only if the two nodes are equivalent 109 NEQ _nEq; 110 111 //Current depth in the search tree 150 112 int _depth; 151 //Functor with bool operator()(G1::Node,G2::Node), which returns 1 152 //if and only if the 2 nodes are equivalent. 153 NEQ _nEq; 154 113 114 //The current mapping. _mapping[v1]=v2 iff v1 has been mapped to v2, 115 //where v1 is a node of G1 and v2 is a node of G2 116 M &_mapping; 117 118 //_order[i] is the node of g1 for which a pair is searched if depth=i 119 std::vector<typename G1::Node> _order; 120 121 //_conn[v2] = number of covered neighbours of v2 155 122 typename G2::template NodeMap<int> _conn; 156 //Current mapping. We index it by the nodes of g1, and match[v] is 157 //a node of g2. 158 M &_mapping; 159 //order[i] is the node of g1, for which we find a pair if depth=i 160 std::vector<typename G1::Node> order; 161 //currEdgeIts[i] is an edge iterator, witch is last used in the ith 162 //depth to find a pair for order[i]. 163 std::vector<typename G2::IncEdgeIt> currEdgeIts; 164 //The small graph. 165 const G1 &_g1; 166 //The big graph. 167 const G2 &_g2; 168 //lookup tables for cut the searchtree 169 typename G1::template NodeMap<int> rNew1t,rInOut1t; 170 171 Vf2MappingType _mapping_type; 123 124 //_currEdgeIts[i] is the last used edge iterator in the i-th 125 //depth to find a pair for node _order[i] 126 std::vector<typename G2::IncEdgeIt> _currEdgeIts; 127 128 //lookup tables for cutting the searchtree 129 typename G1::template NodeMap<int> _rNew1t, _rInOut1t; 130 131 MappingType _mapping_type; 132 133 bool _deallocMappingAfterUse; 172 134 173 135 //cut the search tree 174 template<Vf2MappingType MT> 175 bool cut(const typename G1::Node n1,const typename G2::Node n2) const 176 { 136 template<MappingType MT> 137 bool cut(const typename G1::Node n1,const typename G2::Node n2) const { 177 138 int rNew2=0,rInOut2=0; 178 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) 179 { 180 const typename G2::Node currNode=_g2.oppositeNode(n2,e2); 181 if(_conn[currNode]>0) 182 ++rInOut2; 183 else if(MT!=SUBGRAPH&&_conn[currNode]==0) 184 ++rNew2; 185 } 186 switch(MT) 187 { 188 case INDUCED: 189 return rInOut1t[n1]<=rInOut2&&rNew1t[n1]<=rNew2; 190 case SUBGRAPH: 191 return rInOut1t[n1]<=rInOut2; 192 case ISOMORPH: 193 return rInOut1t[n1]==rInOut2&&rNew1t[n1]==rNew2; 194 default: 195 return false; 196 } 197 } 198 199 template<Vf2MappingType MT> 200 bool feas(const typename G1::Node n1,const typename G2::Node n2) 201 { 139 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { 140 const typename G2::Node currNode=_g2.oppositeNode(n2,e2); 141 if(_conn[currNode]>0) 142 ++rInOut2; 143 else if(MT!=SUBGRAPH&&_conn[currNode]==0) 144 ++rNew2; 145 } 146 switch(MT) { 147 case INDUCED: 148 return _rInOut1t[n1]<=rInOut2&&_rNew1t[n1]<=rNew2; 149 case SUBGRAPH: 150 return _rInOut1t[n1]<=rInOut2; 151 case ISOMORPH: 152 return _rInOut1t[n1]==rInOut2&&_rNew1t[n1]==rNew2; 153 default: 154 return false; 155 } 156 } 157 158 template<MappingType MT> 159 bool feas(const typename G1::Node n1,const typename G2::Node n2) { 202 160 if(!_nEq(n1,n2)) 203 161 return 0; 204 162 205 for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1) 206 { 207 const typename G1::Node currNode=_g1.oppositeNode(n1,e1); 208 if(_mapping[currNode]!=INVALID) 209 --_conn[_mapping[currNode]]; 210 } 163 for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1) { 164 const typename G1::Node& currNode=_g1.oppositeNode(n1,e1); 165 if(_mapping[currNode]!=INVALID) 166 --_conn[_mapping[currNode]]; 167 } 211 168 bool isIso=1; 212 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) 213 { 214 const typename G2::Node currNode=_g2.oppositeNode(n2,e2); 215 if(_conn[currNode]<-1) 216 ++_conn[currNode]; 217 else if(MT!=SUBGRAPH&&_conn[currNode]==-1) 218 { 169 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { 170 int& connCurrNode = _conn[_g2.oppositeNode(n2,e2)]; 171 if(connCurrNode<-1) 172 ++connCurrNode; 173 else if(MT!=SUBGRAPH&&connCurrNode==-1) { 174 isIso=0; 175 break; 176 } 177 } 178 179 for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1) { 180 const typename G2::Node& currNodePair=_mapping[_g1.oppositeNode(n1,e1)]; 181 int& connCurrNodePair=_conn[currNodePair]; 182 if(currNodePair!=INVALID&&connCurrNodePair!=-1) { 183 switch(MT) { 184 case INDUCED: 185 case ISOMORPH: 186 isIso=0; 187 break; 188 case SUBGRAPH: 189 if(connCurrNodePair<-1) 219 190 isIso=0; 220 break; 221 } 222 } 223 224 for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1) 225 { 226 const typename G1::Node currNode=_g1.oppositeNode(n1,e1); 227 if(_mapping[currNode]!=INVALID&&_conn[_mapping[currNode]]!=-1) 228 { 229 switch(MT) 230 { 231 case INDUCED: 232 case ISOMORPH: 233 isIso=0; 234 break; 235 case SUBGRAPH: 236 if(_conn[_mapping[currNode]]<-1) 237 isIso=0; 238 break; 239 } 240 _conn[_mapping[currNode]]=-1; 241 } 242 } 191 break; 192 } 193 connCurrNodePair=-1; 194 } 195 } 243 196 return isIso&&cut<MT>(n1,n2); 244 197 } 245 198 246 void addPair(const typename G1::Node n1,const typename G2::Node n2) 247 { 199 void addPair(const typename G1::Node n1,const typename G2::Node n2) { 248 200 _conn[n2]=-1; 249 201 _mapping.set(n1,n2); 250 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) 251 if(_conn[_g2.oppositeNode(n2,e2)]!=-1) 252 ++_conn[_g2.oppositeNode(n2,e2)]; 253 } 254 255 void subPair(const typename G1::Node n1,const typename G2::Node n2) 256 { 202 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { 203 int& currConn = _conn[_g2.oppositeNode(n2,e2)]; 204 if(currConn!=-1) 205 ++currConn; 206 } 207 } 208 209 void subPair(const typename G1::Node n1,const typename G2::Node n2) { 257 210 _conn[n2]=0; 258 211 _mapping.set(n1,INVALID); 259 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) 260 { 261 const typename G2::Node currNode=_g2.oppositeNode(n2,e2); 262 if(_conn[currNode]>0) 263 --_conn[currNode]; 264 else if(_conn[currNode]==-1) 265 ++_conn[n2]; 266 } 267 } 268 269 void setOrder()//we will find pairs for the nodes of g1 in this order 270 { 271 // bits::vf2::DfsLeaveOrder<G1> v(_g1,order); 272 // DfsVisit<G1,bits::vf2::DfsLeaveOrder<G1> >dfs(_g1, v); 273 // dfs.run(); 274 275 //it is more efficient in practice than DFS 276 bits::vf2::BfsLeaveOrder<G1> v(_g1,order); 277 BfsVisit<G1,bits::vf2::BfsLeaveOrder<G1> >bfs(_g1, v); 212 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { 213 int& currConn = _conn[_g2.oppositeNode(n2,e2)]; 214 if(currConn>0) 215 --currConn; 216 else if(currConn==-1) 217 ++_conn[n2]; 218 } 219 } 220 221 void initOrder() { 222 //determine the order in which we will find pairs for the nodes of g1 223 //BFS order is more efficient in practice than DFS 224 bits::vf2::BfsLeaveOrder<G1> v(_g1,_order); 225 BfsVisit<G1,bits::vf2::BfsLeaveOrder<G1> > bfs(_g1, v); 278 226 bfs.run(); 279 227 } 280 228 281 template<Vf2MappingType MT> 282 bool extMatch() 283 { 284 while(_depth>=0) 285 { 286 //there are not nodes in g1, which has not pair in g2. 287 if(_depth==static_cast<int>(order.size())) 288 { 229 template<MappingType MT> 230 bool extMatch() { 231 while(_depth>=0) { 232 if(_depth==static_cast<int>(_order.size())) { 233 //all nodes of g1 are mapped to nodes of g2 234 --_depth; 235 return true; 236 } 237 typename G1::Node& nodeOfDepth = _order[_depth]; 238 const typename G2::Node& pairOfNodeOfDepth = _mapping[nodeOfDepth]; 239 typename G2::IncEdgeIt &edgeItOfDepth = _currEdgeIts[_depth]; 240 //the node of g2 whose neighbours are the candidates for 241 //the pair of nodeOfDepth 242 typename G2::Node currPNode; 243 if(edgeItOfDepth==INVALID) { 244 typename G1::IncEdgeIt fstMatchedE(_g1,nodeOfDepth); 245 //if pairOfNodeOfDepth!=INVALID, we don't use fstMatchedE 246 if(pairOfNodeOfDepth==INVALID) { 247 for(; fstMatchedE!=INVALID && 248 _mapping[_g1.oppositeNode(nodeOfDepth, 249 fstMatchedE)]==INVALID; 250 ++fstMatchedE) ; //find fstMatchedE 251 } 252 if(fstMatchedE==INVALID||pairOfNodeOfDepth!=INVALID) { 253 //We found no covered neighbours, this means that 254 //the graph is not connected (or _depth==0). Each 255 //uncovered (and there are some other properties due 256 //to the spec. problem types) node of g2 is 257 //candidate. We can read the iterator of the last 258 //tried node from the match if it is not the first 259 //try (match[nodeOfDepth]!=INVALID) 260 typename G2::NodeIt n2(_g2); 261 //if it's not the first try 262 if(pairOfNodeOfDepth!=INVALID) { 263 n2=++typename G2::NodeIt(_g2,pairOfNodeOfDepth); 264 subPair(nodeOfDepth,pairOfNodeOfDepth); 265 } 266 for(; n2!=INVALID; ++n2) 267 if(MT!=SUBGRAPH) { 268 if(_conn[n2]==0&&feas<MT>(nodeOfDepth,n2)) 269 break; 270 } 271 else if(_conn[n2]>=0&&feas<MT>(nodeOfDepth,n2)) 272 break; 273 // n2 is the next candidate 274 if(n2!=INVALID){ 275 addPair(nodeOfDepth,n2); 276 ++_depth; 277 } 278 else // there are no more candidates 289 279 --_depth; 290 return true; 291 } 292 //the node of g2, which neighbours are the candidates for 293 //the pair of order[_depth] 294 typename G2::Node currPNode; 295 if(currEdgeIts[_depth]==INVALID) 296 { 297 typename G1::IncEdgeIt fstMatchedE(_g1,order[_depth]); 298 //if _mapping[order[_depth]]!=INVALID, we dont use 299 //fstMatchedE 300 if(_mapping[order[_depth]]==INVALID) 301 for(; fstMatchedE!=INVALID && 302 _mapping[_g1.oppositeNode(order[_depth], 303 fstMatchedE)]==INVALID; 304 ++fstMatchedE) ; //find fstMatchedE 305 if(fstMatchedE==INVALID||_mapping[order[_depth]]!=INVALID) 306 { 307 //We did not find an covered neighbour, this means 308 //the graph is not connected(or _depth==0). Every 309 //uncovered(and there are some other properties due 310 //to the spec. problem types) node of g2 is 311 //candidate. We can read the iterator of the last 312 //tryed node from the match if it is not the first 313 //try(match[order[_depth]]!=INVALID) 314 typename G2::NodeIt n2(_g2); 315 //if its not the first try 316 if(_mapping[order[_depth]]!=INVALID) 317 { 318 n2=++typename G2::NodeIt(_g2,_mapping[order[_depth]]); 319 subPair(order[_depth],_mapping[order[_depth]]); 320 } 321 for(; n2!=INVALID; ++n2) 322 if(MT!=SUBGRAPH&&_conn[n2]==0) 323 { 324 if(feas<MT>(order[_depth],n2)) 325 break; 326 } 327 else if(MT==SUBGRAPH&&_conn[n2]>=0) 328 if(feas<MT>(order[_depth],n2)) 329 break; 330 // n2 is the next candidate 331 if(n2!=INVALID) 332 { 333 addPair(order[_depth],n2); 334 ++_depth; 335 } 336 else // there is no more candidate 337 --_depth; 338 continue; 339 } 340 else 341 { 342 currPNode=_mapping[_g1.oppositeNode(order[_depth], 343 fstMatchedE)]; 344 currEdgeIts[_depth]=typename G2::IncEdgeIt(_g2,currPNode); 345 } 346 } 347 else 348 { 349 currPNode=_g2.oppositeNode(_mapping[order[_depth]], 350 currEdgeIts[_depth]); 351 subPair(order[_depth],_mapping[order[_depth]]); 352 ++currEdgeIts[_depth]; 353 } 354 for(; currEdgeIts[_depth]!=INVALID; ++currEdgeIts[_depth]) 355 { 356 const typename G2::Node currNode = 357 _g2.oppositeNode(currPNode, currEdgeIts[_depth]); 358 //if currNode is uncovered 359 if(_conn[currNode]>0&&feas<MT>(order[_depth],currNode)) 360 { 361 addPair(order[_depth],currNode); 362 break; 363 } 364 } 365 currEdgeIts[_depth]==INVALID?--_depth:++_depth; 366 } 280 continue; 281 } 282 else { 283 currPNode=_mapping[_g1.oppositeNode(nodeOfDepth, 284 fstMatchedE)]; 285 edgeItOfDepth=typename G2::IncEdgeIt(_g2,currPNode); 286 } 287 } 288 else { 289 currPNode=_g2.oppositeNode(pairOfNodeOfDepth, 290 edgeItOfDepth); 291 subPair(nodeOfDepth,pairOfNodeOfDepth); 292 ++edgeItOfDepth; 293 } 294 for(; edgeItOfDepth!=INVALID; ++edgeItOfDepth) { 295 const typename G2::Node currNode = 296 _g2.oppositeNode(currPNode, edgeItOfDepth); 297 if(_conn[currNode]>0&&feas<MT>(nodeOfDepth,currNode)) { 298 addPair(nodeOfDepth,currNode); 299 break; 300 } 301 } 302 edgeItOfDepth==INVALID?--_depth:++_depth; 303 } 367 304 return false; 368 305 } 369 306 370 //calc. the lookup table for cut the searchtree 371 void setRNew1tRInOut1t() 372 { 307 //calculate the lookup table for cutting the search tree 308 void initRNew1tRInOut1t() { 373 309 typename G1::template NodeMap<int> tmp(_g1,0); 374 for(unsigned int i=0; i<order.size(); ++i) 375 { 376 tmp[order[i]]=-1; 377 for(typename G1::IncEdgeIt e1(_g1,order[i]); e1!=INVALID; ++e1) 378 { 379 const typename G1::Node currNode=_g1.oppositeNode(order[i],e1); 380 if(tmp[currNode]>0) 381 ++rInOut1t[order[i]]; 382 else if(tmp[currNode]==0) 383 ++rNew1t[order[i]]; 384 } 385 for(typename G1::IncEdgeIt e1(_g1,order[i]); e1!=INVALID; ++e1) 386 { 387 const typename G1::Node currNode=_g1.oppositeNode(order[i],e1); 388 if(tmp[currNode]!=-1) 389 ++tmp[currNode]; 390 } 391 } 310 for(unsigned int i=0; i<_order.size(); ++i) { 311 const typename G1::Node& orderI = _order[i]; 312 tmp[orderI]=-1; 313 for(typename G1::IncEdgeIt e1(_g1,orderI); e1!=INVALID; ++e1) { 314 const typename G1::Node currNode=_g1.oppositeNode(orderI,e1); 315 if(tmp[currNode]>0) 316 ++_rInOut1t[orderI]; 317 else if(tmp[currNode]==0) 318 ++_rNew1t[orderI]; 319 } 320 for(typename G1::IncEdgeIt e1(_g1,orderI); e1!=INVALID; ++e1) { 321 const typename G1::Node currNode=_g1.oppositeNode(orderI,e1); 322 if(tmp[currNode]!=-1) 323 ++tmp[currNode]; 324 } 325 } 392 326 } 393 327 public: … … 400 334 ///\param m \ref concepts::ReadWriteMap "read-write" NodeMap 401 335 ///storing the found mapping. 402 ///\param neq A bool-valued binary functor determinin ing whether a node is336 ///\param neq A bool-valued binary functor determining whether a node is 403 337 ///mappable to another. By default it is an always true operator. 404 Vf2(const G1 &g1, const G2 &g2,M &m, const NEQ &neq = NEQ() ) : 405 _nEq(neq), _conn(g2,0), _mapping(m), order(countNodes(g1)), 406 currEdgeIts(countNodes(g1),INVALID), _g1(g1), _g2(g2), rNew1t(g1,0), 407 rInOut1t(g1,0), _mapping_type(SUBGRAPH) 338 Vf2(const G1 &g1, const G2 &g2, M &m, const NEQ &neq = NEQ() ) : 339 _g1(g1), _g2(g2), _nEq(neq), _depth(0), _mapping(m), 340 _order(countNodes(g1)), _conn(g2,0), 341 _currEdgeIts(countNodes(g1),INVALID), _rNew1t(g1,0), _rInOut1t(g1,0), 342 _mapping_type(SUBGRAPH), _deallocMappingAfterUse(0) 408 343 { 409 _depth=0; 410 setOrder(); 411 setRNew1tRInOut1t(); 344 initOrder(); 345 initRNew1tRInOut1t(); 412 346 for(typename G1::NodeIt n(g1);n!=INVALID;++n) 413 347 m[n]=INVALID; 414 348 } 415 349 350 ///Destructor 351 352 ///Destructor. 353 /// 354 355 ~Vf2(){ 356 if(_deallocMappingAfterUse) 357 delete &_mapping; 358 } 359 416 360 ///Returns the current mapping type 417 361 418 362 ///Returns the current mapping type 419 363 /// 420 Vf2MappingType mappingType() const { return _mapping_type; } 364 MappingType mappingType() const { 365 return _mapping_type; 366 } 421 367 ///Sets mapping type 422 368 … … 425 371 ///The mapping type is set to \ref SUBGRAPH by default. 426 372 /// 427 ///\sa See \ref Vf2MappingType for the possible values. 428 void mappingType(Vf2MappingType m_type) { _mapping_type = m_type; } 373 ///\sa See \ref MappingType for the possible values. 374 void mappingType(MappingType m_type) { 375 _mapping_type = m_type; 376 } 429 377 430 378 ///Finds a mapping 431 379 432 ///It finds a mapping betweenfrom g1 into g2 according to the mapping433 ///type set by \ref mappingType( Vf2MappingType) "mappingType()".380 ///It finds a mapping from g1 into g2 according to the mapping 381 ///type set by \ref mappingType(MappingType) "mappingType()". 434 382 /// 435 383 ///By subsequent calls, it returns all possible mappings one-by-one. … … 437 385 ///\retval true if a mapping is found. 438 386 ///\retval false if there is no (more) mapping. 439 bool find() 440 { 441 switch(_mapping_type) 442 { 443 case SUBGRAPH: 444 return extMatch<SUBGRAPH>(); 445 case INDUCED: 446 return extMatch<INDUCED>(); 447 case ISOMORPH: 448 return extMatch<ISOMORPH>(); 449 default: 450 return false; 451 } 387 bool find() { 388 switch(_mapping_type) { 389 case SUBGRAPH: 390 return extMatch<SUBGRAPH>(); 391 case INDUCED: 392 return extMatch<INDUCED>(); 393 case ISOMORPH: 394 return extMatch<ISOMORPH>(); 395 default: 396 return false; 397 } 452 398 } 453 399 }; 454 400 455 401 template<class G1, class G2> 456 class Vf2WizardBase 457 { 402 class Vf2WizardBase { 458 403 protected: 459 404 typedef G1 Graph1; … … 463 408 const G2 &_g2; 464 409 465 Vf2MappingType _mapping_type;410 MappingType _mapping_type; 466 411 467 412 typedef typename G1::template NodeMap<typename G2::Node> Mapping; 468 413 bool _local_mapping; 469 414 void *_mapping; 470 void createMapping() 471 { 415 void createMapping() { 472 416 _mapping = new Mapping(_g1); 473 417 } 418 419 void *myVf2; //used in Vf2Wizard::find 420 474 421 475 422 typedef bits::vf2::AlwaysEq NodeEq; … … 477 424 478 425 Vf2WizardBase(const G1 &g1,const G2 &g2) 479 : _g1(g1), _g2(g2), _mapping_type(SUBGRAPH), _local_mapping(true) { }426 : _g1(g1), _g2(g2), _mapping_type(SUBGRAPH), _local_mapping(true) { } 480 427 }; 481 428 429 482 430 /// Auxiliary class for the function-type interface of %VF2 algorithm. 483 431 /// 484 432 /// This auxiliary class implements the named parameters of 485 433 /// \ref vf2() "function-type interface" of \ref Vf2 algorithm. 486 434 /// 487 /// \warning This class should only be used through the function \ref vf2().435 /// \warning This class is not to be used directly. 488 436 /// 489 437 /// \tparam TR The traits class that defines various types used by the 490 438 /// algorithm. 491 439 template<class TR> 492 class Vf2Wizard : public TR 493 { 440 class Vf2Wizard : public TR { 494 441 typedef TR Base; 495 442 typedef typename TR::Graph1 Graph1; … … 512 459 Vf2Wizard(const Base &b) : Base(b) {} 513 460 461 ///Copy constructor 462 Vf2Wizard(const Vf2Wizard &b) : Base(b) {} 463 514 464 515 465 template<class T> 516 struct SetMappingBase : public Base 466 struct SetMappingBase : public Base{ 517 467 typedef T Mapping; 518 468 SetMappingBase(const Base &b) : Base(b) {} … … 525 475 ///the map that stores the found embedding. 526 476 template<class T> 527 Vf2Wizard< SetMappingBase<T> > mapping(const T &t) 528 { 477 Vf2Wizard< SetMappingBase<T> > mapping(const T &t) { 529 478 Base::_mapping=reinterpret_cast<void*>(const_cast<T*>(&t)); 530 479 Base::_local_mapping = false; … … 537 486 NodeEq _node_eq; 538 487 SetNodeEqBase(const Base &b, const NE &node_eq) 539 : Base(b), _node_eq(node_eq) {} 488 : Base(b), _node_eq(node_eq){ 489 } 540 490 }; 541 491 … … 550 500 ///always true operator. 551 501 template<class T> 552 Vf2Wizard< SetNodeEqBase<T> > nodeEq(const T &node_eq) 553 { 502 Vf2Wizard< SetNodeEqBase<T> > nodeEq(const T &node_eq) { 554 503 return Vf2Wizard<SetNodeEqBase<T> >(SetNodeEqBase<T>(*this,node_eq)); 555 504 } … … 561 510 ///the node labels defining equivalence relation between them. 562 511 /// 563 ///\param m1 It isarbitrary \ref concepts::ReadMap "readable node map"512 ///\param m1 An arbitrary \ref concepts::ReadMap "readable node map" 564 513 ///of g1. 565 ///\param m2 It isarbitrary \ref concepts::ReadMap "readable node map"514 ///\param m2 An arbitrary \ref concepts::ReadMap "readable node map" 566 515 ///of g2. 567 516 /// … … 569 518 template<class M1, class M2> 570 519 Vf2Wizard< SetNodeEqBase<bits::vf2::MapEq<M1,M2> > > 571 nodeLabels(const M1 &m1,const M2 &m2) 572 { 520 nodeLabels(const M1 &m1,const M2 &m2){ 573 521 return nodeEq(bits::vf2::MapEq<M1,M2>(m1,m2)); 574 522 } … … 582 530 ///The mapping type is set to \ref SUBGRAPH by default. 583 531 /// 584 ///\sa See \ref Vf2MappingType for the possible values. 585 Vf2Wizard<Base> &mappingType(Vf2MappingType m_type) 586 { 532 ///\sa See \ref MappingType for the possible values. 533 Vf2Wizard<Base> &mappingType(MappingType m_type) { 587 534 _mapping_type = m_type; 588 535 return *this; … … 594 541 ///\ref named-templ-param "Named parameter" for setting 595 542 ///the mapping type to \ref INDUCED. 596 Vf2Wizard<Base> &induced() 597 { 543 Vf2Wizard<Base> &induced() { 598 544 _mapping_type = INDUCED; 599 545 return *this; … … 605 551 ///\ref named-templ-param "Named parameter" for setting 606 552 ///the mapping type to \ref ISOMORPH. 607 Vf2Wizard<Base> &iso() 608 { 553 Vf2Wizard<Base> &iso() { 609 554 _mapping_type = ISOMORPH; 610 555 return *this; 611 556 } 612 557 558 613 559 ///Runs VF2 algorithm. 614 560 … … 616 562 /// 617 563 ///\retval true if a mapping is found. 618 ///\retval false if there is no (more) mapping. 619 bool run() 620 { 564 ///\retval false if there is no mapping. 565 bool run(){ 621 566 if(Base::_local_mapping) 622 567 Base::createMapping(); … … 631 576 if(Base::_local_mapping) 632 577 delete reinterpret_cast<Mapping*>(_mapping); 578 579 return ret; 580 } 581 582 ///Get a pointer to the generated Vf2 object. 583 584 ///Gives a pointer to the generated Vf2 object. 585 /// 586 ///\return Pointer to the generated Vf2 object. 587 ///\warning Don't forget to delete the referred Vf2 object after use. 588 Vf2<Graph1, Graph2, Mapping, NodeEq >* getPtrToVf2Object() { 589 if(Base::_local_mapping) 590 Base::createMapping(); 591 Vf2<Graph1, Graph2, Mapping, NodeEq >* ptr = 592 new Vf2<Graph1, Graph2, Mapping, NodeEq> 593 (_g1, _g2, *reinterpret_cast<Mapping*>(_mapping), _node_eq); 594 ptr->mappingType(_mapping_type); 595 if(Base::_local_mapping) 596 ptr->_deallocMappingAfterUse = true; 597 return ptr; 598 } 599 600 ///Counts the number of mappings. 601 602 ///This method counts the number of mappings. 603 /// 604 /// \return The number of mappings. 605 int count() { 606 if(Base::_local_mapping) 607 Base::createMapping(); 608 609 Vf2<Graph1, Graph2, Mapping, NodeEq> 610 alg(_g1, _g2, *reinterpret_cast<Mapping*>(_mapping), _node_eq); 611 if(Base::_local_mapping) 612 alg._deallocMappingAfterUse = true; 613 alg.mappingType(_mapping_type); 614 615 int ret = 0; 616 while(alg.find()) 617 ++ret; 633 618 634 619 return ret; … … 645 630 ///The following examples show how to use these parameters. 646 631 ///\code 647 /// // Find an embedding of graph g into graph h632 /// // Find an embedding of graph g1 into graph g2 648 633 /// ListGraph::NodeMap<ListGraph::Node> m(g); 649 /// vf2(g,h).mapping(m).run(); 650 /// 651 /// // Check whether graphs g and h are isomorphic 652 /// bool is_iso = vf2(g,h).iso().run(); 634 /// vf2(g1,g2).mapping(m).run(); 635 /// 636 /// // Check whether graphs g1 and g2 are isomorphic 637 /// bool is_iso = vf2(g1,g2).iso().run(); 638 /// 639 /// // Count the number of isomorphisms 640 /// int num_isos = vf2(g1,g2).iso().count(); 641 /// 642 /// // Iterate through all the induced subgraph mappings of graph g1 into g2 643 /// auto* myVf2 = vf2(g1,g2).mapping(m).nodeLabels(c1,c2) 644 /// .induced().getPtrToVf2Object(); 645 /// while(myVf2->find()){ 646 /// //process the current mapping m 647 /// } 648 /// delete myVf22; 653 649 ///\endcode 654 ///\warning Don't forget to put the \ref Vf2Wizard::run() "run()" 650 ///\warning Don't forget to put the \ref Vf2Wizard::run() "run()", 651 ///\ref Vf2Wizard::count() "count()" or 652 ///the \ref Vf2Wizard::getPtrToVf2Object() "getPtrToVf2Object()" 655 653 ///to the end of the expression. 656 654 ///\sa Vf2Wizard 657 655 ///\sa Vf2 658 656 template<class G1, class G2> 659 Vf2Wizard<Vf2WizardBase<G1,G2> > vf2(const G1 &g1, const G2 &g2) 660 { 657 Vf2Wizard<Vf2WizardBase<G1,G2> > vf2(const G1 &g1, const G2 &g2) { 661 658 return Vf2Wizard<Vf2WizardBase<G1,G2> >(g1,g2); 662 659 } -
test/vf2_test.cc
r1352 r1406 3 3 * This file is a part of LEMON, a generic C++ optimization library. 4 4 * 5 * Copyright (C) 2015 5 * Copyright (C) 2015-2017 6 6 * EMAXA Kutato-fejleszto Kft. (EMAXA Research Ltd.) 7 7 * … … 17 17 18 18 #include <lemon/vf2.h> 19 #include <lemon/vf2pp.h> 19 20 #include <lemon/concepts/digraph.h> 20 21 #include <lemon/smart_graph.h> … … 153 154 std::stringstream ss(petersen_lgf); 154 155 graphReader(petersen, ss) 155 .nodeMap("col1", petersen_col1)156 .nodeMap("col2", petersen_col2)156 .nodeMap("col1", petersen_col1) 157 .nodeMap("col2", petersen_col2) 157 158 .run(); 158 159 159 160 ss.clear(); 160 161 ss.str(""); 161 ss <<c5_lgf;162 ss << c5_lgf; 162 163 //std::stringstream ss2(c5_lgf); 163 164 graphReader(c5, ss) 164 .nodeMap("col", c5_col)165 .nodeMap("col", c5_col) 165 166 .run(); 166 167 167 168 ss.clear(); 168 169 ss.str(""); 169 ss <<c7_lgf;170 ss << c7_lgf; 170 171 graphReader(c7, ss).run(); 171 172 172 173 ss.clear(); 173 174 ss.str(""); 174 ss <<c10_lgf;175 ss << c10_lgf; 175 176 graphReader(c10, ss).run(); 176 177 177 178 ss.clear(); 178 179 ss.str(""); 179 ss <<p10_lgf;180 ss << p10_lgf; 180 181 graphReader(p10, ss).run(); 181 182 … … 184 185 class EqComparable { 185 186 public: 186 bool operator==(const EqComparable&) { return false; } 187 bool operator==(const EqComparable&) { 188 return false; 189 } 187 190 }; 188 191 … … 190 193 class EqClass { 191 194 public: 192 bool operator()(A, B) { return false; } 195 bool operator()(A, B){ 196 return false; 197 } 193 198 }; 194 199 200 class IntConvertible1 { 201 public: 202 operator int() { 203 return 0; 204 } 205 }; 206 207 class IntConvertible2 { 208 public: 209 operator int() { 210 return 0; 211 } 212 }; 213 195 214 template<class G1,class G2> 196 void checkVf2Compile() 197 { 215 void checkVf2Compile() { 198 216 G1 g; 199 217 G2 h; … … 206 224 succ = vf2(g,h).iso().run(); 207 225 succ = vf2(g,h).mapping(r).run(); 226 227 Vf2<G1,G2,concepts::ReadWriteMap<typename G1::Node, typename G2::Node>, 228 EqClass<typename G1::Node,typename G2::Node> > 229 myVf2(g,h,r,EqClass<typename G1::Node,typename G2::Node>()); 230 myVf2.find(); 231 208 232 succ = vf2(g,h).induced().mapping(r).run(); 209 233 succ = vf2(g,h).iso().mapping(r).run(); 234 210 235 concepts::ReadMap<typename G1::Node, EqComparable> l1; 211 236 concepts::ReadMap<typename G2::Node, EqComparable> l2; … … 215 240 } 216 241 217 void justCompile() 218 { 242 void vf2Compile() { 219 243 checkVf2Compile<concepts::Graph,concepts::Graph>(); 220 244 checkVf2Compile<concepts::Graph,SmartGraph>(); … … 222 246 } 223 247 248 template<class G1,class G2> 249 void checkVf2ppCompile() { 250 G1 g; 251 G2 h; 252 concepts::ReadWriteMap<typename G1::Node, typename G2::Node> r; 253 bool succ; 254 ::lemon::ignore_unused_variable_warning(succ); 255 256 succ = vf2pp(g,h).run(); 257 succ = vf2pp(g,h).induced().run(); 258 succ = vf2pp(g,h).iso().run(); 259 succ = vf2pp(g,h).mapping(r).run(); 260 succ = vf2pp(g,h).induced().mapping(r).run(); 261 succ = vf2pp(g,h).iso().mapping(r).run(); 262 263 concepts::ReadMap<typename G1::Node, int> c1; 264 concepts::ReadMap<typename G2::Node, int> c2; 265 Vf2pp<G1,G2,concepts::ReadWriteMap<typename G1::Node, typename G2::Node>, 266 concepts::ReadMap<typename G1::Node, int>, 267 concepts::ReadMap<typename G2::Node, int> > 268 myVf2pp(g,h,r,c1,c2); 269 myVf2pp.find(); 270 271 succ = vf2pp(g,h).nodeLabels(c1,c2).mapping(r).run(); 272 succ = vf2pp(g,h).nodeLabels(c1,c2).mapping(r).run(); 273 274 concepts::ReadMap<typename G1::Node, char> c1_c; 275 concepts::ReadMap<typename G2::Node, char> c2_c; 276 Vf2pp<G1,G2,concepts::ReadWriteMap<typename G1::Node, typename G2::Node>, 277 concepts::ReadMap<typename G1::Node, char>, 278 concepts::ReadMap<typename G2::Node, char> > 279 myVf2pp_c(g,h,r,c1_c,c2_c); 280 myVf2pp_c.find(); 281 282 succ = vf2pp(g,h).nodeLabels(c1_c,c2_c).mapping(r).run(); 283 succ = vf2pp(g,h).nodeLabels(c1_c,c2_c).mapping(r).run(); 284 285 concepts::ReadMap<typename G1::Node, IntConvertible1> c1_IntConv; 286 concepts::ReadMap<typename G2::Node, IntConvertible2> c2_IntConv; 287 Vf2pp<G1,G2,concepts::ReadWriteMap<typename G1::Node, typename G2::Node>, 288 concepts::ReadMap<typename G1::Node, IntConvertible1>, 289 concepts::ReadMap<typename G2::Node, IntConvertible2> > 290 myVf2pp_IntConv(g,h,r,c1_IntConv,c2_IntConv); 291 myVf2pp_IntConv.find(); 292 293 succ = vf2pp(g,h).nodeLabels(c1_IntConv,c2_IntConv).mapping(r).run(); 294 succ = vf2pp(g,h).nodeLabels(c1_IntConv,c2_IntConv).mapping(r).run(); 295 } 296 297 void vf2ppCompile() { 298 checkVf2ppCompile<concepts::Graph,concepts::Graph>(); 299 checkVf2ppCompile<concepts::Graph,SmartGraph>(); 300 checkVf2ppCompile<SmartGraph,concepts::Graph>(); 301 } 302 224 303 template<class G1, class G2, class I> 225 void checkSub(const G1 &g1, const G2 &g2, const I &i) 226 { 227 { 228 std::set<typename G2::Node> image; 229 for(typename G1::NodeIt n(g1);n!=INVALID;++n) 230 { 231 check(i[n]!=INVALID, "Wrong isomorphism: incomplete mapping."); 232 check(image.count(i[n])==0,"Wrong isomorphism: not injective."); 233 image.insert(i[n]); 234 } 235 } 236 for(typename G1::EdgeIt e(g1);e!=INVALID;++e) 237 check(findEdge(g2,i[g1.u(e)],i[g1.v(e)])!=INVALID, 238 "Wrong isomorphism: missing edge(checkSub)."); 239 } 240 241 template<class G1, class G2, class I> 242 void checkInd(const G1 &g1, const G2 &g2, const I &i) 243 { 304 void checkSubIso(const G1 &g1, const G2 &g2, const I &i) { 244 305 std::set<typename G2::Node> image; 245 for(typename G1::NodeIt n(g1);n!=INVALID;++n) 246 { 306 for (typename G1::NodeIt n(g1);n!=INVALID;++n){ 247 307 check(i[n]!=INVALID, "Wrong isomorphism: incomplete mapping."); 248 308 check(image.count(i[n])==0,"Wrong isomorphism: not injective."); 249 309 image.insert(i[n]); 250 } 251 for(typename G1::NodeIt n(g1); n!=INVALID; ++n) 252 for(typename G1::NodeIt m(g1); m!=INVALID; ++m) 253 if((findEdge(g1,n,m)==INVALID) != (findEdge(g2,i[n],i[m])==INVALID)) 254 { 310 } 311 for (typename G1::EdgeIt e(g1);e!=INVALID;++e) { 312 check(findEdge(g2,i[g1.u(e)],i[g1.v(e)])!=INVALID, 313 "Wrong isomorphism: missing edge(checkSub)."); 314 } 315 } 316 317 template<class G1, class G2, class I> 318 void checkIndIso(const G1 &g1, const G2 &g2, const I &i) { 319 std::set<typename G2::Node> image; 320 for (typename G1::NodeIt n(g1);n!=INVALID;++n) { 321 check(i[n]!=INVALID, "Wrong isomorphism: incomplete mapping."); 322 check(image.count(i[n])==0,"Wrong isomorphism: not injective."); 323 image.insert(i[n]); 324 } 325 for (typename G1::NodeIt n(g1); n!=INVALID; ++n) { 326 for (typename G1::NodeIt m(g1); m!=INVALID; ++m) { 327 if((findEdge(g1,n,m)==INVALID) != (findEdge(g2,i[n],i[m])==INVALID)) { 255 328 std::cout << "Wrong isomorphism: edge mismatch"; 256 329 exit(1); 257 } 258 } 259 260 template<class G1,class G2> 261 int checkSub(const G1 &g1, const G2 &g2) 262 { 330 } 331 } 332 } 333 } 334 335 template<class G1,class G2,class T> 336 bool checkSub(const G1 &g1, const G2 &g2, const T &vf2) { 263 337 typename G1:: template NodeMap<typename G2::Node> iso(g1,INVALID); 264 if(vf2(g1,g2).mapping(iso).run()) 265 { 266 checkSub(g1,g2,iso); 267 return true; 268 } 269 else return false; 270 } 271 272 template<class G1,class G2> 273 int checkInd(const G1 &g1, const G2 &g2) 274 { 338 if (const_cast<T&>(vf2).mapping(iso).run()) { 339 checkSubIso(g1,g2,iso); 340 return true; 341 } 342 return false; 343 } 344 345 template<class G1,class G2,class T> 346 bool checkInd(const G1 &g1, const G2 &g2, const T &vf2) { 275 347 typename G1:: template NodeMap<typename G2::Node> iso(g1,INVALID); 276 if(vf2(g1,g2).induced().mapping(iso).run()) 277 { 278 checkInd(g1,g2,iso); 279 return true; 280 } 281 else return false; 282 } 283 284 template<class G1,class G2> 285 int checkIso(const G1 &g1, const G2 &g2) 286 { 348 if (const_cast<T&>(vf2).induced().mapping(iso).run()) { 349 checkIndIso(g1,g2,iso); 350 return true; 351 } 352 return false; 353 } 354 355 template<class G1,class G2,class T> 356 bool checkIso(const G1 &g1, const G2 &g2, const T &vf2) { 287 357 typename G1:: template NodeMap<typename G2::Node> iso(g1,INVALID); 288 if(vf2(g1,g2).iso().mapping(iso).run()) 289 { 290 check(countNodes(g1)==countNodes(g2), 291 "Wrong iso alg.: they are not isomophic."); 292 checkInd(g1,g2,iso); 293 return true; 294 } 295 else return false; 358 if (const_cast<T&>(vf2).iso().mapping(iso).run()) { 359 check(countNodes(g1)==countNodes(g2), 360 "Wrong iso alg.: they are not isomophic."); 361 checkIndIso(g1,g2,iso); 362 return true; 363 } 364 return false; 296 365 } 297 366 298 367 template<class G1, class G2, class L1, class L2, class I> 299 368 void checkLabel(const G1 &g1, const G2 &, 300 const L1 &l1, const L2 &l2,const I &i) 301 { 302 for(typename G1::NodeIt n(g1);n!=INVALID;++n) 303 { 304 check(l1[n]==l2[i[n]],"Wrong isomorphism: label mismatch."); 305 } 369 const L1 &l1, const L2 &l2, const I &i) { 370 for (typename G1::NodeIt n(g1);n!=INVALID;++n) { 371 check(l1[n]==l2[i[n]],"Wrong isomorphism: label mismatch."); 372 } 373 } 374 375 template<class G1,class G2,class L1,class L2,class T> 376 bool checkSub(const G1 &g1, const G2 &g2, const L1 &l1, const L2 &l2, const T &vf2) { 377 typename G1:: template NodeMap<typename G2::Node> iso(g1,INVALID); 378 if (const_cast<T&>(vf2).nodeLabels(l1,l2).mapping(iso).run()){ 379 checkSubIso(g1,g2,iso); 380 checkLabel(g1,g2,l1,l2,iso); 381 return true; 382 } 383 return false; 384 } 385 386 template<class G1,class G2> 387 void checkSub(const G1 &g1, const G2 &g2, bool expected, const char* msg) { 388 check(checkSub(g1,g2,vf2(g1,g2)) == expected, msg); 389 check(checkSub(g1,g2,vf2pp(g1,g2)) == expected, msg); 390 } 391 392 template<class G1,class G2> 393 void checkInd(const G1 &g1, const G2 &g2, bool expected, const char* msg) { 394 check(checkInd(g1,g2,vf2(g1,g2)) == expected, msg); 395 check(checkInd(g1,g2,vf2pp(g1,g2)) == expected, msg); 396 } 397 398 template<class G1,class G2> 399 void checkIso(const G1 &g1, const G2 &g2, bool expected, const char* msg) { 400 check(checkIso(g1,g2,vf2(g1,g2)) == expected, msg); 401 check(checkIso(g1,g2,vf2pp(g1,g2)) == expected, msg); 306 402 } 307 403 308 404 template<class G1,class G2,class L1,class L2> 309 int checkSub(const G1 &g1, const G2 &g2, const L1 &l1, const L2 &l2) 310 { 311 typename G1:: template NodeMap<typename G2::Node> iso(g1,INVALID); 312 if(vf2(g1,g2).nodeLabels(l1,l2).mapping(iso).run()) 313 { 314 checkSub(g1,g2,iso); 315 checkLabel(g1,g2,l1,l2,iso); 316 return true; 317 } 318 else return false; 405 void checkSub(const G1 &g1, const G2 &g2, const L1 &l1, const L2 &l2, bool expected, const char* msg) { 406 check(checkSub(g1,g2,l1,l2,vf2(g1,g2)) == expected, msg); 407 check(checkSub(g1,g2,l1,l2,vf2pp(g1,g2)) == expected, msg); 319 408 } 320 409 321 410 int main() { 322 411 make_graphs(); 323 check(checkSub(c5,petersen), "There should exist a C5->Petersen mapping."); 324 check(!checkSub(c7,petersen), 325 "There should not exist a C7->Petersen mapping."); 326 check(checkSub(p10,petersen), "There should exist a P10->Petersen mapping."); 327 check(!checkSub(c10,petersen), 328 "There should not exist a C10->Petersen mapping."); 329 check(checkSub(petersen,petersen), 330 "There should exist a Petersen->Petersen mapping."); 331 332 check(checkInd(c5,petersen), 333 "There should exist a C5->Petersen spanned mapping."); 334 check(!checkInd(c7,petersen), 335 "There should exist a C7->Petersen spanned mapping."); 336 check(!checkInd(p10,petersen), 337 "There should not exist a P10->Petersen spanned mapping."); 338 check(!checkInd(c10,petersen), 339 "There should not exist a C10->Petersen spanned mapping."); 340 check(checkInd(petersen,petersen), 412 413 checkSub(c5,petersen,true, 414 "There should exist a C5->Petersen mapping."); 415 checkSub(c7,petersen,false, 416 "There should not exist a C7->Petersen mapping."); 417 checkSub(p10,petersen,true, 418 "There should exist a P10->Petersen mapping."); 419 checkSub(c10,petersen,false, 420 "There should not exist a C10->Petersen mapping."); 421 checkSub(petersen,petersen,true, 422 "There should exist a Petersen->Petersen mapping."); 423 424 checkInd(c5,petersen,true, 425 "There should exist a C5->Petersen spanned mapping."); 426 checkInd(c7,petersen,false, 427 "There should exist a C7->Petersen spanned mapping."); 428 checkInd(p10,petersen,false, 429 "There should not exist a P10->Petersen spanned mapping."); 430 checkInd(c10,petersen,false, 431 "There should not exist a C10->Petersen spanned mapping."); 432 checkInd(petersen,petersen,true, 341 433 "There should exist a Petersen->Petersen spanned mapping."); 342 434 343 check(!checkSub(petersen,c10), 344 "There should not exist a Petersen->C10 mapping."); 345 check(checkSub(p10,c10), 346 "There should exist a P10->C10 mapping."); 347 check(!checkInd(p10,c10), 348 "There should not exist a P10->C10 spanned mapping."); 349 check(!checkSub(c10,p10), 350 "There should not exist a C10->P10 mapping."); 351 352 check(!checkIso(p10,c10), 353 "P10 and C10 are not isomorphic."); 354 check(checkIso(c10,c10), 355 "C10 and C10 are isomorphic."); 356 357 check(!vf2(p10,c10).iso().run(), 358 "P10 and C10 are not isomorphic."); 359 check(vf2(c10,c10).iso().run(), 360 "C10 and C10 are isomorphic."); 361 362 check(!checkSub(c5,petersen,c5_col,petersen_col1), 363 "There should exist a C5->Petersen mapping."); 364 check(checkSub(c5,petersen,c5_col,petersen_col2), 365 "There should exist a C5->Petersen mapping."); 366 } 435 checkSub(petersen,c10,false, 436 "There should not exist a Petersen->C10 mapping."); 437 checkSub(p10,c10,true, 438 "There should exist a P10->C10 mapping."); 439 checkInd(p10,c10,false, 440 "There should not exist a P10->C10 spanned mapping."); 441 checkSub(c10,p10,false, 442 "There should not exist a C10->P10 mapping."); 443 444 checkIso(p10,c10,false, 445 "P10 and C10 are not isomorphic."); 446 checkIso(c10,c10,true, 447 "C10 and C10 are isomorphic."); 448 449 checkSub(c5,petersen,c5_col,petersen_col1,false, 450 "There should exist a C5->Petersen mapping."); 451 checkSub(c5,petersen,c5_col,petersen_col2,true, 452 "There should exist a C5->Petersen mapping."); 453 454 check(!vf2(p10,c10).iso().run(), "P10 and C10 are not isomorphic."); 455 check(!vf2pp(p10,c10).iso().run(), "P10 and C10 are not isomorphic."); 456 457 check(vf2(c10,c10).iso().run(), "C10 and C10 are isomorphic."); 458 check(vf2pp(c10,c10).iso().run(), "C10 and C10 are isomorphic."); 459 }
Note: See TracChangeset
for help on using the changeset viewer.