COMBINATORIAL_BLAS  1.6
ApproxWeightPerfectMatching.cpp
Go to the documentation of this file.
1 
2 #ifdef THREADED
3 #ifndef _OPENMP
4 #define _OPENMP
5 #endif
6 
7 #include <omp.h>
8 int cblas_splits = 1;
9 #endif
10 
11 #include "../CombBLAS.h"
12 #include <mpi.h>
13 #include <sys/time.h>
14 #include <iostream>
15 #include <functional>
16 #include <algorithm>
17 #include <vector>
18 #include <string>
19 #include <sstream>
20 #include <limits>
21 
22 
23 #include "BPMaximalMatching.h"
24 #include "BPMaximumMatching.h"
26 
27 using namespace std;
28 using namespace combblas;
29 
30 // algorithmic options
32 int init;
34 bool fewexp;
35 bool randPerm;
37 string ofname;
38 
39 
45 
46 void ShowUsage()
47 {
48  int myrank;
49  MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
50  if(myrank == 0)
51  {
52  cout << "\n-------------- usage --------------\n";
53  cout << "Usage: ./awpm -input <filename>\n";
54  cout << "Optional parameters: -randPerm: randomly permute the matrix for load balance (default: no random permutation)\n";
55  cout << " -optsum: Optimize the sum of diagonal (default: Optimize the product of diagonal)\n";
56  cout << " -noWeightedCard: do not use weighted cardinality matching (default: use weighted cardinality matching)\n";
57  cout << " -output <output file>: output file name (if not provided: inputfile.awpm.txt)\n";
58  cout << " \n-------------- examples ----------\n";
59  cout << "Example: mpirun -np 4 ./awpm -input cage12.mtx \n" << endl;
60  cout << "(output matching is saved to cage12.mtx.awpm.txt)\n" << endl;
61  }
62 }
63 
64 
65 
66 
67 
68 
69 
70 int main(int argc, char* argv[])
71 {
72 
73  // ------------ initialize MPI ---------------
74  int provided;
75  MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided);
76  if (provided < MPI_THREAD_SERIALIZED)
77  {
78  printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n");
79  MPI_Abort(MPI_COMM_WORLD, 1);
80  }
81  int nprocs, myrank;
82  MPI_Comm_size(MPI_COMM_WORLD,&nprocs);
83  MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
84  if(argc < 3)
85  {
86  ShowUsage();
87  MPI_Finalize();
88  return -1;
89  }
90 
91  init = DMD;
92  randMaximal = false;
93  prune = false;
94  randMM = true;
95  moreSplit = false;
96  fewexp=false;
97  saveMatching = true;
98  ofname = "";
99  randPerm = false;
100  bool optimizeProd = true; // by default optimize sum_log_abs(aii) (after equil)
101 
102  bool weightedCard = true;
103  string ifilename = "";
104  string ofname = "";
105  for(int i = 1; i<argc; i++)
106  {
107  if (string(argv[i]) == string("-input")) ifilename = argv[i+1];
108  if (string(argv[i]) == string("-output")) ofname = argv[i+1];
109  if (string(argv[i]) == string("-optsum")) optimizeProd = false;
110  if (string(argv[i]) == string("-noWeightedCard")) weightedCard = false;
111  if (string(argv[i]) == string("-randPerm")) randPerm = true;
112  }
113  if(ofname=="") ofname = ifilename + ".awpm.txt";
114 
115 
116 
117 
118  // ------------ Process input arguments and build matrix ---------------
119  {
120  Par_DCSC_Double * AWeighted;
121  ostringstream tinfo;
122  tinfo << fixed;
123  cout << fixed;
124  double t01, t02;
125  if(ifilename!="")
126  {
127  AWeighted = new Par_DCSC_Double();
128  t01 = MPI_Wtime();
129  AWeighted->ParallelReadMM(ifilename, true, maximum<double>()); // one-based matrix market file
130  t02 = MPI_Wtime();
131 
132  if(AWeighted->getnrow() != AWeighted->getncol())
133  {
134  SpParHelper::Print("Rectangular matrix: Can not compute a perfect matching.\n");
135  MPI_Finalize();
136  return -1;
137  }
138 
139  tinfo.str("");
140  tinfo << "Reading input matrix in" << t02-t01 << " seconds" << endl;
141  SpParHelper::Print(tinfo.str());
142 
143  SpParHelper::Print("Pruning explicit zero entries....\n");
144  AWeighted->Prune([](double val){return fabs(val)==0;}, true);
145 
146  AWeighted->PrintInfo();
147  }
148  else
149  {
150  ShowUsage();
151  MPI_Finalize();
152  return -1;
153  }
154 
155 
156 
157  // ***** careful: if you permute the matrix, you have the permute the matching vectors as well!!
158  // randomly permute for load balance
159 
160  FullyDistVec<int64_t, int64_t> randp( AWeighted->getcommgrid());
161  if(randPerm)
162  {
163  if(AWeighted->getnrow() == AWeighted->getncol())
164  {
165  randp.iota(AWeighted->getnrow(), 0);
166  randp.RandPerm();
167  (*AWeighted)(randp,randp,true);
168  SpParHelper::Print("Matrix is randomly permuted for load balance.\n");
169  }
170  else
171  {
172  SpParHelper::Print("Rectangular matrix: Can not apply symmetric permutation.\n");
173  }
174  }
175 
176 
177  Par_DCSC_Bool A = *AWeighted; //just to compute degree
178  // Reduce is not multithreaded, so I am doing it here
180  A.Reduce(degCol, Column, plus<int64_t>(), static_cast<int64_t>(0));
181 
182 
183  // transform weights
184  if(optimizeProd)
185  TransformWeight(*AWeighted, true);
186  else
187  TransformWeight(*AWeighted, false);
188  // convert to CSC for SpMSpV calls
189  Par_CSC_Double AWeightedCSC(*AWeighted);
190  Par_CSC_Bool ABoolCSC(*AWeighted);
191 
192 
193  // Compute the initial trace
194  int64_t diagnnz;
195  double origWeight = Trace(*AWeighted, diagnnz);
196  bool isOriginalPerfect = diagnnz==A.getnrow();
197 
198 
199 
200 
201 
202  FullyDistVec<int64_t, int64_t> mateRow2Col ( A.getcommgrid(), A.getnrow(), (int64_t) -1);
203  FullyDistVec<int64_t, int64_t> mateCol2Row ( A.getcommgrid(), A.getncol(), (int64_t) -1);
204 
205 
206 
207 
208  init = DMD; randMaximal = false; randMM = false; prune = true;
209 
210  // Maximal
211  double ts = MPI_Wtime();
212  if(weightedCard)
213  WeightedGreedy(AWeightedCSC, mateRow2Col, mateCol2Row, degCol);
214  else
215  WeightedGreedy(ABoolCSC, mateRow2Col, mateCol2Row, degCol);
216  double tmcl = MPI_Wtime() - ts;
217 
218  double mclWeight = MatchingWeight( *AWeighted, mateRow2Col, mateCol2Row);
219  SpParHelper::Print("After Greedy sanity check\n");
220  bool isPerfectMCL = CheckMatching(mateRow2Col,mateCol2Row);
221 
222  if(isOriginalPerfect && mclWeight<=origWeight) // keep original
223  {
224  SpParHelper::Print("Maximal is not better that the natural ordering. Hence, keeping the natural ordering.\n");
225  mateRow2Col.iota(A.getnrow(), 0);
226  mateCol2Row.iota(A.getncol(), 0);
227  mclWeight = origWeight;
228  isPerfectMCL = true;
229  }
230 
231 
232  // MCM
233  double tmcm = 0;
234  double mcmWeight = mclWeight;
235  if(!isPerfectMCL) // run MCM only if we don't have a perfect matching
236  {
237  ts = MPI_Wtime();
238  if(weightedCard)
239  maximumMatching(AWeightedCSC, mateRow2Col, mateCol2Row, true, false, true);
240  else
241  maximumMatching(AWeightedCSC, mateRow2Col, mateCol2Row, true, false, false);
242  tmcm = MPI_Wtime() - ts;
243  mcmWeight = MatchingWeight( *AWeighted, mateRow2Col, mateCol2Row) ;
244  SpParHelper::Print("After MCM sanity check\n");
245  CheckMatching(mateRow2Col,mateCol2Row);
246  }
247 
248 
249  // AWPM
250  ts = MPI_Wtime();
251  TwoThirdApprox(*AWeighted, mateRow2Col, mateCol2Row);
252  double tawpm = MPI_Wtime() - ts;
253 
254  double awpmWeight = MatchingWeight( *AWeighted, mateRow2Col, mateCol2Row) ;
255  SpParHelper::Print("After AWPM sanity check\n");
256  CheckMatching(mateRow2Col,mateCol2Row);
257  if(isOriginalPerfect && awpmWeight<origWeight) // keep original
258  {
259  SpParHelper::Print("AWPM is not better that the natural ordering. Hence, keeping the natural ordering.\n");
260  mateRow2Col.iota(A.getnrow(), 0);
261  mateCol2Row.iota(A.getncol(), 0);
262  awpmWeight = origWeight;
263  }
264 
265 
266  int nthreads = 1;
267 #ifdef THREADED
268 #pragma omp parallel
269  {
270  nthreads = omp_get_num_threads();
271  }
272 #endif
273 
274  tinfo.str("");
275  tinfo << "Weight: [ Original Greedy MCM AWPM] " << origWeight << " " << mclWeight << " "<< mcmWeight << " " << awpmWeight << endl;
276  tinfo << "Time: [Processes Threads Cores Greedy MCM AWPM Total] " << nprocs << " " << nthreads << " " << nprocs * nthreads << " " << tTotalMaximal << " "<< tTotalMaximum << " " << tawpm << " "<< tTotalMaximal + tTotalMaximum + tawpm << endl;
277  SpParHelper::Print(tinfo.str());
278 
279  //revert random permutation if applied before
280  if(randPerm==true && randp.TotalLength() >0)
281  {
282  // inverse permutation
283  FullyDistVec<int64_t, int64_t>invRandp = randp.sort();
284  mateRow2Col = mateRow2Col(invRandp);
285  }
286  if(saveMatching && ofname!="")
287  {
288  mateRow2Col.ParallelWrite(ofname,false,false);
289  }
290 
291 
292  }
293  MPI_Finalize();
294  return 0;
295 }
296 
297 
double tTotalMaximal
int main(int argc, char *argv[])
int cblas_splits
Definition: DirOptBFS.cpp:72
FullyDistVec< IT, NT > Reduce(Dim dim, _BinaryOperation __binary_op, NT id, _UnaryOperation __unary_op) const
Definition: SpParMat.cpp:791
SpParMat< int64_t, int64_t, SpDCCols< int64_t, int64_t > > Par_DCSC_int64_t
std::shared_ptr< CommGrid > getcommgrid() const
Definition: SpParMat.h:275
Compute the maximum of two values.
Definition: Operations.h:154
NT Trace(SpParMat< IT, NT, DER > &A, IT &rettrnnz=0)
SpParMat< int64_t, bool, SpCCols< int64_t, bool > > Par_CSC_Bool
SpParMat< int64_t, bool, SpDCCols< int64_t, bool > > Par_DCSC_Bool
SpParMat< int64_t, double, SpCCols< int64_t, double > > Par_CSC_Double
double A
void iota(IT globalsize, NT first)
void maximumMatching(PSpMat_s32p64 &Aeff, FullyDistVec< int64_t, int64_t > &mateRow2Col, FullyDistVec< int64_t, int64_t > &mateCol2Row)
void TransformWeight(SpParMat< IT, NT, DER > &A, bool applylog)
NT MatchingWeight(std::vector< NT > &RepMateWC2R, MPI_Comm RowWorld, NT &minw)
long int64_t
Definition: compat.h:21
IT getncol() const
Definition: SpParMat.cpp:694
void PrintInfo() const
Definition: SpParMat.cpp:2387
void ParallelWrite(const std::string &filename, bool onebased, HANDLER handler, bool includeindices=true)
Definition: FullyDistVec.h:96
FullyDistVec< IT, IT > sort()
Definition: CCGrid.h:4
void TwoThirdApprox(SpParMat< IT, NT, DER > &A, FullyDistVec< IT, IT > &mateRow2Col, FullyDistVec< IT, IT > &mateCol2Row)
bool CheckMatching(FullyDistVec< IT, IT > &mateRow2Col, FullyDistVec< IT, IT > &mateCol2Row)
Definition: Utility.h:113
void WeightedGreedy(Par_MAT_Double &A, FullyDistVec< IT, IT > &mateRow2Col, FullyDistVec< IT, IT > &mateCol2Row, FullyDistVec< IT, IT > &degCol)
double tTotalMaximum
#define DMD
SpParMat< IT, NT, DER > Prune(_UnaryOperation __unary_op, bool inPlace=true)
Definition: SpParMat.h:175
SpParMat< int64_t, double, SpDCCols< int64_t, double > > Par_DCSC_Double
void ParallelReadMM(const std::string &filename, bool onebased, _BinaryOperation BinOp)
Definition: SpParMat.cpp:3417
IT getnrow() const
Definition: SpParMat.cpp:685