Zoltan2
Loading...
Searching...
No Matches
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
35namespace Zoltan2 {
36
46
47
48template <typename LO>
49class Matcher
50{
51private:
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
91public:
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
151template <typename LO>
152Matcher<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
190template <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.
216template <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.
245template <typename LO>
246LO 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.
412template <typename LO>
413LO 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
506template <typename LO>
507LO 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
541template <typename LO>
542LO Matcher<LO>::match_dfs()
543{
544 LO totc=0;
545 icm_=0;
546
547 totc=dfs_augment();
548
549 return totc;
550}
552
555template <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
588template <typename LO>
589void 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
An implementation of the Matcher interface that operates on Epetra matrices and Graphs.
const std::vector< LO > & getVertexVMatches()
virtual ~Matcher()
Destructor.
const std::vector< LO > & getVertexUMatches()
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)
LO match()
Computes the maximum cardinality matching.
Matcher(LO *_rowPtr, LO *_cols, LO _numU, LO _numV, LO _numE)
Constructor.
Created by mbenlioglu on Aug 31, 2020.