Ticket #597: 3feba0ea1bda.patch
File 3feba0ea1bda.patch, 76.6 KB (added by , 7 years ago) |
---|
-
new file lemon/bits/vf2_internals.h
# HG changeset patch # User Peter Madarasi <madarasip@caesar.elte.hu> # Date 1505822900 -7200 # Tue Sep 19 14:08:20 2017 +0200 # Node ID 3feba0ea1bdac112e99996191ebe7760d5480f1b # Parent bc571f16e1e998989a2d6357b304c78dd27d5fd7 Vf2 improvements and Vf2pp implementation (#597) diff --git a/lemon/bits/vf2_internals.h b/lemon/bits/vf2_internals.h new file mode 100644
- + 1 /* -*- mode: C++; indent-tabs-mode: nil; -*- 2 * 3 * This file is a part of LEMON, a generic C++ optimization library. 4 * 5 * Copyright (C) 2015-2017 6 * EMAXA Kutato-fejleszto Kft. (EMAXA Research Ltd.) 7 * 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. 11 * 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 14 * purpose. 15 * 16 */ 17 18 #ifndef VF2_INTERNALS_H 19 #define VF2_INTERNALS_H 20 21 22 ///\ingroup graph_properties 23 ///\file 24 ///\brief Mapping types for graph matching algorithms. 25 26 namespace lemon { 27 ///\ingroup graph_isomorphism 28 ///The \ref Vf2 "VF2" algorithm is capable of finding different kind of 29 ///embeddings, this enum specifies its type. 30 /// 31 ///See \ref graph_isomorphism for a more detailed description. 32 enum MappingType { 33 /// Subgraph isomorphism 34 SUBGRAPH = 0, 35 /// Induced subgraph isomorphism 36 INDUCED = 1, 37 /// Graph isomorphism 38 39 /// If the two graph has the same number of nodes, than it is 40 /// equivalent to \ref INDUCED, and if they also have the same 41 /// number of edges, then it is also equivalent to \ref SUBGRAPH. 42 /// 43 /// However, using this setting is faster than the other two 44 /// options. 45 ISOMORPH = 2 46 }; 47 } 48 #endif -
lemon/vf2.h
diff --git a/lemon/vf2.h b/lemon/vf2.h
a b 2 2 * 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 * 8 8 * Permission to use, modify and distribute this software is granted … … 26 26 #include <lemon/concepts/graph.h> 27 27 #include <lemon/dfs.h> 28 28 #include <lemon/bfs.h> 29 #include <lemon/bits/vf2_internals.h> 29 30 30 31 #include <vector> 31 #include <set>32 32 33 namespace lemon 34 { 35 namespace bits 36 { 37 namespace vf2 38 { 39 class AlwaysEq 40 { 33 namespace lemon { 34 namespace bits { 35 namespace vf2 { 36 37 class AlwaysEq { 41 38 public: 42 39 template<class T1, class T2> 43 bool operator()(T1, T2) const 44 { 40 bool operator()(T1&, T2&) const { 45 41 return true; 46 42 } 47 43 }; 48 44 49 45 template<class M1, class M2> 50 class MapEq 51 { 46 class MapEq{ 52 47 const M1 &_m1; 53 48 const M2 &_m2; 54 49 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 { 50 MapEq(const M1 &m1, const M2 &m2) : _m1(m1), _m2(m2) { } 51 bool operator()(typename M1::Key k1, typename M2::Key k2) const { 58 52 return _m1[k1] == _m2[k2]; 59 53 } 60 54 }; 61 55 56 57 62 58 template <class G> 63 class DfsLeaveOrder : public DfsVisitor<G> 64 { 59 class DfsLeaveOrder : public DfsVisitor<G> { 65 60 const G &_g; 66 61 std::vector<typename G::Node> &_order; 67 62 int i; 68 63 public: 69 64 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 { 65 : i(countNodes(g)), _g(g), _order(order) { } 66 void leave(const typename G::Node &node) { 74 67 _order[--i]=node; 75 68 } 76 69 }; 77 70 78 71 template <class G> 79 class BfsLeaveOrder : public BfsVisitor<G> 80 { 72 class BfsLeaveOrder : public BfsVisitor<G> { 81 73 int i; 82 74 const G &_g; 83 75 std::vector<typename G::Node> &_order; 84 76 public: 85 77 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 { 78 : i(0), _g(g), _order(order){ 79 } 80 void process(const typename G::Node &node) { 90 81 _order[i++]=node; 91 82 } 92 83 }; 93 84 } 94 85 } 95 86 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 87 119 88 ///%VF2 algorithm class. 120 89 … … 144 113 class M = typename G1::template NodeMap<G2::Node>, 145 114 class NEQ = bits::vf2::AlwaysEq > 146 115 #endif 147 class Vf2 148 { 116 class Vf2 { 149 117 //Current depth in the DFS tree. 150 118 int _depth; 151 119 //Functor with bool operator()(G1::Node,G2::Node), which returns 1 152 //if and only if the 2nodes are equivalent.120 //ifff the two nodes are equivalent. 153 121 NEQ _nEq; 154 122 155 123 typename G2::template NodeMap<int> _conn; 156 124 //Current mapping. We index it by the nodes of g1, and match[v] is 157 125 //a node of g2. 158 126 M &_mapping; 159 //order[i] is the node of g1, for which we finda pair if depth=i127 //order[i] is the node of g1, for which we search a pair if depth=i 160 128 std::vector<typename G1::Node> order; 161 129 //currEdgeIts[i] is an edge iterator, witch is last used in the ith 162 130 //depth to find a pair for order[i]. 163 131 std::vector<typename G2::IncEdgeIt> currEdgeIts; 164 132 //The small graph. 165 133 const G1 &_g1; 166 //The biggraph.134 //The large graph. 167 135 const G2 &_g2; 168 //lookup tables for cut the searchtree136 //lookup tables for cutting the searchtree 169 137 typename G1::template NodeMap<int> rNew1t,rInOut1t; 170 138 171 Vf2MappingType _mapping_type; 139 MappingType _mapping_type; 140 141 bool _deallocMappingAfterUse; 172 142 173 143 //cut the search tree 174 template<Vf2MappingType MT> 175 bool cut(const typename G1::Node n1,const typename G2::Node n2) const 176 { 144 template<MappingType MT> 145 bool cut(const typename G1::Node n1,const typename G2::Node n2) const { 177 146 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 } 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) 150 ++rInOut2; 151 else if(MT!=SUBGRAPH&&_conn[currNode]==0) 152 ++rNew2; 153 } 154 switch(MT) { 155 case INDUCED: 156 return rInOut1t[n1]<=rInOut2&&rNew1t[n1]<=rNew2; 157 case SUBGRAPH: 158 return rInOut1t[n1]<=rInOut2; 159 case ISOMORPH: 160 return rInOut1t[n1]==rInOut2&&rNew1t[n1]==rNew2; 161 default: 162 return false; 163 } 197 164 } 198 165 199 template<Vf2MappingType MT> 200 bool feas(const typename G1::Node n1,const typename G2::Node n2) 201 { 166 template<MappingType MT> 167 bool feas(const typename G1::Node n1,const typename G2::Node n2) { 202 168 if(!_nEq(n1,n2)) 203 169 return 0; 204 170 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]]; 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]]; 175 } 176 bool isIso=1; 177 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { 178 int& connCurrNode = _conn[_g2.oppositeNode(n2,e2)]; 179 if(connCurrNode<-1) 180 ++connCurrNode; 181 else if(MT!=SUBGRAPH&&connCurrNode==-1) { 182 isIso=0; 183 break; 210 184 } 211 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 { 185 } 186 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) { 191 switch(MT) { 192 case INDUCED: 193 case ISOMORPH: 194 isIso=0; 195 break; 196 case SUBGRAPH: 197 if(connCurrNodePair<-1) 219 198 isIso=0; 220 break; 221 } 199 break; 200 } 201 connCurrNodePair=-1; 222 202 } 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 } 203 } 243 204 return isIso&&cut<MT>(n1,n2); 244 205 } 245 206 246 void addPair(const typename G1::Node n1,const typename G2::Node n2) 247 { 207 void addPair(const typename G1::Node n1,const typename G2::Node n2) { 248 208 _conn[n2]=-1; 249 209 _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)]; 210 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { 211 int& currConn = _conn[_g2.oppositeNode(n2,e2)]; 212 if(currConn!=-1) 213 ++currConn; 214 } 253 215 } 254 216 255 void subPair(const typename G1::Node n1,const typename G2::Node n2) 256 { 217 void subPair(const typename G1::Node n1,const typename G2::Node n2) { 257 218 _conn[n2]=0; 258 219 _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 } 220 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { 221 int& currConn = _conn[_g2.oppositeNode(n2,e2)]; 222 if(currConn>0) 223 --currConn; 224 else if(currConn==-1) 225 ++_conn[n2]; 226 } 267 227 } 268 228 269 void setOrder()//we will find pairs for the nodes of g1 in this order 270 { 229 void setOrder() { 230 // we will find pairs for the nodes of g1 in this order 231 271 232 // bits::vf2::DfsLeaveOrder<G1> v(_g1,order); 272 233 // DfsVisit<G1,bits::vf2::DfsLeaveOrder<G1> >dfs(_g1, v); 273 234 // dfs.run(); … … 278 239 bfs.run(); 279 240 } 280 241 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 { 242 template<MappingType MT> 243 bool extMatch() { 244 while(_depth>=0) { 245 //there are not nodes in g1, which has not pair in g2. 246 if(_depth==static_cast<int>(order.size())) { 247 --_depth; 248 return true; 249 } 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 259 //fstMatchedE 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); 278 } 279 for(; n2!=INVALID; ++n2) 280 if(MT!=SUBGRAPH) { 281 if(_conn[n2]==0&&feas<MT>(nodeOfDepth,n2)) 282 break; 283 } 284 else if(_conn[n2]>=0&&feas<MT>(nodeOfDepth,n2)) 285 break; 286 // n2 is the next candidate 287 if(n2!=INVALID){ 288 addPair(nodeOfDepth,n2); 289 ++_depth; 290 } 291 else // there are no more candidates 289 292 --_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; 293 continue; 294 } 295 else { 296 currPNode=_mapping[_g1.oppositeNode(nodeOfDepth, 297 fstMatchedE)]; 298 edgeItOfDepth=typename G2::IncEdgeIt(_g2,currPNode); 299 } 366 300 } 301 else { 302 currPNode=_g2.oppositeNode(pairOfNodeOfDepth, 303 edgeItOfDepth); 304 subPair(nodeOfDepth,pairOfNodeOfDepth); 305 ++edgeItOfDepth; 306 } 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); 312 break; 313 } 314 } 315 edgeItOfDepth==INVALID?--_depth:++_depth; 316 } 367 317 return false; 368 318 } 369 319 370 320 //calc. the lookup table for cut the searchtree 371 void setRNew1tRInOut1t() 372 { 321 void setRNew1tRInOut1t() { 373 322 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 } 323 for(unsigned int i=0; i<order.size(); ++i) { 324 const typename G1::Node& orderI = order[i]; 325 tmp[orderI]=-1; 326 for(typename G1::IncEdgeIt e1(_g1,orderI); e1!=INVALID; ++e1) { 327 const typename G1::Node currNode=_g1.oppositeNode(orderI,e1); 328 if(tmp[currNode]>0) 329 ++rInOut1t[orderI]; 330 else if(tmp[currNode]==0) 331 ++rNew1t[orderI]; 391 332 } 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) 336 ++tmp[currNode]; 337 } 338 } 392 339 } 393 340 public: 394 341 ///Constructor … … 399 346 ///\param g2 The graph \e g1 will be embedded into. 400 347 ///\param m \ref concepts::ReadWriteMap "read-write" NodeMap 401 348 ///storing the found mapping. 402 ///\param neq A bool-valued binary functor determinin ing whether a node is349 ///\param neq A bool-valued binary functor determining whether a node is 403 350 ///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() ) :351 Vf2(const G1 &g1, const G2 &g2, M &m, const NEQ &neq = NEQ() ) : 405 352 _nEq(neq), _conn(g2,0), _mapping(m), order(countNodes(g1)), 406 353 currEdgeIts(countNodes(g1),INVALID), _g1(g1), _g2(g2), rNew1t(g1,0), 407 rInOut1t(g1,0), _mapping_type(SUBGRAPH) 408 { 354 rInOut1t(g1,0), _mapping_type(SUBGRAPH), _deallocMappingAfterUse(0) { 409 355 _depth=0; 410 356 setOrder(); 411 357 setRNew1tRInOut1t(); … … 413 359 m[n]=INVALID; 414 360 } 415 361 362 ///Destructor 363 364 ///Destructor. 365 /// 366 367 ~Vf2(){ 368 if(_deallocMappingAfterUse) 369 delete &_mapping; 370 } 371 416 372 ///Returns the current mapping type 417 373 418 374 ///Returns the current mapping type 419 375 /// 420 Vf2MappingType mappingType() const { return _mapping_type; } 376 MappingType mappingType() const { 377 return _mapping_type; 378 } 421 379 ///Sets mapping type 422 380 423 381 ///Sets mapping type. 424 382 /// 425 383 ///The mapping type is set to \ref SUBGRAPH by default. 426 384 /// 427 ///\sa See \ref Vf2MappingType for the possible values. 428 void mappingType(Vf2MappingType m_type) { _mapping_type = m_type; } 385 ///\sa See \ref MappingType for the possible values. 386 void mappingType(MappingType m_type) { 387 _mapping_type = m_type; 388 } 429 389 430 390 ///Finds a mapping 431 391 432 ///It finds a mapping betweenfrom g1 into g2 according to the mapping433 ///type set by \ref mappingType( Vf2MappingType) "mappingType()".392 ///It finds a mapping from g1 into g2 according to the mapping 393 ///type set by \ref mappingType(MappingType) "mappingType()". 434 394 /// 435 395 ///By subsequent calls, it returns all possible mappings one-by-one. 436 396 /// 437 397 ///\retval true if a mapping is found. 438 398 ///\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 } 399 bool find() { 400 switch(_mapping_type) { 401 case SUBGRAPH: 402 return extMatch<SUBGRAPH>(); 403 case INDUCED: 404 return extMatch<INDUCED>(); 405 case ISOMORPH: 406 return extMatch<ISOMORPH>(); 407 default: 408 return false; 409 } 452 410 } 453 411 }; 454 412 455 413 template<class G1, class G2> 456 class Vf2WizardBase 457 { 414 class Vf2WizardBase { 458 415 protected: 459 416 typedef G1 Graph1; 460 417 typedef G2 Graph2; … … 462 419 const G1 &_g1; 463 420 const G2 &_g2; 464 421 465 Vf2MappingType _mapping_type;422 MappingType _mapping_type; 466 423 467 424 typedef typename G1::template NodeMap<typename G2::Node> Mapping; 468 425 bool _local_mapping; 469 426 void *_mapping; 470 void createMapping() 471 { 427 void createMapping() { 472 428 _mapping = new Mapping(_g1); 473 429 } 474 430 431 void *myVf2; //used in Vf2Wizard::find 432 433 475 434 typedef bits::vf2::AlwaysEq NodeEq; 476 435 NodeEq _node_eq; 477 436 478 437 Vf2WizardBase(const G1 &g1,const G2 &g2) 479 : _g1(g1), _g2(g2), _mapping_type(SUBGRAPH), _local_mapping(true) { }438 : _g1(g1), _g2(g2), _mapping_type(SUBGRAPH), _local_mapping(true) { } 480 439 }; 481 440 441 482 442 /// Auxiliary class for the function-type interface of %VF2 algorithm. 483 443 484 444 /// This auxiliary class implements the named parameters of … … 489 449 /// \tparam TR The traits class that defines various types used by the 490 450 /// algorithm. 491 451 template<class TR> 492 class Vf2Wizard : public TR 493 { 452 class Vf2Wizard : public TR { 494 453 typedef TR Base; 495 454 typedef typename TR::Graph1 Graph1; 496 455 typedef typename TR::Graph2 Graph2; … … 506 465 507 466 public: 508 467 ///Constructor 509 Vf2Wizard(const Graph1 &g1,const Graph2 &g2) : Base(g1,g2) {} 468 Vf2Wizard(const Graph1 &g1,const Graph2 &g2) : Base(g1,g2) { 469 } 510 470 511 471 ///Copy constructor 512 Vf2Wizard(const Base &b) : Base(b) {} 472 Vf2Wizard(const Base &b) : Base(b) { } 473 474 ///Copy constructor 475 Vf2Wizard(const Vf2Wizard &b) : Base(b) {} 513 476 514 477 515 478 template<class T> 516 struct SetMappingBase : public Base 479 struct SetMappingBase : public Base{ 517 480 typedef T Mapping; 518 481 SetMappingBase(const Base &b) : Base(b) {} 519 482 }; … … 524 487 ///\ref named-templ-param "Named parameter" function for setting 525 488 ///the map that stores the found embedding. 526 489 template<class T> 527 Vf2Wizard< SetMappingBase<T> > mapping(const T &t) 528 { 490 Vf2Wizard< SetMappingBase<T> > mapping(const T &t) { 529 491 Base::_mapping=reinterpret_cast<void*>(const_cast<T*>(&t)); 530 492 Base::_local_mapping = false; 531 493 return Vf2Wizard<SetMappingBase<T> >(*this); … … 536 498 typedef NE NodeEq; 537 499 NodeEq _node_eq; 538 500 SetNodeEqBase(const Base &b, const NE &node_eq) 539 : Base(b), _node_eq(node_eq) {} 501 : Base(b), _node_eq(node_eq){ 502 } 540 503 }; 541 504 542 505 ///\brief \ref named-templ-param "Named parameter" for setting … … 549 512 ///whether a node is mappable to another. By default it is an 550 513 ///always true operator. 551 514 template<class T> 552 Vf2Wizard< SetNodeEqBase<T> > nodeEq(const T &node_eq) 553 { 515 Vf2Wizard< SetNodeEqBase<T> > nodeEq(const T &node_eq) { 554 516 return Vf2Wizard<SetNodeEqBase<T> >(SetNodeEqBase<T>(*this,node_eq)); 555 517 } 556 518 … … 560 522 ///\ref named-templ-param "Named parameter" function for setting 561 523 ///the node labels defining equivalence relation between them. 562 524 /// 563 ///\param m1 It isarbitrary \ref concepts::ReadMap "readable node map"525 ///\param m1 An arbitrary \ref concepts::ReadMap "readable node map" 564 526 ///of g1. 565 ///\param m2 It isarbitrary \ref concepts::ReadMap "readable node map"527 ///\param m2 An arbitrary \ref concepts::ReadMap "readable node map" 566 528 ///of g2. 567 529 /// 568 530 ///The value type of these maps must be equal comparable. 569 531 template<class M1, class M2> 570 532 Vf2Wizard< SetNodeEqBase<bits::vf2::MapEq<M1,M2> > > 571 nodeLabels(const M1 &m1,const M2 &m2) 572 { 533 nodeLabels(const M1 &m1,const M2 &m2){ 573 534 return nodeEq(bits::vf2::MapEq<M1,M2>(m1,m2)); 574 535 } 575 536 … … 581 542 /// 582 543 ///The mapping type is set to \ref SUBGRAPH by default. 583 544 /// 584 ///\sa See \ref Vf2MappingType for the possible values. 585 Vf2Wizard<Base> &mappingType(Vf2MappingType m_type) 586 { 545 ///\sa See \ref MappingType for the possible values. 546 Vf2Wizard<Base> &mappingType(MappingType m_type) { 587 547 _mapping_type = m_type; 588 548 return *this; 589 549 } … … 593 553 /// 594 554 ///\ref named-templ-param "Named parameter" for setting 595 555 ///the mapping type to \ref INDUCED. 596 Vf2Wizard<Base> &induced() 597 { 556 Vf2Wizard<Base> &induced() { 598 557 _mapping_type = INDUCED; 599 558 return *this; 600 559 } … … 604 563 /// 605 564 ///\ref named-templ-param "Named parameter" for setting 606 565 ///the mapping type to \ref ISOMORPH. 607 Vf2Wizard<Base> &iso() 608 { 566 Vf2Wizard<Base> &iso() { 609 567 _mapping_type = ISOMORPH; 610 568 return *this; 611 569 } 612 570 571 613 572 ///Runs VF2 algorithm. 614 573 615 574 ///This method runs VF2 algorithm. 616 575 /// 617 576 ///\retval true if a mapping is found. 618 ///\retval false if there is no (more) mapping. 619 bool run() 620 { 577 ///\retval false if there is no mapping. 578 bool run(){ 621 579 if(Base::_local_mapping) 622 580 Base::createMapping(); 623 581 … … 633 591 634 592 return ret; 635 593 } 594 595 ///Get a pointer to the generated Vf2 object. 596 597 ///Gives a pointer to the generated Vf2 object. 598 /// 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; 610 return ptr; 611 } 612 613 ///Counts the number of mappings. 614 615 ///This method counts the number of mappings. 616 /// 617 /// \return The number of mappings. 618 int count() { 619 if(Base::_local_mapping) 620 Base::createMapping(); 621 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); 627 628 int ret = 0; 629 while(alg.find()) 630 ++ret; 631 632 return ret; 633 } 636 634 }; 637 635 638 636 ///Function-type interface for VF2 algorithm. … … 644 642 ///declared as the members of class \ref Vf2Wizard. 645 643 ///The following examples show how to use these parameters. 646 644 ///\code 647 /// // Find an embedding of graph g into graph h645 /// // Find an embedding of graph g1 into graph g2 648 646 /// ListGraph::NodeMap<ListGraph::Node> m(g); 649 /// vf2(g ,h).mapping(m).run();647 /// vf2(g1,g2).mapping(m).run(); 650 648 /// 651 /// // Check whether graphs g and h are isomorphic 652 /// bool is_iso = vf2(g,h).iso().run(); 649 /// // Check whether graphs g1 and g2 are isomorphic 650 /// bool is_iso = vf2(g1,g2).iso().run(); 651 /// 652 /// // Count the number of isomorphisms 653 /// int num_isos = vf2(g1,g2).iso().count(); 654 /// 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 660 /// } 661 /// delete myVf22; 653 662 ///\endcode 654 ///\warning Don't forget to put the \ref Vf2Wizard::run() "run()" 663 ///\warning Don't forget to put the \ref Vf2Wizard::run() "run()", 664 ///\ref Vf2Wizard::count() "count()" or 665 ///the \ref Vf2Wizard::getPtrToVf2Object() "getPtrToVf2Object()" 655 666 ///to the end of the expression. 656 667 ///\sa Vf2Wizard 657 668 ///\sa Vf2 658 669 template<class G1, class G2> 659 Vf2Wizard<Vf2WizardBase<G1,G2> > vf2(const G1 &g1, const G2 &g2) 660 { 670 Vf2Wizard<Vf2WizardBase<G1,G2> > vf2(const G1 &g1, const G2 &g2) { 661 671 return Vf2Wizard<Vf2WizardBase<G1,G2> >(g1,g2); 662 672 } 663 673 -
new file lemon/vf2pp.h
diff --git a/lemon/vf2pp.h b/lemon/vf2pp.h new file mode 100644
- + 1 /* -*- mode: C++; indent-tabs-mode: nil; -*- 2 * 3 * This file is a part of LEMON, a generic C++ optimization library. 4 * 5 * Copyright (C) 2015-2017 6 * EMAXA Kutato-fejleszto Kft. (EMAXA Research Ltd.) 7 * 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. 11 * 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 14 * purpose. 15 * 16 */ 17 18 #ifndef LEMON_VF2PP_H 19 #define LEMON_VF2PP_H 20 21 ///\ingroup graph_properties 22 ///\file 23 ///\brief VF2 Plus Plus algorithm. 24 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> 30 31 32 #include <vector> 33 #include <algorithm> 34 #include <utility> 35 36 37 namespace lemon { 38 namespace bits { 39 namespace vf2pp { 40 41 template <class G> 42 class DfsLeaveOrder : public DfsVisitor<G> { 43 int i; 44 const G &_g; 45 std::vector<typename G::Node> &_order; 46 public: 47 DfsLeaveOrder(const G &g, std::vector<typename G::Node> &order) 48 : i(countNodes(g)), _g(g), _order(order) { 49 } 50 void leave(const typename G::Node &node) { 51 _order[--i]=node; 52 } 53 }; 54 55 template <class G> 56 class BfsLeaveOrder : public BfsVisitor<G> { 57 int i; 58 const G &_g; 59 std::vector<typename G::Node> &_order; 60 public: 61 BfsLeaveOrder(const G &g, std::vector<typename G::Node> &order) { } 62 void process(const typename G::Node &node) { 63 _order[i++]=node; 64 } 65 }; 66 } 67 } 68 69 70 ///%VF2 Plus Plus algorithm class. 71 72 ///\ingroup graph_isomorphism This class provides an efficient 73 ///implementation of the %VF2 Plus Plus algorithm 74 ///for variants of the (Sub)graph Isomorphism problem. 75 /// 76 ///There is also a \ref vf2pp() "function-type interface" called 77 ///\ref vf2pp() for the %VF2 Plus Plus algorithm, which is probably 78 ///more convenient in most use-cases. 79 /// 80 ///\tparam G1 The type of the graph to be embedded. 81 ///The default type is \ref ListDigraph. 82 ///\tparam G2 The type of the graph g1 will be embedded into. 83 ///The default type is \ref ListDigraph. 84 ///\tparam M The type of the NodeMap storing the mapping. 85 ///By default, it is G1::NodeMap<G2::Node> 86 ///\tparam M1 The type of the NodeMap storing the integer node labels of G1. 87 ///The labels must be the numbers {0,1,2,..,K-1}, where K is the number of 88 ///different labels. By default, it is G1::NodeMap<int>. 89 ///\tparam M2 The type of the NodeMap storing the integer node labels of G2. 90 ///The labels must be the numbers {0,1,2,..,K-1}, where K is the number of 91 ///different labels. By default, it is G2::NodeMap<int>. 92 /// 93 ///\sa vf2pp() 94 #ifdef DOXYGEN 95 template<class G1, class G2, class M, class M1, class M2 > 96 #else 97 template<class G1=ListDigraph, 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 102 class M1 = typename G1::template NodeMap<int>, 103 //labels of G2, ... 104 class M2 = typename G2::template NodeMap<int> > 105 #endif 106 class Vf2pp { 107 //Current depth in the search tree. 108 int _depth; 109 110 //_conn[v2] = number of covered neighbours of v2 111 typename G2::template NodeMap<int> _conn; 112 113 //The current mapping. _mapping[v1]=v2 iff v1 has been mapped to v2, 114 //where v1 is a node of G1 and v2 is a node of G2 115 M &_mapping; 116 117 //order[i] is a node of g1, for which a pair is searched if depth=i 118 std::vector<typename G1::Node> order; 119 120 //currEdgeIts[i] is the last used edge iterator in the ith 121 //depth to find a pair for node order[i] 122 std::vector<typename G2::IncEdgeIt> currEdgeIts; 123 124 //The small graph. 125 const G1 &_g1; 126 127 //The large graph. 128 const G2 &_g2; 129 130 //rNewLabels1[v] is a pair of form 131 //(label; num. of uncov. nodes with such label and no covered neighbours) 132 typename G1::template NodeMap<std::vector<std::pair<int,int> > > 133 rNewLabels1; 134 135 //rInOutLabels1[v] is the number of covered neighbours of v for each label 136 //in form (label,number of such labels) 137 typename G1::template NodeMap<std::vector<std::pair<int,int> > > 138 rInOutLabels1; 139 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) 142 M1 &_intLabels1; 143 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) 146 M2 &_intLabels2; 147 148 //largest label 149 const int maxLabel; 150 151 //lookup tables for manipulating with label class cardinalities 152 //after use they have to be reset to 0..0 153 std::vector<int> labelTmp1,labelTmp2; 154 155 MappingType _mapping_type; 156 157 //indicates whether the mapping or the labels must be deleted in the ctor 158 bool _deallocMappingAfterUse,_deallocLabelsAfterUse; 159 160 161 //improved cutting function 162 template<MappingType MT> 163 bool cutByLabels(const typename G1::Node n1,const typename G2::Node n2) { 164 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { 165 const typename G2::Node currNode=_g2.oppositeNode(n2,e2); 166 if(_conn[currNode]>0) 167 --labelTmp1[_intLabels2[currNode]]; 168 else if(MT!=SUBGRAPH&&_conn[currNode]==0) 169 --labelTmp2[_intLabels2[currNode]]; 170 } 171 172 bool ret=1; 173 if(ret) { 174 for(unsigned int i = 0; i < rInOutLabels1[n1].size(); ++i) 175 labelTmp1[rInOutLabels1[n1][i].first]+=rInOutLabels1[n1][i].second; 176 177 if(MT!=SUBGRAPH) 178 for(unsigned int i = 0; i < rNewLabels1[n1].size(); ++i) 179 labelTmp2[rNewLabels1[n1][i].first]+=rNewLabels1[n1][i].second; 180 181 switch(MT) { 182 case INDUCED: 183 for(unsigned int i = 0; i < rInOutLabels1[n1].size(); ++i) 184 if(labelTmp1[rInOutLabels1[n1][i].first]>0) { 185 ret=0; 186 break; 187 } 188 if(ret) 189 for(unsigned int i = 0; i < rNewLabels1[n1].size(); ++i) 190 if(labelTmp2[rNewLabels1[n1][i].first]>0) { 191 ret=0; 192 break; 193 } 194 break; 195 case SUBGRAPH: 196 for(unsigned int i = 0; i < rInOutLabels1[n1].size(); ++i) 197 if(labelTmp1[rInOutLabels1[n1][i].first]>0) { 198 ret=0; 199 break; 200 } 201 break; 202 case ISOMORPH: 203 for(unsigned int i = 0; i < rInOutLabels1[n1].size(); ++i) 204 if(labelTmp1[rInOutLabels1[n1][i].first]!=0) { 205 ret=0; 206 break; 207 } 208 if(ret) 209 for(unsigned int i = 0; i < rNewLabels1[n1].size(); ++i) 210 if(labelTmp2[rNewLabels1[n1][i].first]!=0) { 211 ret=0; 212 break; 213 } 214 break; 215 default: 216 return false; 217 } 218 for(unsigned int i = 0; i < rInOutLabels1[n1].size(); ++i) 219 labelTmp1[rInOutLabels1[n1][i].first]=0; 220 221 if(MT!=SUBGRAPH) 222 for(unsigned int i = 0; i < rNewLabels1[n1].size(); ++i) 223 labelTmp2[rNewLabels1[n1][i].first]=0; 224 } 225 226 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { 227 const typename G2::Node currNode=_g2.oppositeNode(n2,e2); 228 labelTmp1[_intLabels2[currNode]]=0; 229 if(MT!=SUBGRAPH&&_conn[currNode]==0) 230 labelTmp2[_intLabels2[currNode]]=0; 231 } 232 233 return ret; 234 } 235 236 237 //try to exclude the matching of n1 and n2 238 template<MappingType MT> 239 bool feas(const typename G1::Node n1,const typename G2::Node n2) { 240 if(_intLabels1[n1]!=_intLabels2[n2]) 241 return 0; 242 243 for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1) { 244 const typename G1::Node& currNode=_g1.oppositeNode(n1,e1); 245 if(_mapping[currNode]!=INVALID) 246 --_conn[_mapping[currNode]]; 247 } 248 249 bool isIso=1; 250 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { 251 int& connCurrNode = _conn[_g2.oppositeNode(n2,e2)]; 252 if(connCurrNode<-1) 253 ++connCurrNode; 254 else if(MT!=SUBGRAPH&&connCurrNode==-1) { 255 isIso=0; 256 break; 257 } 258 } 259 260 if(isIso) 261 for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1) { 262 const typename G2::Node& currNodePair = 263 _mapping[_g1.oppositeNode(n1,e1)]; 264 int& connCurrNodePair=_conn[currNodePair]; 265 if(currNodePair!=INVALID&&connCurrNodePair!=-1) { 266 switch(MT){ 267 case INDUCED: 268 case ISOMORPH: 269 isIso=0; 270 break; 271 case SUBGRAPH: 272 if(connCurrNodePair<-1) 273 isIso=0; 274 break; 275 } 276 connCurrNodePair=-1; 277 } 278 } 279 else 280 for(typename G1::IncEdgeIt e1(_g1,n1); e1!=INVALID; ++e1) { 281 const typename G2::Node currNode=_mapping[_g1.oppositeNode(n1,e1)]; 282 if(currNode!=INVALID/*&&_conn[currNode]!=-1*/) 283 _conn[currNode]=-1; 284 } 285 286 return isIso&&cutByLabels<MT>(n1,n2); 287 } 288 289 290 //matches n1 and n2 291 void addPair(const typename G1::Node n1,const typename G2::Node n2) { 292 _conn[n2]=-1; 293 _mapping.set(n1,n2); 294 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2) { 295 int& currConn = _conn[_g2.oppositeNode(n2,e2)]; 296 if(currConn!=-1) 297 ++currConn; 298 } 299 } 300 301 302 //dematches n1 and n2 303 void subPair(const typename G1::Node n1,const typename G2::Node n2) { 304 _conn[n2]=0; 305 _mapping.set(n1,INVALID); 306 for(typename G2::IncEdgeIt e2(_g2,n2); e2!=INVALID; ++e2){ 307 int& currConn = _conn[_g2.oppositeNode(n2,e2)]; 308 if(currConn>0) 309 --currConn; 310 else if(currConn==-1) 311 ++_conn[n2]; 312 } 313 } 314 315 316 void processBFSLevel(typename G1::Node source,unsigned int& orderIndex, 317 typename G1::template NodeMap<int>& dm1, 318 typename G1::template NodeMap<bool>& added) { 319 order[orderIndex]=source; 320 added[source]=1; 321 322 unsigned int endPosOfLevel=orderIndex, 323 startPosOfLevel=orderIndex, 324 lastAdded=orderIndex; 325 326 typename G1::template NodeMap<int> currConn(_g1,0); 327 328 while(orderIndex<=lastAdded){ 329 typename G1::Node currNode = order[orderIndex]; 330 for(typename G1::IncEdgeIt e(_g1,currNode); e!=INVALID; ++e) { 331 typename G1::Node n = _g1.oppositeNode(currNode,e); 332 if(!added[n]) { 333 order[++lastAdded]=n; 334 added[n]=1; 335 } 336 } 337 if(orderIndex>endPosOfLevel){ 338 for(unsigned int j = startPosOfLevel; j <= endPosOfLevel; ++j) { 339 int minInd=j; 340 for(unsigned int i = j+1; i <= endPosOfLevel; ++i) 341 if(currConn[order[i]]>currConn[order[minInd]]|| 342 (currConn[order[i]]==currConn[order[minInd]]&& 343 (dm1[order[i]]>dm1[order[minInd]]|| 344 (dm1[order[i]]==dm1[order[minInd]]&& 345 labelTmp1[_intLabels1[order[minInd]]]> 346 labelTmp1[_intLabels1[order[i]]])))) 347 minInd=i; 348 349 --labelTmp1[_intLabels1[order[minInd]]]; 350 for(typename G1::IncEdgeIt e(_g1,order[minInd]); e!=INVALID; ++e) 351 ++currConn[_g1.oppositeNode(order[minInd],e)]; 352 std::swap(order[j],order[minInd]); 353 } 354 startPosOfLevel=endPosOfLevel+1; 355 endPosOfLevel=lastAdded; 356 } 357 ++orderIndex; 358 } 359 } 360 361 362 //we will find pairs for the nodes of g1 in this order 363 void setOrder(){ 364 for(typename G2::NodeIt n2(_g2); n2!=INVALID; ++n2) 365 ++labelTmp1[_intLabels2[n2]]; 366 367 // OutDegMap<G1> dm1(_g1); 368 typename G1::template NodeMap<int> dm1(_g1,0); 369 for(typename G1::EdgeIt e(_g1); e!=INVALID; ++e) { 370 ++dm1[_g1.u(e)]; 371 ++dm1[_g1.v(e)]; 372 } 373 374 typename G1::template NodeMap<bool> added(_g1,0); 375 unsigned int orderIndex=0; 376 377 for(typename G1::NodeIt n(_g1); n!=INVALID;) { 378 if(!added[n]){ 379 typename G1::Node minNode = n; 380 for(typename G1::NodeIt n1(_g1,minNode); n1!=INVALID; ++n1) 381 if(!added[n1] && 382 (labelTmp1[_intLabels1[minNode]]> 383 labelTmp1[_intLabels1[n1]]||(dm1[minNode]<dm1[n1]&& 384 labelTmp1[_intLabels1[minNode]]== 385 labelTmp1[_intLabels1[n1]]))) 386 minNode=n1; 387 processBFSLevel(minNode,orderIndex,dm1,added); 388 } 389 else 390 ++n; 391 } 392 for(unsigned int i = 0; i < labelTmp1.size(); ++i) 393 labelTmp1[i]=0; 394 } 395 396 397 template<MappingType MT> 398 bool extMatch(){ 399 while(_depth>=0) { 400 //there is no node in g1, which has not pair in g2. 401 if(_depth==static_cast<int>(order.size())) { 402 --_depth; 403 return true; 404 } 405 typename G1::Node& nodeOfDepth = order[_depth]; 406 const typename G2::Node& pairOfNodeOfDepth = _mapping[nodeOfDepth]; 407 typename G2::IncEdgeIt &edgeItOfDepth = currEdgeIts[_depth]; 408 //the node of g2, which neighbours are the candidates for 409 //the pair of order[_depth] 410 typename G2::Node currPNode; 411 if(edgeItOfDepth==INVALID){ 412 typename G1::IncEdgeIt fstMatchedE(_g1,nodeOfDepth); 413 //if _mapping[order[_depth]]!=INVALID, we dont need 414 //fstMatchedE 415 if(pairOfNodeOfDepth==INVALID) 416 for(; fstMatchedE!=INVALID && 417 _mapping[_g1.oppositeNode(nodeOfDepth, 418 fstMatchedE)]==INVALID; 419 ++fstMatchedE); //find fstMatchedE, it could be preprocessed 420 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 424 //to the spec. problem types) node of g2 is 425 //candidate. We can read the iterator of the last 426 //tried node from the match if it is not the first 427 //try(match[nodeOfDepth]!=INVALID) 428 typename G2::NodeIt n2(_g2); 429 //if it's not the first try 430 if(pairOfNodeOfDepth!=INVALID) { 431 n2=++typename G2::NodeIt(_g2,pairOfNodeOfDepth); 432 subPair(nodeOfDepth,pairOfNodeOfDepth); 433 } 434 for(; n2!=INVALID; ++n2) 435 if(MT!=SUBGRAPH) { 436 if(_conn[n2]==0&&feas<MT>(nodeOfDepth,n2)) 437 break; 438 } 439 else if(_conn[n2]>=0&&feas<MT>(nodeOfDepth,n2)) 440 break; 441 // n2 is the next candidate 442 if(n2!=INVALID) { 443 addPair(nodeOfDepth,n2); 444 ++_depth; 445 } 446 else // there are no more candidates 447 --_depth; 448 continue; 449 } 450 else{ 451 currPNode=_mapping[_g1.oppositeNode(nodeOfDepth, 452 fstMatchedE)]; 453 edgeItOfDepth=typename G2::IncEdgeIt(_g2,currPNode); 454 } 455 } 456 else{ 457 currPNode=_g2.oppositeNode(pairOfNodeOfDepth, 458 edgeItOfDepth); 459 subPair(nodeOfDepth,pairOfNodeOfDepth); 460 ++edgeItOfDepth; 461 } 462 for(; edgeItOfDepth!=INVALID; ++edgeItOfDepth) { 463 const typename G2::Node currNode = 464 _g2.oppositeNode(currPNode, edgeItOfDepth); 465 if(_conn[currNode]>0&&feas<MT>(nodeOfDepth,currNode)) { 466 addPair(nodeOfDepth,currNode); 467 break; 468 } 469 } 470 edgeItOfDepth==INVALID?--_depth:++_depth; 471 } 472 return false; 473 } 474 475 //calc. the lookup table for cutting the searchtree 476 void setRNew1tRInOut1t(){ 477 typename G1::template NodeMap<int> tmp(_g1,0); 478 for(unsigned int i=0; i<order.size(); ++i) { 479 tmp[order[i]]=-1; 480 for(typename G1::IncEdgeIt e1(_g1,order[i]); e1!=INVALID; ++e1) { 481 const typename G1::Node currNode=_g1.oppositeNode(order[i],e1); 482 if(tmp[currNode]>0) 483 ++labelTmp1[_intLabels1[currNode]]; 484 else if(tmp[currNode]==0) 485 ++labelTmp2[_intLabels1[currNode]]; 486 } 487 //labelTmp1[i]=number of neightbours with label i in set rInOut 488 //labelTmp2[i]=number of neightbours with label i in set rNew 489 for(typename G1::IncEdgeIt e1(_g1,order[i]); e1!=INVALID; ++e1) { 490 const int& currIntLabel = _intLabels1[_g1.oppositeNode(order[i],e1)]; 491 if(labelTmp1[currIntLabel]>0) { 492 rInOutLabels1[order[i]] 493 .push_back(std::make_pair(currIntLabel, 494 labelTmp1[currIntLabel])); 495 labelTmp1[currIntLabel]=0; 496 } 497 else if(labelTmp2[currIntLabel]>0) { 498 rNewLabels1[order[i]]. 499 push_back(std::make_pair(currIntLabel,labelTmp2[currIntLabel])); 500 labelTmp2[currIntLabel]=0; 501 } 502 } 503 504 for(typename G1::IncEdgeIt e1(_g1,order[i]); e1!=INVALID; ++e1) { 505 int& tmpCurrNode=tmp[_g1.oppositeNode(order[i],e1)]; 506 if(tmpCurrNode!=-1) 507 ++tmpCurrNode; 508 } 509 } 510 } 511 512 int getMaxLabel() const{ 513 int m=-1; 514 for(typename G1::NodeIt n1(_g1); n1!=INVALID; ++n1) { 515 const int& currIntLabel = _intLabels1[n1]; 516 if(currIntLabel>m) 517 m=currIntLabel; 518 } 519 for(typename G2::NodeIt n2(_g2); n2!=INVALID; ++n2) { 520 const int& currIntLabel = _intLabels2[n2]; 521 if(currIntLabel>m) 522 m=currIntLabel; 523 } 524 return m; 525 } 526 527 public: 528 ///Constructor 529 530 ///Constructor. 531 ///\param g1 The graph to be embedded. 532 ///\param g2 The graph \e g1 will be embedded into. 533 ///\param m The type of the NodeMap storing the mapping. 534 ///By default, it is G1::NodeMap<G2::Node> 535 ///\param intLabel1 The NodeMap storing the integer node labels of G1. 536 ///The labels must be the numbers {0,1,2,..,K-1}, where K is the number of 537 ///different labels. 538 ///\param intLabel1 The NodeMap storing the integer node labels of G2. 539 ///The labels must be the numbers {0,1,2,..,K-1}, where K is the number of 540 ///different labels. 541 Vf2pp(const G1 &g1, const G2 &g2,M &m, M1 &intLabels1, M2 &intLabels2) : 542 _depth(0), _conn(g2,0), _mapping(m), order(countNodes(g1),INVALID), 543 currEdgeIts(countNodes(g1),INVALID), _g1(g1), _g2(g2), rNewLabels1(_g1), 544 rInOutLabels1(_g1), _intLabels1(intLabels1) ,_intLabels2(intLabels2), 545 maxLabel(getMaxLabel()), labelTmp1(maxLabel+1),labelTmp2(maxLabel+1), 546 _mapping_type(SUBGRAPH), _deallocMappingAfterUse(0), 547 _deallocLabelsAfterUse(0) 548 { 549 setOrder(); 550 setRNew1tRInOut1t(); 551 552 //reset mapping 553 for(typename G1::NodeIt n(g1);n!=INVALID;++n) 554 m[n]=INVALID; 555 } 556 557 ///Destructor 558 559 ///Destructor. 560 /// 561 ~Vf2pp() 562 { 563 if(_deallocMappingAfterUse) 564 delete &_mapping; 565 if(_deallocLabelsAfterUse) { 566 delete &_intLabels1; 567 delete &_intLabels2; 568 } 569 } 570 571 ///Returns the current mapping type. 572 573 ///Returns the current mapping type. 574 /// 575 MappingType mappingType() const 576 { 577 return _mapping_type; 578 } 579 580 ///Sets the mapping type 581 582 ///Sets the mapping type. 583 /// 584 ///The mapping type is set to \ref SUBGRAPH by default. 585 /// 586 ///\sa See \ref MappingType for the possible values. 587 void mappingType(MappingType m_type) 588 { 589 _mapping_type = m_type; 590 } 591 592 ///Finds a mapping. 593 594 ///This method finds a mapping from g1 into g2 according to the mapping 595 ///type set by \ref mappingType(MappingType) "mappingType()". 596 /// 597 ///By subsequent calls, it returns all possible mappings one-by-one. 598 /// 599 ///\retval true if a mapping is found. 600 ///\retval false if there is no (more) mapping. 601 bool find() 602 { 603 switch(_mapping_type) 604 { 605 case SUBGRAPH: 606 return extMatch<SUBGRAPH>(); 607 case INDUCED: 608 return extMatch<INDUCED>(); 609 case ISOMORPH: 610 return extMatch<ISOMORPH>(); 611 default: 612 return false; 613 } 614 } 615 }; 616 617 template<typename G1, typename G2> 618 class Vf2ppWizardBase { 619 protected: 620 typedef G1 Graph1; 621 typedef G2 Graph2; 622 623 const G1 &_g1; 624 const G2 &_g2; 625 626 MappingType _mapping_type; 627 628 typedef typename G1::template NodeMap<typename G2::Node> Mapping; 629 bool _local_mapping; 630 void *_mapping; 631 void createMapping() { 632 _mapping = new Mapping(_g1); 633 } 634 635 bool _local_nodeLabels; 636 typedef typename G1::template NodeMap<int> NodeLabels1; 637 typedef typename G2::template NodeMap<int> NodeLabels2; 638 void *_nodeLabels1, *_nodeLabels2; 639 void createNodeLabels() { 640 _nodeLabels1 = new NodeLabels1(_g1,0); 641 _nodeLabels2 = new NodeLabels2(_g2,0); 642 } 643 644 Vf2ppWizardBase(const G1 &g1,const G2 &g2) 645 : _g1(g1), _g2(g2), _mapping_type(SUBGRAPH), 646 _local_mapping(1), _local_nodeLabels(1) { } 647 }; 648 649 650 /// \brief Auxiliary class for the function-type interface of %VF2 651 /// Plus Plus algorithm. 652 /// 653 /// This auxiliary class implements the named parameters of 654 /// \ref vf2pp() "function-type interface" of \ref Vf2pp algorithm. 655 /// 656 /// \warning This class is not to be used directly. 657 /// 658 /// \tparam TR The traits class that defines various types used by the 659 /// algorithm. 660 template<typename TR> 661 class Vf2ppWizard : public TR { 662 typedef TR Base; 663 typedef typename TR::Graph1 Graph1; 664 typedef typename TR::Graph2 Graph2; 665 typedef typename TR::Mapping Mapping; 666 typedef typename TR::NodeLabels1 NodeLabels1; 667 typedef typename TR::NodeLabels2 NodeLabels2; 668 669 using TR::_g1; 670 using TR::_g2; 671 using TR::_mapping_type; 672 using TR::_mapping; 673 using TR::_nodeLabels1; 674 using TR::_nodeLabels2; 675 676 public: 677 ///Constructor 678 Vf2ppWizard(const Graph1 &g1,const Graph2 &g2) : Base(g1,g2) { } 679 680 ///Copy constructor 681 Vf2ppWizard(const Base &b) : Base(b) {} 682 683 684 template<typename T> 685 struct SetMappingBase : public Base { 686 typedef T Mapping; 687 SetMappingBase(const Base &b) : Base(b) { } 688 }; 689 690 ///\brief \ref named-templ-param "Named parameter" for setting 691 ///the mapping. 692 /// 693 ///\ref named-templ-param "Named parameter" function for setting 694 ///the map that stores the found embedding. 695 template<typename T> 696 Vf2ppWizard< SetMappingBase<T> > mapping(const T &t) { 697 Base::_mapping=reinterpret_cast<void*>(const_cast<T*>(&t)); 698 Base::_local_mapping = 0; 699 return Vf2ppWizard<SetMappingBase<T> >(*this); 700 } 701 702 template<typename NL1, typename NL2> 703 struct SetNodeLabelsBase : public Base { 704 typedef NL1 NodeLabels1; 705 typedef NL2 NodeLabels2; 706 SetNodeLabelsBase(const Base &b) : Base(b) { } 707 }; 708 709 ///\brief \ref named-templ-param "Named parameter" for setting the 710 ///node labels. 711 /// 712 ///\ref named-templ-param "Named parameter" function for setting 713 ///the node labels. 714 /// 715 ///\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. 718 ///\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. 721 template<typename NL1, typename NL2> 722 Vf2ppWizard< SetNodeLabelsBase<NL1,NL2> > 723 nodeLabels(const NL1 &nodeLabels1, const NL2 &nodeLabels2) { 724 Base::_local_nodeLabels = 0; 725 Base::_nodeLabels1= 726 reinterpret_cast<void*>(const_cast<NL1*>(&nodeLabels1)); 727 Base::_nodeLabels2= 728 reinterpret_cast<void*>(const_cast<NL2*>(&nodeLabels2)); 729 return Vf2ppWizard<SetNodeLabelsBase<NL1,NL2> > 730 (SetNodeLabelsBase<NL1,NL2>(*this)); 731 } 732 733 734 ///\brief \ref named-templ-param "Named parameter" for setting 735 ///the mapping type. 736 /// 737 ///\ref named-templ-param "Named parameter" for setting 738 ///the mapping type. 739 /// 740 ///The mapping type is set to \ref SUBGRAPH by default. 741 /// 742 ///\sa See \ref MappingType for the possible values. 743 Vf2ppWizard<Base> &mappingType(MappingType m_type) { 744 _mapping_type = m_type; 745 return *this; 746 } 747 748 ///\brief \ref named-templ-param "Named parameter" for setting 749 ///the mapping type to \ref INDUCED. 750 /// 751 ///\ref named-templ-param "Named parameter" for setting 752 ///the mapping type to \ref INDUCED. 753 Vf2ppWizard<Base> &induced() { 754 _mapping_type = INDUCED; 755 return *this; 756 } 757 758 ///\brief \ref named-templ-param "Named parameter" for setting 759 ///the mapping type to \ref ISOMORPH. 760 /// 761 ///\ref named-templ-param "Named parameter" for setting 762 ///the mapping type to \ref ISOMORPH. 763 Vf2ppWizard<Base> &iso() { 764 _mapping_type = ISOMORPH; 765 return *this; 766 } 767 768 ///Runs the %VF2 Plus Plus algorithm. 769 770 ///This method runs the VF2 Plus Plus algorithm. 771 /// 772 ///\retval true if a mapping is found. 773 ///\retval false if there is no mapping. 774 bool run() { 775 if(Base::_local_mapping) 776 Base::createMapping(); 777 if(Base::_local_nodeLabels) 778 Base::createNodeLabels(); 779 780 Vf2pp<Graph1, Graph2, Mapping, NodeLabels1, NodeLabels2 > 781 alg(_g1, _g2, *reinterpret_cast<Mapping*>(_mapping), 782 *reinterpret_cast<NodeLabels1*>(_nodeLabels1), 783 *reinterpret_cast<NodeLabels2*>(_nodeLabels2)); 784 785 alg.mappingType(_mapping_type); 786 787 const bool ret = alg.find(); 788 789 if(Base::_local_nodeLabels) { 790 delete reinterpret_cast<NodeLabels1*>(_nodeLabels1); 791 delete reinterpret_cast<NodeLabels2*>(_nodeLabels2); 792 } 793 if(Base::_local_mapping) 794 delete reinterpret_cast<Mapping*>(_mapping); 795 796 return ret; 797 } 798 799 ///Get a pointer to the generated Vf2pp object. 800 801 ///Gives a pointer to the generated Vf2pp object. 802 /// 803 ///\return Pointer to the generated Vf2pp object. 804 ///\warning Don't forget to delete the referred Vf2pp object after use. 805 Vf2pp<Graph1, Graph2, Mapping, NodeLabels1, NodeLabels2 >* 806 getPtrToVf2ppObject(){ 807 if(Base::_local_mapping) 808 Base::createMapping(); 809 if(Base::_local_nodeLabels) 810 Base::createNodeLabels(); 811 812 Vf2pp<Graph1, Graph2, Mapping, NodeLabels1, NodeLabels2 >* ptr = 813 new Vf2pp<Graph1, Graph2, Mapping, NodeLabels1, NodeLabels2> 814 (_g1, _g2, *reinterpret_cast<Mapping*>(_mapping), 815 *reinterpret_cast<NodeLabels1*>(_nodeLabels1), 816 *reinterpret_cast<NodeLabels2*>(_nodeLabels2)); 817 ptr->mappingType(_mapping_type); 818 if(Base::_local_mapping) 819 ptr->_deallocMappingAfterUse=true; 820 if(Base::_local_nodeLabels) 821 ptr->_deallocLabelMapsAfterUse=true; 822 823 return ptr; 824 } 825 826 ///Counts the number of mappings. 827 828 ///This method counts the number of mappings. 829 /// 830 /// \return The number of mappings. 831 int count() { 832 if(Base::_local_mapping) 833 Base::createMapping(); 834 if(Base::_local_nodeLabels) 835 Base::createNodeLabels(); 836 837 Vf2pp<Graph1, Graph2, Mapping, NodeLabels1, NodeLabels2> 838 alg(_g1, _g2, *reinterpret_cast<Mapping*>(_mapping), 839 *reinterpret_cast<NodeLabels1*>(_nodeLabels1), 840 *reinterpret_cast<NodeLabels2*>(_nodeLabels2)); 841 842 alg.mappingType(_mapping_type); 843 844 int ret = 0; 845 while(alg.find()) 846 ++ret; 847 848 if(Base::_local_nodeLabels) { 849 delete reinterpret_cast<NodeLabels1*>(_nodeLabels1); 850 delete reinterpret_cast<NodeLabels2*>(_nodeLabels2); 851 } 852 if(Base::_local_mapping) 853 delete reinterpret_cast<Mapping*>(_mapping); 854 855 return ret; 856 } 857 }; 858 859 860 ///Function-type interface for VF2 Plus Plus algorithm. 861 862 /// \ingroup graph_isomorphism 863 ///Function-type interface for VF2 Plus Plus algorithm. 864 /// 865 ///This function has several \ref named-func-param "named parameters" 866 ///declared as the members of class \ref Vf2ppWizard. 867 ///The following examples show how to use these parameters. 868 ///\code 869 /// ListGraph::NodeMap<ListGraph::Node> m(g); 870 /// // Find an embedding of graph g1 into graph g2 871 /// vf2pp(g1,g2).mapping(m).run(); 872 /// 873 /// // Check whether graphs g1 and g2 are isomorphic 874 /// bool is_iso = vf2pp(g1,g2).iso().run(); 875 /// 876 /// // Count the number of isomorphisms 877 /// int num_isos = vf2pp(g1,g2).iso().count(); 878 /// 879 /// // Iterate through all the induced subgraph mappings 880 /// //of graph g1 into g2 using the labels c1 and c2 881 /// auto* myVf2pp = vf2pp(g1,g2).mapping(m).nodeLabels(c1,c2) 882 /// .induced().getPtrToVf2Object(); 883 /// while(myVf2pp->find()){ 884 /// //process the current mapping m 885 /// } 886 /// delete myVf22pp; 887 ///\endcode 888 ///\warning Don't forget to put the \ref Vf2ppWizard::run() "run()", 889 ///\ref Vf2ppWizard::count() "count()" or 890 ///the \ref Vf2ppWizard::getPtrToVf2ppObject() "getPtrToVf2ppObject()" 891 ///to the end of the expression. 892 ///\sa Vf2ppWizard 893 ///\sa Vf2pp 894 template<class G1, class G2> 895 Vf2ppWizard<Vf2ppWizardBase<G1,G2> > vf2pp(const G1 &g1, const G2 &g2) { 896 return Vf2ppWizard<Vf2ppWizardBase<G1,G2> >(g1,g2); 897 } 898 899 } 900 901 #endif 902 -
test/CMakeLists.txt
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
a b 55 55 tsp_test 56 56 unionfind_test 57 57 vf2_test 58 vf2pp_test 58 59 ) 59 60 60 61 IF(LEMON_HAVE_LP) -
test/vf2_test.cc
diff --git a/test/vf2_test.cc b/test/vf2_test.cc
a b 2 2 * 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 * 8 8 * Permission to use, modify and distribute this software is granted … … 183 183 184 184 class EqComparable { 185 185 public: 186 bool operator==(const EqComparable&) { return false; } 186 bool operator==(const EqComparable&) { 187 return false; 188 } 187 189 }; 188 190 189 191 template<class A, class B> 190 192 class EqClass { 191 193 public: 192 bool operator()(A, B) { return false; } 194 bool operator()(A, B){ 195 return false; 196 } 193 197 }; 194 198 195 199 template<class G1,class G2> 196 void checkVf2Compile() 197 { 200 void checkVf2Compile() { 198 201 G1 g; 199 202 G2 h; 200 203 concepts::ReadWriteMap<typename G1::Node, typename G2::Node> r; … … 205 208 succ = vf2(g,h).induced().run(); 206 209 succ = vf2(g,h).iso().run(); 207 210 succ = vf2(g,h).mapping(r).run(); 211 212 Vf2<G1,G2,concepts::ReadWriteMap<typename G1::Node, typename G2::Node>, 213 EqClass<typename G1::Node,typename G2::Node> > 214 myVf2(g,h,r,EqClass<typename G1::Node,typename G2::Node>()); 215 myVf2.find(); 216 208 217 succ = vf2(g,h).induced().mapping(r).run(); 209 218 succ = vf2(g,h).iso().mapping(r).run(); 219 210 220 concepts::ReadMap<typename G1::Node, EqComparable> l1; 211 221 concepts::ReadMap<typename G2::Node, EqComparable> l2; 212 222 succ = vf2(g,h).nodeLabels(l1,l2).mapping(r).run(); … … 214 224 .mapping(r).run(); 215 225 } 216 226 217 void justCompile() 218 { 227 void justCompile() { 219 228 checkVf2Compile<concepts::Graph,concepts::Graph>(); 220 229 checkVf2Compile<concepts::Graph,SmartGraph>(); 221 230 checkVf2Compile<SmartGraph,concepts::Graph>(); 222 231 } 223 232 224 233 template<class G1, class G2, class I> 225 void checkSub(const G1 &g1, const G2 &g2, const I &i) 226 { 234 void checkSub(const G1 &g1, const G2 &g2, const I &i) { 227 235 { 228 236 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 } 237 for(typename G1::NodeIt n(g1);n!=INVALID;++n){ 238 check(i[n]!=INVALID, "Wrong isomorphism: incomplete mapping."); 239 check(image.count(i[n])==0,"Wrong isomorphism: not injective."); 240 image.insert(i[n]); 241 } 235 242 } 236 243 for(typename G1::EdgeIt e(g1);e!=INVALID;++e) 237 244 check(findEdge(g2,i[g1.u(e)],i[g1.v(e)])!=INVALID, … … 239 246 } 240 247 241 248 template<class G1, class G2, class I> 242 void checkInd(const G1 &g1, const G2 &g2, const I &i) 243 { 249 void checkInd(const G1 &g1, const G2 &g2, const I &i) { 244 250 std::set<typename G2::Node> image; 245 for(typename G1::NodeIt n(g1);n!=INVALID;++n) 246 { 251 for(typename G1::NodeIt n(g1);n!=INVALID;++n) { 247 252 check(i[n]!=INVALID, "Wrong isomorphism: incomplete mapping."); 248 253 check(image.count(i[n])==0,"Wrong isomorphism: not injective."); 249 254 image.insert(i[n]); 250 255 } 251 256 for(typename G1::NodeIt n(g1); n!=INVALID; ++n) 252 257 for(typename G1::NodeIt m(g1); m!=INVALID; ++m) 253 if((findEdge(g1,n,m)==INVALID) != (findEdge(g2,i[n],i[m])==INVALID)) 254 { 258 if((findEdge(g1,n,m)==INVALID) != (findEdge(g2,i[n],i[m])==INVALID)) { 255 259 std::cout << "Wrong isomorphism: edge mismatch"; 256 260 exit(1); 257 } 258 } 259 260 template<class G1,class G2> 261 int checkSub(const G1 &g1, const G2 &g2) 262 { 263 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; 261 } 270 262 } 271 263 272 264 template<class G1,class G2> 273 int checkInd(const G1 &g1, const G2 &g2) 274 { 265 int checkSub(const G1 &g1, const G2 &g2) { 275 266 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 elsereturn false;267 if(vf2(g1,g2).mapping(iso).run()) { 268 checkSub(g1,g2,iso); 269 return true; 270 } 271 else 272 return false; 282 273 } 283 274 284 275 template<class G1,class G2> 285 int checkIso(const G1 &g1, const G2 &g2) 286 { 276 int checkInd(const G1 &g1, const G2 &g2) { 287 277 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; 278 if(vf2(g1,g2).induced().mapping(iso).run()) { 279 checkInd(g1,g2,iso); 280 return true; 281 } 282 else 283 return false; 284 } 285 286 template<class G1,class G2> 287 int checkIso(const G1 &g1, const G2 &g2) { 288 typename G1:: template NodeMap<typename G2::Node> iso(g1,INVALID); 289 if(vf2(g1,g2).iso().mapping(iso).run()) { 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 296 return false; 296 297 } 297 298 298 299 template<class G1, class G2, class L1, class L2, class I> 299 300 void checkLabel(const G1 &g1, const G2 &, 300 const L1 &l1, const L2 &l2,const I &i) 301 { 301 const L1 &l1, const L2 &l2,const I &i) { 302 302 for(typename G1::NodeIt n(g1);n!=INVALID;++n) 303 { 304 check(l1[n]==l2[i[n]],"Wrong isomorphism: label mismatch."); 305 } 303 check(l1[n]==l2[i[n]],"Wrong isomorphism: label mismatch."); 306 304 } 307 305 308 306 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 { 307 int checkSub(const G1 &g1, const G2 &g2, const L1 &l1, const L2 &l2) { 311 308 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 elsereturn false;309 if(vf2(g1,g2).nodeLabels(l1,l2).mapping(iso).run()){ 310 checkSub(g1,g2,iso); 311 checkLabel(g1,g2,l1,l2,iso); 312 return true; 313 } 314 else 315 return false; 319 316 } 320 317 321 318 int main() { 322 319 make_graphs(); 320 // justCompile(); 323 321 check(checkSub(c5,petersen), "There should exist a C5->Petersen mapping."); 324 322 check(!checkSub(c7,petersen), 325 323 "There should not exist a C7->Petersen mapping."); -
new file test/vf2pp_test.cc
diff --git a/test/vf2pp_test.cc b/test/vf2pp_test.cc new file mode 100644
- + 1 /* -*- mode: C++; indent-tabs-mode: nil; -*- 2 * 3 * This file is a part of LEMON, a generic C++ optimization library. 4 * 5 * Copyright (C) 2015-2017 6 * EMAXA Kutato-fejleszto Kft. (EMAXA Research Ltd.) 7 * 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. 11 * 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 14 * purpose. 15 * 16 */ 17 18 #include <lemon/vf2pp.h> 19 #include <lemon/concepts/digraph.h> 20 #include <lemon/smart_graph.h> 21 #include <lemon/lgf_reader.h> 22 #include <lemon/concepts/maps.h> 23 #include <lemon/maps.h> 24 #include <lemon/list_graph.h> 25 26 #include <test/test_tools.h> 27 #include <sstream> 28 29 using namespace lemon; 30 31 char petersen_lgf[] = 32 "@nodes\n" 33 "label col1 col2\n" 34 "0 1 1\n" 35 "1 1 2\n" 36 "2 1 3\n" 37 "3 1 4\n" 38 "4 2 5\n" 39 "5 2 1\n" 40 "6 2 2\n" 41 "7 2 3\n" 42 "8 2 4\n" 43 "9 2 5\n" 44 "@arcs\n" 45 " -\n" 46 "0 1\n" 47 "1 2\n" 48 "2 3\n" 49 "3 4\n" 50 "4 0\n" 51 "0 5\n" 52 "1 6\n" 53 "2 7\n" 54 "3 8\n" 55 "4 9\n" 56 "5 8\n" 57 "5 7\n" 58 "9 6\n" 59 "9 7\n" 60 "6 8\n"; 61 62 char c5_lgf[] = 63 "@nodes\n" 64 "label col\n" 65 "0 1\n" 66 "1 2\n" 67 "2 3\n" 68 "3 4\n" 69 "4 5\n" 70 "@arcs\n" 71 " -\n" 72 "0 1\n" 73 "1 2\n" 74 "2 3\n" 75 "3 4\n" 76 "4 0\n"; 77 78 char c7_lgf[] = 79 "@nodes\n" 80 "label\n" 81 "0\n" 82 "1\n" 83 "2\n" 84 "3\n" 85 "4\n" 86 "5\n" 87 "6\n" 88 "@arcs\n" 89 " -\n" 90 "0 1\n" 91 "1 2\n" 92 "2 3\n" 93 "3 4\n" 94 "4 5\n" 95 "5 6\n" 96 "6 0\n"; 97 98 char c10_lgf[] = 99 "@nodes\n" 100 "label\n" 101 "0\n" 102 "1\n" 103 "2\n" 104 "3\n" 105 "4\n" 106 "5\n" 107 "6\n" 108 "7\n" 109 "8\n" 110 "9\n" 111 "@arcs\n" 112 " -\n" 113 "0 1\n" 114 "1 2\n" 115 "2 3\n" 116 "3 4\n" 117 "4 5\n" 118 "5 6\n" 119 "6 7\n" 120 "7 8\n" 121 "8 9\n" 122 "9 0\n"; 123 124 char p10_lgf[] = 125 "@nodes\n" 126 "label\n" 127 "0\n" 128 "1\n" 129 "2\n" 130 "3\n" 131 "4\n" 132 "5\n" 133 "6\n" 134 "7\n" 135 "8\n" 136 "9\n" 137 "@arcs\n" 138 " -\n" 139 "0 1\n" 140 "1 2\n" 141 "2 3\n" 142 "3 4\n" 143 "4 5\n" 144 "5 6\n" 145 "6 7\n" 146 "7 8\n" 147 "8 9\n"; 148 149 SmartGraph petersen, c5, c7, c10, p10; 150 SmartGraph::NodeMap<int> petersen_col1(petersen); 151 SmartGraph::NodeMap<int> petersen_col2(petersen); 152 SmartGraph::NodeMap<int> c5_col(c5); 153 154 void make_graphs(){ 155 std::stringstream ss(petersen_lgf); 156 graphReader(petersen, ss) 157 .nodeMap("col1",petersen_col1) 158 .nodeMap("col2",petersen_col2) 159 .run(); 160 161 ss.clear(); 162 ss.str(""); 163 ss<<c5_lgf; 164 165 graphReader(c5, ss) 166 .nodeMap("col",c5_col) 167 .run(); 168 169 ss.clear(); 170 ss.str(""); 171 ss<<c7_lgf; 172 graphReader(c7, ss).run(); 173 174 ss.clear(); 175 ss.str(""); 176 ss<<c10_lgf; 177 graphReader(c10, ss).run(); 178 179 ss.clear(); 180 ss.str(""); 181 ss<<p10_lgf; 182 graphReader(p10, ss).run(); 183 184 } 185 186 class IntConvertible1{ 187 public: 188 operator int(){ 189 return 0; 190 } 191 }; 192 193 class IntConvertible2{ 194 public: 195 operator int(){ 196 return 0; 197 } 198 }; 199 200 template<class G1,class G2> 201 void checkVf2Compile() { 202 G1 g; 203 G2 h; 204 concepts::ReadWriteMap<typename G1::Node, typename G2::Node> r; 205 bool succ; 206 ::lemon::ignore_unused_variable_warning(succ); 207 208 succ = vf2pp(g,h).run(); 209 succ = vf2pp(g,h).induced().run(); 210 succ = vf2pp(g,h).iso().run(); 211 succ = vf2pp(g,h).mapping(r).run(); 212 succ = vf2pp(g,h).induced().mapping(r).run(); 213 succ = vf2pp(g,h).iso().mapping(r).run(); 214 215 216 concepts::ReadMap<typename G1::Node, int> c1; 217 concepts::ReadMap<typename G2::Node, int> c2; 218 Vf2pp<G1,G2,concepts::ReadWriteMap<typename G1::Node, typename G2::Node>, 219 concepts::ReadMap<typename G1::Node, int>, 220 concepts::ReadMap<typename G2::Node, int> > 221 myVf2pp(g,h,r,c1,c2); 222 myVf2pp.find(); 223 224 succ = vf2pp(g,h).nodeLabels(c1,c2).mapping(r).run(); 225 succ = vf2pp(g,h).nodeLabels(c1,c2) 226 .mapping(r).run(); 227 228 229 concepts::ReadMap<typename G1::Node, char> c1_c; 230 concepts::ReadMap<typename G2::Node, char> c2_c; 231 Vf2pp<G1,G2,concepts::ReadWriteMap<typename G1::Node, typename G2::Node>, 232 concepts::ReadMap<typename G1::Node, char>, 233 concepts::ReadMap<typename G2::Node, char> > 234 myVf2pp_c(g,h,r,c1_c,c2_c); 235 myVf2pp_c.find(); 236 237 succ = vf2pp(g,h).nodeLabels(c1_c,c2_c).mapping(r).run(); 238 succ = vf2pp(g,h).nodeLabels(c1_c,c2_c) 239 .mapping(r).run(); 240 241 242 concepts::ReadMap<typename G1::Node, IntConvertible1> c1_IntConv; 243 concepts::ReadMap<typename G2::Node, IntConvertible2> c2_IntConv; 244 Vf2pp<G1,G2,concepts::ReadWriteMap<typename G1::Node, typename G2::Node>, 245 concepts::ReadMap<typename G1::Node, IntConvertible1>, 246 concepts::ReadMap<typename G2::Node, IntConvertible2> > 247 myVf2pp_IntConv(g,h,r,c1_IntConv,c2_IntConv); 248 myVf2pp_IntConv.find(); 249 250 succ = vf2pp(g,h).nodeLabels(c1_IntConv,c2_IntConv).mapping(r).run(); 251 succ = vf2pp(g,h).nodeLabels(c1_IntConv,c2_IntConv) 252 .mapping(r).run(); 253 } 254 255 void justCompile() { 256 checkVf2Compile<concepts::Graph,concepts::Graph>(); 257 checkVf2Compile<concepts::Graph,SmartGraph>(); 258 checkVf2Compile<SmartGraph,concepts::Graph>(); 259 } 260 261 template<class G1, class G2, class I> 262 void checkSub(const G1 &g1, const G2 &g2, const I &i) { 263 { 264 std::set<typename G2::Node> image; 265 for(typename G1::NodeIt n(g1);n!=INVALID;++n){ 266 check(i[n]!=INVALID, "Wrong isomorphism: incomplete mapping."); 267 check(image.count(i[n])==0,"Wrong isomorphism: not injective."); 268 image.insert(i[n]); 269 } 270 } 271 for(typename G1::EdgeIt e(g1);e!=INVALID;++e) 272 check(findEdge(g2,i[g1.u(e)],i[g1.v(e)])!=INVALID, 273 "Wrong isomorphism: missing edge(checkSub)."); 274 } 275 276 template<class G1, class G2, class I> 277 void checkInd(const G1 &g1, const G2 &g2, const I &i) { 278 std::set<typename G2::Node> image; 279 for(typename G1::NodeIt n(g1);n!=INVALID;++n) { 280 check(i[n]!=INVALID, "Wrong isomorphism: incomplete mapping."); 281 check(image.count(i[n])==0,"Wrong isomorphism: not injective."); 282 image.insert(i[n]); 283 } 284 for(typename G1::NodeIt n(g1); n!=INVALID; ++n) 285 for(typename G1::NodeIt m(g1); m!=INVALID; ++m) 286 if((findEdge(g1,n,m)==INVALID) != (findEdge(g2,i[n],i[m])==INVALID)) { 287 std::cout << "Wrong isomorphism: edge mismatch"; 288 exit(1); 289 } 290 } 291 292 template<class G1,class G2> 293 int checkSub(const G1 &g1, const G2 &g2) { 294 typename G1:: template NodeMap<typename G2::Node> iso(g1,INVALID); 295 if(vf2pp(g1,g2).mapping(iso).run()){ 296 checkSub(g1,g2,iso); 297 return true; 298 } 299 else 300 return false; 301 } 302 303 template<class G1,class G2> 304 int checkInd(const G1 &g1, const G2 &g2) { 305 typename G1:: template NodeMap<typename G2::Node> iso(g1,INVALID); 306 if(vf2pp(g1,g2).induced().mapping(iso).run()) { 307 checkInd(g1,g2,iso); 308 return true; 309 } 310 else 311 return false; 312 } 313 314 template<class G1,class G2> 315 int checkIso(const G1 &g1, const G2 &g2) { 316 typename G1:: template NodeMap<typename G2::Node> iso(g1,INVALID); 317 if(vf2pp(g1,g2).iso().mapping(iso).run()) { 318 check(countNodes(g1)==countNodes(g2), 319 "Wrong iso alg.: they are not isomophic."); 320 checkInd(g1,g2,iso); 321 return true; 322 } 323 else 324 return false; 325 } 326 327 template<class G1, class G2, class L1, class L2, class I> 328 void checkLabel(const G1 &g1, const G2 &, 329 const L1 &l1, const L2 &l2,const I &i) { 330 for(typename G1::NodeIt n(g1);n!=INVALID;++n) 331 check(l1[n]==l2[i[n]],"Wrong isomorphism: label mismatch."); 332 } 333 334 template<class G1,class G2,class L1,class L2> 335 int checkSub(const G1 &g1, const G2 &g2, const L1 &l1, const L2 &l2) { 336 typename G1:: template NodeMap<typename G2::Node> iso(g1,INVALID); 337 if(vf2pp(g1,g2).nodeLabels(l1,l2).mapping(iso).run()) { 338 checkSub(g1,g2,iso); 339 checkLabel(g1,g2,l1,l2,iso); 340 return true; 341 } 342 else 343 return false; 344 } 345 346 int main() { 347 make_graphs(); 348 // justCompile(); 349 check(checkSub(c5,petersen), "There should exist a C5->Petersen mapping."); 350 check(!checkSub(c7,petersen), 351 "There should not exist a C7->Petersen mapping."); 352 check(checkSub(p10,petersen), "There should exist a P10->Petersen mapping."); 353 check(!checkSub(c10,petersen), 354 "There should not exist a C10->Petersen mapping."); 355 check(checkSub(petersen,petersen), 356 "There should exist a Petersen->Petersen mapping."); 357 358 check(checkInd(c5,petersen), 359 "There should exist a C5->Petersen spanned mapping."); 360 check(!checkInd(c7,petersen), 361 "There should exist a C7->Petersen spanned mapping."); 362 check(!checkInd(p10,petersen), 363 "There should not exist a P10->Petersen spanned mapping."); 364 check(!checkInd(c10,petersen), 365 "There should not exist a C10->Petersen spanned mapping."); 366 check(checkInd(petersen,petersen), 367 "There should exist a Petersen->Petersen spanned mapping."); 368 369 check(!checkSub(petersen,c10), 370 "There should not exist a Petersen->C10 mapping."); 371 check(checkSub(p10,c10), 372 "There should exist a P10->C10 mapping."); 373 check(!checkInd(p10,c10), 374 "There should not exist a P10->C10 spanned mapping."); 375 check(!checkSub(c10,p10), 376 "There should not exist a C10->P10 mapping."); 377 378 check(!checkIso(p10,c10), 379 "P10 and C10 are not isomorphic."); 380 check(checkIso(c10,c10), 381 "C10 and C10 are isomorphic."); 382 383 check(!vf2pp(p10,c10).iso().run(), 384 "P10 and C10 are not isomorphic."); 385 check(vf2pp(c10,c10).iso().run(), 386 "C10 and C10 are isomorphic."); 387 388 check(!checkSub(c5,petersen,c5_col,petersen_col1), 389 "There should exist a C5->Petersen mapping."); 390 check(checkSub(c5,petersen,c5_col,petersen_col2), 391 "There should exist a C5->Petersen mapping."); 392 }