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