Zoltan2
Zoltan2_MatcherHelper.hpp
Go to the documentation of this file.
1 #ifndef _ZOLTAN2_MatcherHelper_hpp_
2 #define _ZOLTAN2_MatcherHelper_hpp_
3 
4 //#define _ZOLTAN2_MatcherHelper_hpp_
5 
6 
7 #ifdef ZOLTAN2ND_HAVE_OMP
8 #include <omp.h>
9 #endif
10 
11 #include <iostream>
12 #include <fstream>
13 #include <string>
14 #include <vector>
15 #include <algorithm>
16 #include <cstdlib>
17 #include <set>
18 #include <queue>
19 #include <cassert>
20 
21 // PPF Matching code copied from Isorropia. Siva has reservations about algorithm
22 // robustness. It uses recursion, which can cause problems on the stack.
23 // Probably should rewrite at least some of these algorithms, eventually.
24 
25 
26 namespace Zoltan2 {
27 
36 
38 
39 template <typename LO>
40 class Matcher
41 {
42 private:
43 
44  LO *mCRS_rowPtrs;
45  LO *mCRS_cols;
46 
47  // Number of vertices in u set (num row vertices)
48  LO numU;
49 
50  // Number of vertices in v set (num col vertices)
51  LO numV;
52 
53  // For each u vertex, the matching v vertex
54  std::vector<LO> matchedVertexForU;
55 
56  // For each v vertex, the matching u vertex
57  std::vector<LO> matchedVertexForV;
58 
59  std::vector<LO> queue;
60  LO* LV_;
61  LO* LU_;
62  LO* unmatchedU_;
63  LO *parent_;
64  LO *lookahead_;
65  bool finish_;
66  LO numE,avgDegU_,k_star_,icm_,BFSInd_,numThread_,Qst_,Qend_,numMatched;
67 
68  LO *visitedV_;
69 
70  void delete_matched_v();
71  LO SGM();
72  LO match_dfs();
73  LO match_hk();
74  LO construct_layered_graph();
75  LO find_set_del_M();
76  LO recursive_path_finder(LO, LO);
77  LO dfs_path_finder(LO);
78  LO dfs_augment();
79  LO augment_matching(LO);
80  LO DW_phase();
81 
82 public:
90  Matcher(LO *_rowPtr, LO *_cols, LO _numU, LO _numV, LO _numE);
91 
92 
93 
94 
98  virtual ~Matcher();
99 
100  /* @ingroup matching_grp
101  Returns the number of matched vertices from Maximum Cardinality
102  Matching set
103  */
105  {
106  LO count=0;
107  for(unsigned int i=0;i<matchedVertexForU.size(); i++)
108  {
109  if(matchedVertexForU[i]!=-1)
110  {
111  count++;
112  }
113  }
114  return count;
115  }
116 
117 
118 
119  const std::vector<LO> &getVertexUMatches() {return matchedVertexForU;};
120  const std::vector<LO> &getVertexVMatches() {return matchedVertexForV;};
121 
122 
123  // Perhaps should be moved outside of class eventually or reworked to use internal variables
124  void getVCfromMatching(const std::vector<LO> &bigraphCRSRowPtr,
125  std::vector<LO> &bigraphCRSCols,
126  const std::vector<LO> &vertUMatches,
127  const std::vector<LO> &vertVMatches,
128  const std::vector<LO> &bigraphVMapU,
129  const std::vector<LO> &bigraphVMapV,
130  std::vector<LO> &VC);
131 
132 
136  LO match();
137 };
139 
142 template <typename LO>
143 Matcher<LO>::Matcher(LO *_rowPtr, LO *_cols, LO _numU, LO _numV, LO _numE)
144  :mCRS_rowPtrs(_rowPtr),mCRS_cols(_cols),numU(_numU),numV(_numV),numE(_numE)
145 {
146  finish_=false;
147  avgDegU_=numE/numU+1;
148  matchedVertexForU.resize(numU);
149  matchedVertexForV.resize(numV);
150 
151  lookahead_=new LO[numU];
152  unmatchedU_=new LO[numU];
153 
154  for(LO i=0;i<numU;i++)
155  {
156  matchedVertexForU[i]=-1;
157 
158  lookahead_[i]=mCRS_rowPtrs[i];
159  unmatchedU_[i]=i;
160  }
161 
162  visitedV_=new LO[numV];
163 
164  parent_=new LO[numV];
165 
166  for(LO i=0;i<numV;i++)
167  {
168  visitedV_[i]=0;
169 
170  matchedVertexForV[i]=-1;
171  parent_[i]=-1;
172  }
173 
174  numThread_=1;
175 }
177 
178 
181 template <typename LO>
183 {
184  delete [] lookahead_;
185  delete [] unmatchedU_;
186 
187  if (parent_) delete [] parent_; parent_ = NULL;
188 
189  if(visitedV_)
190  {
191  delete [] visitedV_;
192  }
193 
194 }
196 
198 // This function increases the matching size by one with the help of a
199 // augmenting path. The input is an integer which is the id of the last
200 // columns vertex of the augmenting path. We trace the whole path by
201 // backtraking using parent array. while tracing back we flip the unmatched
202 // edges to matched edges.
204 template <typename LO>
206 {
207  LO u,v,t,lnt=1;
208  v=tv;
209  while(true)
210  {
211  u=parent_[v];
212  t=matchedVertexForU[u];
213  matchedVertexForV[v]=u;
214  matchedVertexForU[u]=v;
215  if(t==-1)
216  break;
217  lnt+=2;
218  v=t;
219  }
220 
221  return lnt;
222 }
224 
226 // This function is almost similar to the previous function which is to find
227 // a vertex disjoint path. It is used for the algorithm DFS, PPF and in HKDW. The
228 // difference is that this function operates on the original graph not on the
229 // layerd subgraph. It also does the incorporates the lookahead mechanism and
230 // scanning adjacency list alternately from backward and from forward in
231 // alternate iterations for PPF and HKDW.
233 template <typename LO>
235 {
236  LO i,ind=-1,res=0;
237 
238  for(i=lookahead_[u];i<mCRS_rowPtrs[u+1];i++) // the lookahead scheme
239  {
240  ind=mCRS_cols[i];
241  assert(ind>=0 && ind<numV);
242  if(matchedVertexForV[ind]==-1)
243  {
244  LO lock2=0;
245  if (visitedV_[ind]==0)
246  {
247  visitedV_[ind]=1;
248  lock2=1;
249  }
250 
251  if(lock2>0)
252  {
253  parent_[ind]=u;
254  lookahead_[u]=i+1;
255  return ind;
256  }
257  }
258  }
259 
260 
261  if(icm_%2==1) // odd number iteration so scan the adj list forward dir
262  {
263  for(i=mCRS_rowPtrs[u];i<mCRS_rowPtrs[u+1];i++)
264  {
265  ind=mCRS_cols[i];
266  assert(ind>=0 && ind<numV);
267 
268  LO lock2=0;
269  if (visitedV_[ind]==0)
270  {
271  visitedV_[ind]=1;
272  lock2=1;
273  }
274 
275  if(lock2>0)
276  {
277  parent_[ind]=u;
278  res=dfs_path_finder(matchedVertexForV[ind]);
279  if(res!=-1)
280  return res;
281  }
282  }
283  }
284  else // even number iteration so scan from backward
285  {
286  for(i=mCRS_rowPtrs[u+1]-1;i>=mCRS_rowPtrs[u];i--)
287  {
288  ind=mCRS_cols[i];
289  assert(ind>=0 && ind<numV);
290 
291 
292  LO lock2=0;
293  if (visitedV_[ind]==0)
294  {
295  visitedV_[ind]=1;
296  lock2=1;
297  }
298 
299  if(lock2>0)
300  {
301  parent_[ind]=u;
302  res=dfs_path_finder(matchedVertexForV[ind]);
303  if(res!=-1)
304  return res;
305  }
306  }
307  }
308 
309 
310  return -1;
311 }
313 
314 // ////////////////////////////////////////////////////////////////////////////////
315 // // This function starts the BFS phase for the HK and HKDW. It starts from the
316 // // layer 0 vertices and tries to find a vertex disjoint path from each of
317 // // these vertices.
318 // ////////////////////////////////////////////////////////////////////////////////
319 // int Matcher::find_set_del_M()
320 // {
321 
322 // int i,j,count=0;
323 // delete_matched_v();
324 
325 // #ifdef ZOLTAN2ND_HAVE_OMP
326 // #pragma omp parallel for private(j)
327 // #endif
328 // for(i=0;i<BFSInd_;i++)
329 // {
330 
331 // j=recursive_path_finder(0,queue[i]);
332 // if(j!=-1)
333 // {
334 // augment_matching(j);
335 
336 // //MMW: Not 100% this is necessary, let this in just in case bug in original code
337 // #ifdef ZOLTAN2ND_HAVE_OMP
338 // #pragma omp atomic
339 // #endif
340 // count++;
341 
342 
343 
344 // }
345 
346 // }
347 // return count;
348 // }
349 // ////////////////////////////////////////////////////////////////////////////////
350 
351 // ////////////////////////////////////////////////////////////////////////////////
352 // // This is the additional Duff and Wiberg phase for the HKDW. This function
353 // // does nothing but first unset the locks and then runs PPF from the
354 // // remaining unmatched row vertices after the BFS phase of HK.
355 // ////////////////////////////////////////////////////////////////////////////////
356 // int Matcher::DW_phase()
357 // {
358 // int i,count=0;
359 
360 // #ifdef ZOLTAN2ND_HAVE_OMP
361 // #pragma omp parallel for
362 // #endif
363 // for(i=0;i<numV;i++)
364 // {
365 // #ifdef ZOLTAN2ND_HAVE_OMP
366 // omp_unset_lock(&scannedV_[i]); // unsetting the locks
367 // #endif
368 // }
369 
370 // #ifdef ZOLTAN2ND_HAVE_OMP
371 // #pragma omp parallel for
372 // #endif
373 // for(i=0;i<BFSInd_;i++) // calling the PPF
374 // {
375 // int u=queue[i];
376 // if(matchedVertexForU[u]==-1)
377 // {
378 // int ind=dfs_path_finder(u);
379 // if(ind!=-1)
380 // {
381 // augment_matching(ind);
382 
383 // //MMW: Not 100% this is necessary, let this in just in case bug in original code
384 // #ifdef ISORROPIA_HAVE_OMP
385 // #pragma omp atomic
386 // #endif
387 // count++;
388 
389 // }
390 // }
391 // }
392 // return count;
393 // }
394 // ////////////////////////////////////////////////////////////////////////////////
395 
397  //This function is the starter function for PPF and DFS. It unsets the locks
398  //the call dfs_path_finder and then call the augment_matching.
400 template <typename LO>
402 {
403  LO i,flag=0,flag1=0,count=0,totc=0,index=numU,cur=0;
404  icm_=0;
405 
406  while(true)
407  {
408  icm_++;
409 
410  for(i=0;i<numV;i++)
411  {
412  visitedV_[i]=0;
413  }
414 
415  cur=0;
416  for(i=0;i<numU;i++)
417  {
418  if(matchedVertexForU[i]==-1)
419  unmatchedU_[cur++]=i;
420  }
421  index=cur;
422  flag=flag1=count=0;
423 
424  for(i=0;i<index;i++)
425  {
426  flag=1;
427  LO u=unmatchedU_[i];
428  LO ind=dfs_path_finder(u);
429  if(ind!=-1)
430  {
431  flag1=1;
432  augment_matching(ind);
433  }
434  }
435 
436  if(flag==0 || flag1==0)
437  break;
438  else
439  {
440  cur=0;
441  for(i=0;i<index;i++)
442  {
443  if(matchedVertexForU[unmatchedU_[i]]==-1)
444  unmatchedU_[cur++]=unmatchedU_[i];
445  }
446  index=cur;
447 
448  }
449  }
450 
451  return totc;
452 }
454 
455 // ////////////////////////////////////////////////////////////////////////////////
456 // ////////////////////////////////////////////////////////////////////////////////
457 // int Matcher::SGM()
458 // {
459 // int i,j,lock,ind,count=0;
460 // #ifdef ZOLTAN2ND_HAVE_OMP
461 // #pragma omp parallel for private(j,ind,lock)
462 // #endif
463 // for(i=0;i<numU;i++)
464 // {
465 // for(j=mCRS_rowPtrs[i];j<mCRS_rowPtrs[i+1];j++)
466 // {
467 // ind=mCRS_cols[j];
468 // #ifdef ZOLTAN2ND_HAVE_OMP
469 // lock=omp_test_lock(&scannedV_[ind]);
470 // #else
471 // // mfh 07 Feb 2013: lock wasn't getting initialized if
472 // // ZOLTAN2ND_HAVE_OMP was not defined. omp_test_lock()
473 // // returns nonzero if the thread successfully acquired the
474 // // lock. If there's only one thread, that thread will
475 // // always be successful, so the default value of lock
476 // // should be nonzero.
477 // lock = 1;
478 // #endif
479 // if(lock>0)
480 // {
481 // matchedVertexForU[i]=ind;
482 // matchedVertexForV[ind]=i;
483 // break;
484 // }
485 // }
486 // }
487 // return count;
488 // }
489 // ////////////////////////////////////////////////////////////////////////////////
490 
491 
494 template <typename LO>
495 LO Matcher<LO>::SGM()
496 {
497  LO i,j,ind,count=0;
498 
499  for(i=0;i<numU;i++)
500  {
501  for(j=mCRS_rowPtrs[i];j<mCRS_rowPtrs[i+1];j++)
502  {
503  ind=mCRS_cols[j];
504 
505  LO lock2=0;
506  if (visitedV_[ind]==0)
507  {
508  visitedV_[ind]=1;
509  lock2=1;
510  }
511 
512  if(lock2>0)
513  {
514  matchedVertexForU[i]=ind;
515  matchedVertexForV[ind]=i;
516  break;
517  }
518  }
519  }
520  return count;
521 }
523 
524 
525 
527 // Forking function for DFS based algorithm
529 template <typename LO>
531 {
532  LO totc=0;
533  icm_=0;
534 
535  totc=dfs_augment();
536 
537  return totc;
538 }
540 
543 template <typename LO>
545 {
546  // User interface function for the matching..
547 
548  LO totc=0;
549  totc=SGM();
550  totc+=match_dfs();
551 
552  numMatched = totc;
553 
554  return 0;
555 }
557 
558 
560 // Calculate vertex cover (which is vertex separator) from matching
561 // VC = (U-L) union (B intersection L)
562 //
563 // Let X be the exposed nodes in U (those without a match)
564 //
565 // E':
566 // Matched edges should be directed VtoU -- just use vertVMatches array
567 // All other edges should be directed UtoV -- use modified biGraphCRScols
568 //
569 // L is the set of vertices in (U union V, E') that can be reached from X
570 //
571 //
572 // Perhaps I should use internal class members in this function
573 // However, I decided not to do this for now since I'm changing some of these
574 // member variables in this function
576 template <typename LO>
577 void Matcher<LO>::getVCfromMatching(const std::vector<LO> &bigraphCRSRowPtr,
578  std::vector<LO> &bigraphCRSCols,
579  const std::vector<LO> &vertSMatches,
580  const std::vector<LO> &vertTMatches,
581  const std::vector<LO> &bigraphVMapU,
582  const std::vector<LO> &bigraphVMapV,
583  std::vector<LO> &VC)
584 {
585  LO numS = vertSMatches.size();
586  LO numT = vertTMatches.size();
587 
589  // Build X
591  std::set<LO> X;
592  for(LO i=0;i<numS; i++)
593  {
594  if(vertSMatches[i]==-1)
595  {
596  X.insert(i);
597  }
598  }
600 
602  // Non matched edges should be directed UtoV
603  // Removed matches edges from bipartite graph (set to -1)
604  // -- perhaps replace this with something more efficient/compact
606  for (LO uID=0;uID<numS;uID++)
607  {
608  for (LO eIdx=bigraphCRSRowPtr[uID];eIdx<bigraphCRSRowPtr[uID+1];eIdx++)
609  {
610  LO vID = bigraphCRSCols[eIdx];
611 
612  if (vertSMatches[uID]==vID)
613  {
614  bigraphCRSCols[eIdx]=-1;
615  }
616  }
617 
618  }
620 
622  // Calculate L - set of vertices in (U union V, E') that can be reached from X
624  std::set<LO> L;
625 
626  std::queue<LO> vqueue;
627 
628  std::copy(X.begin(), X.end(), std::inserter( L, L.begin() ) );
629 
630 
631 
632  typename std::set<LO>::const_iterator iter;
633  for(iter = X.begin(); iter != X.end(); ++iter)
634  {
635  L.insert(bigraphVMapU[*iter]); // L contains all vertices in X
636 
637  vqueue.push(*iter); // copy on to vqueue
638  }
639 
640  LO iteration=0;
641  while(vqueue.size()!=0)
642  {
643  //Number of active vertices on this side of bipartite graph
644  LO nstarts=vqueue.size();
645 
646  for (LO i=0; i<nstarts; i++)
647  {
648  LO start = vqueue.front();
649  vqueue.pop();
650 
651  //Traverse from U to V
652  if(iteration%2==0)
653  {
654  //Traverse edges starting from vertex "start"
655  for (LO eIdx=bigraphCRSRowPtr[start];eIdx<bigraphCRSRowPtr[start+1];eIdx++)
656  {
657  LO newV = bigraphCRSCols[eIdx];
658 
659  //Edge is in correct direction (U to V) => newV is valid
660  if (newV!=-1)
661  {
662  // If this vertex has not been reached
663  if(L.find(bigraphVMapV[newV])==L.end())
664  {
665  L.insert(bigraphVMapV[newV]);
666  vqueue.push(newV);
667  }
668  }
669  }
670 
671  }
672 
673  //Traverse from V to U
674  else
675  {
676  LO newU = vertTMatches[start];
677 
678  // If this vertex has not been reached
679  if(L.find(bigraphVMapU[newU])==L.end())
680  {
681  L.insert(bigraphVMapU[newU]);
682  vqueue.push(newU);
683  }
684  }
685  } // for
686 
687  iteration++;
688  } //while
690 
692  // Calc VC = (U-L) union (B intersection L)
694  for(LO uID=0;uID<numS;uID++)
695  {
696  // if vertex not in L, it is in VC
697  if(L.find(bigraphVMapU[uID])==L.end())
698  {
699  VC.push_back(bigraphVMapU[uID]);
700  }
701  }
702 
703  for(LO vID=0;vID<numT;vID++)
704  {
705  // if vertex is in L, it is in VC
706  if(L.find(bigraphVMapV[vID])!=L.end())
707  {
708  VC.push_back(bigraphVMapV[vID]);
709  }
710  }
712 
713 
714 
715 }
717 
718 
719 
720 
721 
722 
723 
724 
725 
726 
727 
728 
729 
730 } //Zoltan2 namespace
731 #endif //ifdef
LO match()
Computes the maximum cardinality matching.
void getVCfromMatching(const std::vector< LO > &bigraphCRSRowPtr, std::vector< LO > &bigraphCRSCols, const std::vector< LO > &vertUMatches, const std::vector< LO > &vertVMatches, const std::vector< LO > &bigraphVMapU, const std::vector< LO > &bigraphVMapV, std::vector< LO > &VC)
An implementation of the Matcher interface that operates on Epetra matrices and Graphs.
const std::vector< LO > & getVertexUMatches()
const std::vector< LO > & getVertexVMatches()
virtual ~Matcher()
Destructor.
Matcher(LO *_rowPtr, LO *_cols, LO _numU, LO _numV, LO _numE)
Constructor.