# HG changeset patch
# User Peter Kovacs <kpeter@inf.elte.hu>
# Date 1507339129 -7200
# Node ID 76349d8212d3953003885aabcef528a29de3d42d
# Parent 120b9031eadac340f6078b85d9bcb43c9342bc93
Improve and unify comments and API docs of VF2 algorithms (#597)
diff --git a/lemon/bits/vf2_internals.h b/lemon/bits/vf2_internals.h
|
a
|
b
|
|
| 26 | 26 | namespace lemon { |
| 27 | 27 | ///\ingroup graph_isomorphism |
| 28 | 28 | ///The \ref Vf2 "VF2" algorithm is capable of finding different kind of |
| 29 | | ///embeddings, this enum specifies its type. |
| | 29 | ///graph embeddings, this enum specifies their types. |
| 30 | 30 | /// |
| 31 | 31 | ///See \ref graph_isomorphism for a more detailed description. |
| 32 | 32 | enum MappingType { |
| … |
… |
|
| 35 | 35 | /// Induced subgraph isomorphism |
| 36 | 36 | INDUCED = 1, |
| 37 | 37 | /// Graph isomorphism |
| 38 | | |
| 39 | | /// If the two graph has the same number of nodes, than it is |
| | 38 | /// |
| | 39 | /// If the two graphs have the same number of nodes, than it is |
| 40 | 40 | /// equivalent to \ref INDUCED, and if they also have the same |
| 41 | 41 | /// number of edges, then it is also equivalent to \ref SUBGRAPH. |
| 42 | 42 | /// |
| 43 | | /// However, using this setting is faster than the other two |
| 44 | | /// options. |
| | 43 | /// However, using this setting is faster than the other two options. |
| 45 | 44 | ISOMORPH = 2 |
| 46 | 45 | }; |
| 47 | 46 | } |
diff --git a/lemon/vf2.h b/lemon/vf2.h
|
a
|
b
|
|
| 53 | 53 | } |
| 54 | 54 | }; |
| 55 | 55 | |
| 56 | | |
| 57 | | |
| 58 | 56 | template <class G> |
| 59 | 57 | class DfsLeaveOrder : public DfsVisitor<G> { |
| 60 | 58 | const G &_g; |
| … |
… |
|
| 93 | 91 | /// |
| 94 | 92 | ///There is also a \ref vf2() "function-type interface" called \ref vf2() |
| 95 | 93 | ///for the %VF2 algorithm, which is probably more convenient in most |
| 96 | | ///use-cases. |
| | 94 | ///use cases. |
| 97 | 95 | /// |
| 98 | 96 | ///\tparam G1 The type of the graph to be embedded. |
| 99 | 97 | ///The default type is \ref ListDigraph. |
| … |
… |
|
| 102 | 100 | ///\tparam M The type of the NodeMap storing the mapping. |
| 103 | 101 | ///By default, it is G1::NodeMap<G2::Node> |
| 104 | 102 | ///\tparam NEQ A bool-valued binary functor determinining whether a node is |
| 105 | | ///mappable to another. By default it is an always true operator. |
| | 103 | ///mappable to another. By default, it is an always-true operator. |
| 106 | 104 | /// |
| 107 | 105 | ///\sa vf2() |
| 108 | 106 | #ifdef DOXYGEN |
| … |
… |
|
| 116 | 114 | class Vf2 { |
| 117 | 115 | //Current depth in the DFS tree. |
| 118 | 116 | int _depth; |
| | 117 | |
| 119 | 118 | //Functor with bool operator()(G1::Node,G2::Node), which returns 1 |
| 120 | | //ifff the two nodes are equivalent. |
| | 119 | //if and only if the two nodes are equivalent |
| 121 | 120 | NEQ _nEq; |
| 122 | 121 | |
| | 122 | //_conn[v2] = number of covered neighbours of v2 |
| 123 | 123 | typename G2::template NodeMap<int> _conn; |
| 124 | | //Current mapping. We index it by the nodes of g1, and match[v] is |
| 125 | | //a node of g2. |
| | 124 | |
| | 125 | //The current mapping. _mapping[v1]=v2 iff v1 has been mapped to v2, |
| | 126 | //where v1 is a node of G1 and v2 is a node of G2 |
| 126 | 127 | M &_mapping; |
| 127 | | //order[i] is the node of g1, for which we search a pair if depth=i |
| | 128 | |
| | 129 | //order[i] is the node of g1 for which a pair is searched if depth=i |
| 128 | 130 | 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 | |
| | 132 | //currEdgeIts[i] is the last used edge iterator in the i-th |
| | 133 | //depth to find a pair for node order[i] |
| 131 | 134 | std::vector<typename G2::IncEdgeIt> currEdgeIts; |
| 132 | | //The small graph. |
| | 135 | |
| | 136 | //The graph to be embedded |
| 133 | 137 | const G1 &_g1; |
| 134 | | //The large graph. |
| | 138 | |
| | 139 | //The graph into which g1 is to be embedded |
| 135 | 140 | const G2 &_g2; |
| | 141 | |
| 136 | 142 | //lookup tables for cutting the searchtree |
| 137 | 143 | typename G1::template NodeMap<int> rNew1t,rInOut1t; |
| 138 | 144 | |
| … |
… |
|
| 242 | 248 | template<MappingType MT> |
| 243 | 249 | bool extMatch() { |
| 244 | 250 | while(_depth>=0) { |
| 245 | | //there are not nodes in g1, which has not pair in g2. |
| 246 | 251 | if(_depth==static_cast<int>(order.size())) { |
| | 252 | //all nodes of g1 are mapped to nodes of g2 |
| 247 | 253 | --_depth; |
| 248 | 254 | return true; |
| 249 | 255 | } |
| 250 | 256 | typename G1::Node& nodeOfDepth = order[_depth]; |
| 251 | 257 | const typename G2::Node& pairOfNodeOfDepth = _mapping[nodeOfDepth]; |
| 252 | 258 | typename G2::IncEdgeIt &edgeItOfDepth = currEdgeIts[_depth]; |
| 253 | | //the node of g2, which neighbours are the candidates for |
| | 259 | //the node of g2 whose neighbours are the candidates for |
| 254 | 260 | //the pair of nodeOfDepth |
| 255 | 261 | typename G2::Node currPNode; |
| 256 | 262 | if(edgeItOfDepth==INVALID) { |
| 257 | 263 | typename G1::IncEdgeIt fstMatchedE(_g1,nodeOfDepth); |
| 258 | | //if pairOfNodeOfDepth!=INVALID, we dont use |
| 259 | | //fstMatchedE |
| 260 | | if(pairOfNodeOfDepth==INVALID) |
| | 264 | //if pairOfNodeOfDepth!=INVALID, we don't use fstMatchedE |
| | 265 | if(pairOfNodeOfDepth==INVALID) { |
| 261 | 266 | for(; fstMatchedE!=INVALID && |
| 262 | 267 | _mapping[_g1.oppositeNode(nodeOfDepth, |
| 263 | 268 | fstMatchedE)]==INVALID; |
| 264 | 269 | ++fstMatchedE) ; //find fstMatchedE |
| | 270 | } |
| 265 | 271 | 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 |
| | 272 | //We found no covered neighbours, this means that |
| | 273 | //the graph is not connected (or _depth==0). Each |
| | 274 | //uncovered (and there are some other properties due |
| 269 | 275 | //to the spec. problem types) node of g2 is |
| 270 | | //candidate. We can read the iterator of the last |
| | 276 | //candidate. We can read the iterator of the last |
| 271 | 277 | //tried node from the match if it is not the first |
| 272 | | //try(match[nodeOfDepth]!=INVALID) |
| | 278 | //try (match[nodeOfDepth]!=INVALID) |
| 273 | 279 | typename G2::NodeIt n2(_g2); |
| 274 | 280 | //if it's not the first try |
| 275 | 281 | if(pairOfNodeOfDepth!=INVALID) { |
| … |
… |
|
| 317 | 323 | return false; |
| 318 | 324 | } |
| 319 | 325 | |
| 320 | | //calc. the lookup table for cut the searchtree |
| | 326 | //calculate the lookup table for cutting the search tree |
| 321 | 327 | void setRNew1tRInOut1t() { |
| 322 | 328 | typename G1::template NodeMap<int> tmp(_g1,0); |
| 323 | 329 | for(unsigned int i=0; i<order.size(); ++i) { |
| … |
… |
|
| 351 | 357 | Vf2(const G1 &g1, const G2 &g2, M &m, const NEQ &neq = NEQ() ) : |
| 352 | 358 | _nEq(neq), _conn(g2,0), _mapping(m), order(countNodes(g1)), |
| 353 | 359 | currEdgeIts(countNodes(g1),INVALID), _g1(g1), _g2(g2), rNew1t(g1,0), |
| 354 | | rInOut1t(g1,0), _mapping_type(SUBGRAPH), _deallocMappingAfterUse(0) { |
| | 360 | rInOut1t(g1,0), _mapping_type(SUBGRAPH), _deallocMappingAfterUse(0) |
| | 361 | { |
| 355 | 362 | _depth=0; |
| 356 | 363 | setOrder(); |
| 357 | 364 | setRNew1tRInOut1t(); |
| … |
… |
|
| 440 | 447 | |
| 441 | 448 | |
| 442 | 449 | /// Auxiliary class for the function-type interface of %VF2 algorithm. |
| 443 | | |
| | 450 | /// |
| 444 | 451 | /// This auxiliary class implements the named parameters of |
| 445 | 452 | /// \ref vf2() "function-type interface" of \ref Vf2 algorithm. |
| 446 | 453 | /// |
| 447 | | /// \warning This class should only be used through the function \ref vf2(). |
| | 454 | /// \warning This class is not to be used directly. |
| 448 | 455 | /// |
| 449 | 456 | /// \tparam TR The traits class that defines various types used by the |
| 450 | 457 | /// algorithm. |
| … |
… |
|
| 465 | 472 | |
| 466 | 473 | public: |
| 467 | 474 | ///Constructor |
| 468 | | Vf2Wizard(const Graph1 &g1,const Graph2 &g2) : Base(g1,g2) { |
| 469 | | } |
| | 475 | Vf2Wizard(const Graph1 &g1,const Graph2 &g2) : Base(g1,g2) {} |
| 470 | 476 | |
| 471 | 477 | ///Copy constructor |
| 472 | | Vf2Wizard(const Base &b) : Base(b) { } |
| | 478 | Vf2Wizard(const Base &b) : Base(b) {} |
| 473 | 479 | |
| 474 | 480 | ///Copy constructor |
| 475 | 481 | Vf2Wizard(const Vf2Wizard &b) : Base(b) {} |
diff --git a/lemon/vf2pp.h b/lemon/vf2pp.h
|
a
|
b
|
|
| 75 | 75 | /// |
| 76 | 76 | ///There is also a \ref vf2pp() "function-type interface" called |
| 77 | 77 | ///\ref vf2pp() for the %VF2 Plus Plus algorithm, which is probably |
| 78 | | ///more convenient in most use-cases. |
| | 78 | ///more convenient in most use cases. |
| 79 | 79 | /// |
| 80 | 80 | ///\tparam G1 The type of the graph to be embedded. |
| 81 | 81 | ///The default type is \ref ListDigraph. |
| … |
… |
|
| 96 | 96 | #else |
| 97 | 97 | template<class G1=ListDigraph, |
| 98 | 98 | class G2=ListDigraph, |
| 99 | | class M = typename G1::template NodeMap<G2::Node>,//the mapping |
| 100 | | //labels of G1,the labels are the numbers {0,1,2,..,K-1}, |
| 101 | | //where K is the number of different labels |
| | 99 | class M = typename G1::template NodeMap<G2::Node>, |
| 102 | 100 | class M1 = typename G1::template NodeMap<int>, |
| 103 | | //labels of G2, ... |
| 104 | 101 | class M2 = typename G2::template NodeMap<int> > |
| 105 | 102 | #endif |
| 106 | 103 | class Vf2pp { |
| … |
… |
|
| 114 | 111 | //where v1 is a node of G1 and v2 is a node of G2 |
| 115 | 112 | M &_mapping; |
| 116 | 113 | |
| 117 | | //order[i] is a node of g1, for which a pair is searched if depth=i |
| | 114 | //order[i] is a node of g1 for which a pair is searched if depth=i |
| 118 | 115 | std::vector<typename G1::Node> order; |
| 119 | 116 | |
| 120 | | //currEdgeIts[i] is the last used edge iterator in the ith |
| | 117 | //currEdgeIts[i] is the last used edge iterator in the i-th |
| 121 | 118 | //depth to find a pair for node order[i] |
| 122 | 119 | std::vector<typename G2::IncEdgeIt> currEdgeIts; |
| 123 | | |
| 124 | | //The small graph. |
| | 120 | |
| | 121 | //The graph to be embedded |
| 125 | 122 | const G1 &_g1; |
| 126 | 123 | |
| 127 | | //The large graph. |
| | 124 | //The graph into which g1 is to be embedded |
| 128 | 125 | const G2 &_g2; |
| 129 | 126 | |
| 130 | 127 | //rNewLabels1[v] is a pair of form |
| … |
… |
|
| 137 | 134 | typename G1::template NodeMap<std::vector<std::pair<int,int> > > |
| 138 | 135 | rInOutLabels1; |
| 139 | 136 | |
| 140 | | //_intLabels1[v]==i means that vertex v has the i label in |
| 141 | | //_g1 (i is in {0,1,2,..,K-1}, where K is the number of diff. labels) |
| | 137 | //_intLabels1[v]==i means that node v has label i in _g1 |
| | 138 | //(i is in {0,1,2,..,K-1}, where K is the number of diff. labels) |
| 142 | 139 | M1 &_intLabels1; |
| 143 | 140 | |
| 144 | | //_intLabels2[v]==i means that vertex v has the i label in |
| 145 | | //_g2 (i is in {0,1,2,..,K-1}, where K is the number of diff. labels) |
| | 141 | //_intLabels2[v]==i means that node v has label i in _g2 |
| | 142 | //(i is in {0,1,2,..,K-1}, where K is the number of diff. labels) |
| 146 | 143 | M2 &_intLabels2; |
| 147 | 144 | |
| 148 | 145 | //largest label |
| 149 | 146 | const int maxLabel; |
| 150 | 147 | |
| 151 | 148 | //lookup tables for manipulating with label class cardinalities |
| 152 | | //after use they have to be reset to 0..0 |
| | 149 | //(after use they have to be reset to 0..0) |
| 153 | 150 | std::vector<int> labelTmp1,labelTmp2; |
| 154 | 151 | |
| 155 | 152 | MappingType _mapping_type; |
| 156 | 153 | |
| 157 | | //indicates whether the mapping or the labels must be deleted in the ctor |
| | 154 | //indicates whether the mapping or the labels must be deleted in the destructor |
| 158 | 155 | bool _deallocMappingAfterUse,_deallocLabelsAfterUse; |
| 159 | 156 | |
| 160 | 157 | |
| … |
… |
|
| 286 | 283 | return isIso&&cutByLabels<MT>(n1,n2); |
| 287 | 284 | } |
| 288 | 285 | |
| 289 | | |
| 290 | | //matches n1 and n2 |
| | 286 | //maps n1 to n2 |
| 291 | 287 | void addPair(const typename G1::Node n1,const typename G2::Node n2) { |
| 292 | 288 | _conn[n2]=-1; |
| 293 | 289 | _mapping.set(n1,n2); |
| … |
… |
|
| 298 | 294 | } |
| 299 | 295 | } |
| 300 | 296 | |
| 301 | | |
| 302 | | //dematches n1 and n2 |
| | 297 | //removes mapping of n1 to n2 |
| 303 | 298 | void subPair(const typename G1::Node n1,const typename G2::Node n2) { |
| 304 | 299 | _conn[n2]=0; |
| 305 | 300 | _mapping.set(n1,INVALID); |
| … |
… |
|
| 312 | 307 | } |
| 313 | 308 | } |
| 314 | 309 | |
| 315 | | |
| 316 | 310 | void processBFSLevel(typename G1::Node source,unsigned int& orderIndex, |
| 317 | 311 | typename G1::template NodeMap<int>& dm1, |
| 318 | 312 | typename G1::template NodeMap<bool>& added) { |
| … |
… |
|
| 364 | 358 | for(typename G2::NodeIt n2(_g2); n2!=INVALID; ++n2) |
| 365 | 359 | ++labelTmp1[_intLabels2[n2]]; |
| 366 | 360 | |
| 367 | | // OutDegMap<G1> dm1(_g1); |
| 368 | 361 | typename G1::template NodeMap<int> dm1(_g1,0); |
| 369 | 362 | for(typename G1::EdgeIt e(_g1); e!=INVALID; ++e) { |
| 370 | 363 | ++dm1[_g1.u(e)]; |
| … |
… |
|
| 397 | 390 | template<MappingType MT> |
| 398 | 391 | bool extMatch(){ |
| 399 | 392 | while(_depth>=0) { |
| 400 | | //there is no node in g1, which has not pair in g2. |
| 401 | 393 | if(_depth==static_cast<int>(order.size())) { |
| | 394 | //all nodes of g1 are mapped to nodes of g2 |
| 402 | 395 | --_depth; |
| 403 | 396 | return true; |
| 404 | 397 | } |
| 405 | 398 | typename G1::Node& nodeOfDepth = order[_depth]; |
| 406 | 399 | const typename G2::Node& pairOfNodeOfDepth = _mapping[nodeOfDepth]; |
| 407 | 400 | typename G2::IncEdgeIt &edgeItOfDepth = currEdgeIts[_depth]; |
| 408 | | //the node of g2, which neighbours are the candidates for |
| | 401 | //the node of g2 whose neighbours are the candidates for |
| 409 | 402 | //the pair of order[_depth] |
| 410 | 403 | typename G2::Node currPNode; |
| 411 | 404 | if(edgeItOfDepth==INVALID){ |
| 412 | 405 | typename G1::IncEdgeIt fstMatchedE(_g1,nodeOfDepth); |
| 413 | | //if _mapping[order[_depth]]!=INVALID, we dont need |
| 414 | | //fstMatchedE |
| 415 | | if(pairOfNodeOfDepth==INVALID) |
| | 406 | //if _mapping[order[_depth]]!=INVALID, we don't need fstMatchedE |
| | 407 | if(pairOfNodeOfDepth==INVALID) { |
| 416 | 408 | for(; fstMatchedE!=INVALID && |
| 417 | 409 | _mapping[_g1.oppositeNode(nodeOfDepth, |
| 418 | 410 | fstMatchedE)]==INVALID; |
| 419 | 411 | ++fstMatchedE); //find fstMatchedE, it could be preprocessed |
| | 412 | } |
| 420 | 413 | if(fstMatchedE==INVALID||pairOfNodeOfDepth!=INVALID) { |
| 421 | | //We found no covered neighbours, this means |
| 422 | | //the graph is not connected(or _depth==0). Each |
| 423 | | //uncovered(and there are some other properties due |
| | 414 | //We found no covered neighbours, this means that |
| | 415 | //the graph is not connected (or _depth==0). Each |
| | 416 | //uncovered (and there are some other properties due |
| 424 | 417 | //to the spec. problem types) node of g2 is |
| 425 | | //candidate. We can read the iterator of the last |
| | 418 | //candidate. We can read the iterator of the last |
| 426 | 419 | //tried node from the match if it is not the first |
| 427 | | //try(match[nodeOfDepth]!=INVALID) |
| | 420 | //try (match[nodeOfDepth]!=INVALID) |
| 428 | 421 | typename G2::NodeIt n2(_g2); |
| 429 | 422 | //if it's not the first try |
| 430 | 423 | if(pairOfNodeOfDepth!=INVALID) { |
| … |
… |
|
| 447 | 440 | --_depth; |
| 448 | 441 | continue; |
| 449 | 442 | } |
| 450 | | else{ |
| | 443 | else { |
| 451 | 444 | currPNode=_mapping[_g1.oppositeNode(nodeOfDepth, |
| 452 | 445 | fstMatchedE)]; |
| 453 | 446 | edgeItOfDepth=typename G2::IncEdgeIt(_g2,currPNode); |
| 454 | 447 | } |
| 455 | 448 | } |
| 456 | | else{ |
| | 449 | else { |
| 457 | 450 | currPNode=_g2.oppositeNode(pairOfNodeOfDepth, |
| 458 | 451 | edgeItOfDepth); |
| 459 | 452 | subPair(nodeOfDepth,pairOfNodeOfDepth); |
| … |
… |
|
| 472 | 465 | return false; |
| 473 | 466 | } |
| 474 | 467 | |
| 475 | | //calc. the lookup table for cutting the searchtree |
| | 468 | //calculate the lookup table for cutting the search tree |
| 476 | 469 | void setRNew1tRInOut1t(){ |
| 477 | 470 | typename G1::template NodeMap<int> tmp(_g1,0); |
| 478 | 471 | for(unsigned int i=0; i<order.size(); ++i) { |
| … |
… |
|
| 675 | 668 | |
| 676 | 669 | public: |
| 677 | 670 | ///Constructor |
| 678 | | Vf2ppWizard(const Graph1 &g1,const Graph2 &g2) : Base(g1,g2) { } |
| | 671 | Vf2ppWizard(const Graph1 &g1,const Graph2 &g2) : Base(g1,g2) {} |
| 679 | 672 | |
| 680 | 673 | ///Copy constructor |
| 681 | 674 | Vf2ppWizard(const Base &b) : Base(b) {} |
| … |
… |
|
| 684 | 677 | template<typename T> |
| 685 | 678 | struct SetMappingBase : public Base { |
| 686 | 679 | typedef T Mapping; |
| 687 | | SetMappingBase(const Base &b) : Base(b) { } |
| | 680 | SetMappingBase(const Base &b) : Base(b) {} |
| 688 | 681 | }; |
| 689 | 682 | |
| 690 | 683 | ///\brief \ref named-templ-param "Named parameter" for setting |
| … |
… |
|
| 713 | 706 | ///the node labels. |
| 714 | 707 | /// |
| 715 | 708 | ///\param nodeLabels1 A \ref concepts::ReadMap "readable node map" |
| 716 | | ///of g1 with integer value. In case of K different labels, the labels |
| 717 | | ///must be the {0,1,..,K-1} numbers. |
| | 709 | ///of g1 with integer values. In case of K different labels, the labels |
| | 710 | ///must be the numbers {0,1,..,K-1}. |
| 718 | 711 | ///\param nodeLabels2 A \ref concepts::ReadMap "readable node map" |
| 719 | | ///of g2 with integer value. In case of K different labels, the labels |
| 720 | | ///must be the {0,1,..,K-1} numbers. |
| | 712 | ///of g2 with integer values. In case of K different labels, the labels |
| | 713 | ///must be the numbers {0,1,..,K-1}. |
| 721 | 714 | template<typename NL1, typename NL2> |
| 722 | 715 | Vf2ppWizard< SetNodeLabelsBase<NL1,NL2> > |
| 723 | 716 | nodeLabels(const NL1 &nodeLabels1, const NL2 &nodeLabels2) { |
| … |
… |
|
| 877 | 870 | /// int num_isos = vf2pp(g1,g2).iso().count(); |
| 878 | 871 | /// |
| 879 | 872 | /// // Iterate through all the induced subgraph mappings |
| 880 | | /// //of graph g1 into g2 using the labels c1 and c2 |
| | 873 | /// // of graph g1 into g2 using the labels c1 and c2 |
| 881 | 874 | /// auto* myVf2pp = vf2pp(g1,g2).mapping(m).nodeLabels(c1,c2) |
| 882 | 875 | /// .induced().getPtrToVf2Object(); |
| 883 | 876 | /// while(myVf2pp->find()){ |