Zoltan2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
partitioning1.cpp
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 
14 #include <Zoltan2_TestHelpers.hpp>
15 #include <iostream>
16 #include <limits>
17 #include <Teuchos_ParameterList.hpp>
18 #include <Teuchos_RCP.hpp>
19 #include <Teuchos_FancyOStream.hpp>
20 #include <Teuchos_CommandLineProcessor.hpp>
21 #include <Tpetra_CrsMatrix.hpp>
22 #include <Tpetra_Vector.hpp>
23 #include <MatrixMarket_Tpetra.hpp>
24 
25 using Teuchos::RCP;
26 
28 // Program to demonstrate use of Zoltan2 to partition a TPetra matrix
29 // (read from a MatrixMarket file or generated by Galeri::Xpetra).
30 // Usage:
31 // a.out [--inputFile=filename] [--outputFile=outfile] [--verbose]
32 // [--x=#] [--y=#] [--z=#] [--matrix={Laplace1D,Laplace2D,Laplace3D}
33 // Karen Devine, 2011
35 
37 // Eventually want to use Teuchos unit tests to vary z2TestLO and
38 // GO. For now, we set them at compile time based on whether Tpetra
39 // is built with explicit instantiation on. (in Zoltan2_TestHelpers.hpp)
40 
41 typedef zlno_t z2TestLO;
42 typedef zgno_t z2TestGO;
44 
45 typedef Tpetra::CrsMatrix<z2TestScalar, z2TestLO, z2TestGO> SparseMatrix;
46 typedef Tpetra::CrsGraph<z2TestLO, z2TestGO> SparseGraph;
47 typedef Tpetra::Vector<z2TestScalar, z2TestLO, z2TestGO> Vector;
48 typedef Vector::node_type Node;
49 
53 
54 
55 // Integer vector
56 typedef Tpetra::Vector<int, z2TestLO, z2TestGO> IntVector;
58 
59 #define epsilon 0.00000001
60 #define NNZ_IDX 1
61 
63 int main(int narg, char** arg)
64 {
65  std::string inputFile = ""; // Matrix Market or Zoltan file to read
66  std::string outputFile = ""; // Matrix Market or Zoltan file to write
67  std::string inputPath = testDataFilePath; // Directory with input file
68  std::string method = "scotch";
69  bool verbose = false; // Verbosity of output
70  bool distributeInput = true;
71  bool haveFailure = false;
72  int nParts = -1;
73  int nVwgts = 0;
74  int nEwgts = 0;
75  int testReturn = 0;
76 
78  Tpetra::ScopeGuard tscope(&narg, &arg);
79  RCP<const Teuchos::Comm<int> > comm = Tpetra::getDefaultComm();
80  int me = comm->getRank();
81 
82  // Read run-time options.
83  Teuchos::CommandLineProcessor cmdp (false, false);
84  cmdp.setOption("inputPath", &inputPath,
85  "Path to the MatrixMarket or Zoltan file to be read; "
86  "if not specified, a default path will be used.");
87  cmdp.setOption("inputFile", &inputFile,
88  "Name of the Matrix Market or Zoltan file to read; "
89  "if not specified, a matrix will be generated by MueLu.");
90  cmdp.setOption("outputFile", &outputFile,
91  "Name of the Matrix Market sparse matrix file to write, "
92  "echoing the input/generated matrix.");
93  cmdp.setOption("method", &method,
94  "Partitioning method to use: scotch or parmetis.");
95  cmdp.setOption("nparts", &nParts,
96  "Number of parts being requested");
97  cmdp.setOption("vertexWeights", &nVwgts,
98  "Number of weights to generate for each vertex");
99  cmdp.setOption("edgeWeights", &nEwgts,
100  "Number of weights to generate for each edge");
101  cmdp.setOption("verbose", "quiet", &verbose,
102  "Print messages and results.");
103  cmdp.setOption("distribute", "no-distribute", &distributeInput,
104  "indicate whether or not to distribute "
105  "input across the communicator");
106 
108  // Even with cmdp option "true", I get errors for having these
109  // arguments on the command line. (On redsky build)
110  // KDDKDD Should just be warnings, right? Code should still work with these
111  // KDDKDD params in the create-a-matrix file. Better to have them where
112  // KDDKDD they are used.
113  int xdim=10;
114  int ydim=10;
115  int zdim=10;
116  std::string matrixType("Laplace3D");
117 
118  cmdp.setOption("x", &xdim,
119  "number of gridpoints in X dimension for "
120  "mesh used to generate matrix.");
121  cmdp.setOption("y", &ydim,
122  "number of gridpoints in Y dimension for "
123  "mesh used to generate matrix.");
124  cmdp.setOption("z", &zdim,
125  "number of gridpoints in Z dimension for "
126  "mesh used to generate matrix.");
127  cmdp.setOption("matrix", &matrixType,
128  "Matrix type: Laplace1D, Laplace2D, or Laplace3D");
129 
131  // Quotient-specific parameters
132  int quotientThreshold = -1;
133  cmdp.setOption("qthreshold", &quotientThreshold,
134  "Threshold on the number of vertices for active MPI ranks to hold"
135  "after the migrating the communication graph to the active ranks.");
136 
138 
139  cmdp.parse(narg, arg);
140 
141  RCP<UserInputForTests> uinput;
142 
143  if (inputFile != "") // Input file specified; read a matrix
144  uinput = rcp(new UserInputForTests(inputPath, inputFile, comm,
145  true, distributeInput));
146 
147  else // Let MueLu generate a default matrix
148  uinput = rcp(new UserInputForTests(xdim, ydim, zdim, string(""), comm,
149  true, distributeInput));
150 
151  RCP<SparseMatrix> origMatrix = uinput->getUITpetraCrsMatrix();
152 
153  if (origMatrix->getGlobalNumRows() < 40) {
154  Teuchos::FancyOStream out(Teuchos::rcp(&std::cout,false));
155  origMatrix->describe(out, Teuchos::VERB_EXTREME);
156  }
157 
158 
159  if (outputFile != "") {
160  // Just a sanity check.
161  Tpetra::MatrixMarket::Writer<SparseMatrix>::writeSparseFile(outputFile,
162  origMatrix, verbose);
163  }
164 
165  if (me == 0)
166  std::cout << "NumRows = " << origMatrix->getGlobalNumRows() << std::endl
167  << "NumNonzeros = " << origMatrix->getGlobalNumEntries() << std::endl
168  << "NumProcs = " << comm->getSize() << std::endl
169  << "NumLocalRows (rank 0) = " << origMatrix->getLocalNumRows() << std::endl;
170 
172  RCP<Vector> origVector, origProd;
173  origProd = Tpetra::createVector<z2TestScalar,z2TestLO,z2TestGO>(
174  origMatrix->getRangeMap());
175  origVector = Tpetra::createVector<z2TestScalar,z2TestLO,z2TestGO>(
176  origMatrix->getDomainMap());
177  origVector->randomize();
178 
180  Teuchos::ParameterList params;
181 
182  params.set("partitioning_approach", "partition");
183  params.set("algorithm", method);
184 
186  if(nParts > 0) {
187  params.set("num_global_parts", nParts);
188  }
189 
191  if(method == "quotient" && quotientThreshold > 0) {
192  params.set("quotient_threshold", quotientThreshold);
193  }
194 
196  SparseGraphAdapter adapter(origMatrix->getCrsGraph(), nVwgts, nEwgts);
197 
201 
202  zscalar_t *vwgts = NULL, *ewgts = NULL;
203  if (nVwgts) {
204  // Test vertex weights with stride nVwgts.
205  size_t nrows = origMatrix->getLocalNumRows();
206  if (nrows) {
207  vwgts = new zscalar_t[nVwgts * nrows];
208  for (size_t i = 0; i < nrows; i++) {
209  size_t idx = i * nVwgts;
210  vwgts[idx] = zscalar_t(origMatrix->getRowMap()->getGlobalElement(i))
211  ;// + zscalar_t(0.5);
212  for (int j = 1; j < nVwgts; j++) vwgts[idx+j] = 1.;
213  }
214  for (int j = 0; j < nVwgts; j++) {
215  if (j != NNZ_IDX) adapter.setVertexWeights(&vwgts[j], nVwgts, j);
216  else adapter.setVertexWeightIsDegree(NNZ_IDX);
217  }
218  }
219  }
220 
221  if (nEwgts) {
222  // Test edge weights with stride 1.
223  size_t nnz = origMatrix->getLocalNumEntries();
224  if (nnz) {
225  size_t nrows = origMatrix->getLocalNumRows();
226  size_t maxnzrow = origMatrix->getLocalMaxNumRowEntries();
227  ewgts = new zscalar_t[nEwgts * nnz];
228  size_t cnt = 0;
229  typename SparseMatrix::nonconst_global_inds_host_view_type egids("egids", maxnzrow);
230  typename SparseMatrix::nonconst_values_host_view_type evals("evals", maxnzrow);
231  for (size_t i = 0; i < nrows; i++) {
232  size_t nnzinrow;
233  z2TestGO gid = origMatrix->getRowMap()->getGlobalElement(i);
234  origMatrix->getGlobalRowCopy(gid, egids, evals, nnzinrow);
235  for (size_t k = 0; k < nnzinrow; k++) {
236  ewgts[cnt] = (gid < egids[k] ? gid : egids[k]);
237  if (nEwgts > 1) ewgts[cnt+nnz] = (gid < egids[k] ? egids[k] : gid);
238  for (int j = 2; j < nEwgts; j++) ewgts[cnt+nnz*j] = 1.;
239  cnt++;
240  }
241  }
242  for (int j = 0; j < nEwgts; j++) {
243  adapter.setEdgeWeights(&ewgts[j*nnz], 1, j);
244  }
245  }
246  }
247 
248 
250  Zoltan2::PartitioningProblem<SparseGraphAdapter> problem(&adapter, &params);
251 
252  try {
253  if (me == 0) std::cout << "Calling solve() " << std::endl;
254 
255  problem.solve();
256 
257  if (me == 0) std::cout << "Done solve() " << std::endl;
258  }
259  catch (std::runtime_error &e) {
260  delete [] vwgts;
261  delete [] ewgts;
262  std::cout << "Runtime exception returned from solve(): " << e.what();
263  if (!strncmp(e.what(), "BUILD ERROR", 11)) {
264  // Catching build errors as exceptions is OK in the tests
265  std::cout << " PASS" << std::endl;
266  return 0;
267  }
268  else {
269  // All other runtime_errors are failures
270  std::cout << " FAIL" << std::endl;
271  return -1;
272  }
273  }
274  catch (std::logic_error &e) {
275  delete [] vwgts;
276  delete [] ewgts;
277  std::cout << "Logic exception returned from solve(): " << e.what()
278  << " FAIL" << std::endl;
279  return -1;
280  }
281  catch (std::bad_alloc &e) {
282  delete [] vwgts;
283  delete [] ewgts;
284  std::cout << "Bad_alloc exception returned from solve(): " << e.what()
285  << " FAIL" << std::endl;
286  return -1;
287  }
288  catch (std::exception &e) {
289  delete [] vwgts;
290  delete [] ewgts;
291  std::cout << "Unknown exception returned from solve(). " << e.what()
292  << " FAIL" << std::endl;
293  return -1;
294  }
295 
298  size_t checkNparts = comm->getSize();
299  if(nParts != -1) checkNparts = size_t(nParts);
300  size_t checkLength = origMatrix->getLocalNumRows();
301 
302  const SparseGraphAdapter::part_t *checkParts = problem.getSolution().getPartListView();
303 
304  // Check for load balance
305  size_t *countPerPart = new size_t[checkNparts];
306  size_t *globalCountPerPart = new size_t[checkNparts];
307  zscalar_t *wtPerPart = (nVwgts ? new zscalar_t[checkNparts*nVwgts] : NULL);
308  zscalar_t *globalWtPerPart = (nVwgts ? new zscalar_t[checkNparts*nVwgts] : NULL);
309  for (size_t i = 0; i < checkNparts; i++) countPerPart[i] = 0;
310  for (size_t i = 0; i < checkNparts * nVwgts; i++) wtPerPart[i] = 0.;
311 
312  for (size_t i = 0; i < checkLength; i++) {
313  if (size_t(checkParts[i]) >= checkNparts)
314  std::cout << "Invalid Part " << checkParts[i] << ": FAIL" << std::endl;
315  countPerPart[checkParts[i]]++;
316  for (int j = 0; j < nVwgts; j++) {
317  if (j != NNZ_IDX)
318  wtPerPart[checkParts[i]*nVwgts+j] += vwgts[i*nVwgts+j];
319  else
320  wtPerPart[checkParts[i]*nVwgts+j] += origMatrix->getNumEntriesInLocalRow(i);
321  }
322  }
323 
324  // Quotient algorithm should produce the same result for each local row
325  if(method == "quotient") {
326  size_t result = size_t(checkParts[0]);
327  for (size_t i = 1; i < checkLength; i++) {
328  if (size_t(checkParts[i]) != result)
329  std::cout << "Different parts in the quotient algorithm: "
330  << result << "!=" << checkParts[i] << ": FAIL" << std::endl;
331  }
332  }
333 
334 
335  Teuchos::reduceAll<int, size_t>(*comm, Teuchos::REDUCE_SUM, checkNparts,
336  countPerPart, globalCountPerPart);
337  Teuchos::reduceAll<int, zscalar_t>(*comm, Teuchos::REDUCE_SUM,
338  checkNparts*nVwgts,
339  wtPerPart, globalWtPerPart);
340 
341  size_t min = std::numeric_limits<std::size_t>::max();
342  size_t max = 0;
343  size_t sum = 0;
344  size_t minrank = 0, maxrank = 0;
345  for (size_t i = 0; i < checkNparts; i++) {
346  if (globalCountPerPart[i] < min) {min = globalCountPerPart[i]; minrank = i;}
347  if (globalCountPerPart[i] > max) {max = globalCountPerPart[i]; maxrank = i;}
348  sum += globalCountPerPart[i];
349  }
350 
351  if (me == 0) {
352  float avg = (float) sum / (float) checkNparts;
353  std::cout << "Minimum count: " << min << " on rank " << minrank << std::endl;
354  std::cout << "Maximum count: " << max << " on rank " << maxrank << std::endl;
355  std::cout << "Average count: " << avg << std::endl;
356  std::cout << "Total count: " << sum
357  << (sum != origMatrix->getGlobalNumRows()
358  ? "Work was lost; FAIL"
359  : " ")
360  << std::endl;
361  std::cout << "Imbalance: " << max / avg << std::endl;
362  if (nVwgts) {
363  std::vector<zscalar_t> minwt(nVwgts, std::numeric_limits<zscalar_t>::max());
364  std::vector<zscalar_t> maxwt(nVwgts, 0.);
365  std::vector<zscalar_t> sumwt(nVwgts, 0.);
366  for (size_t i = 0; i < checkNparts; i++) {
367  for (int j = 0; j < nVwgts; j++) {
368  size_t idx = i*nVwgts+j;
369  if (globalWtPerPart[idx] < minwt[j]) minwt[j] = globalWtPerPart[idx];
370  if (globalWtPerPart[idx] > maxwt[j]) maxwt[j] = globalWtPerPart[idx];
371  sumwt[j] += globalWtPerPart[idx];
372  }
373  }
374  for (int j = 0; j < nVwgts; j++) {
375  float avgwt = (float) sumwt[j] / (float) checkNparts;
376  std::cout << std::endl;
377  std::cout << "Minimum weight[" << j << "]: " << minwt[j] << std::endl;
378  std::cout << "Maximum weight[" << j << "]: " << maxwt[j] << std::endl;
379  std::cout << "Average weight[" << j << "]: " << avgwt << std::endl;
380  std::cout << "Imbalance: " << maxwt[j] / avgwt << std::endl;
381  }
382  }
383  }
384 
385  delete [] countPerPart;
386  delete [] wtPerPart;
387  delete [] globalCountPerPart;
388  delete [] globalWtPerPart;
389  delete [] vwgts;
390  delete [] ewgts;
391 
392 
394  if (me == 0) std::cout << "Redistributing matrix..." << std::endl;
395  SparseMatrix *redistribMatrix;
396  SparseMatrixAdapter adapterMatrix(origMatrix);
397  adapterMatrix.applyPartitioningSolution(*origMatrix, redistribMatrix,
398  problem.getSolution());
399  if (redistribMatrix->getGlobalNumRows() < 40) {
400  Teuchos::FancyOStream out(Teuchos::rcp(&std::cout,false));
401  redistribMatrix->describe(out, Teuchos::VERB_EXTREME);
402  }
403 
404  if (me == 0) std::cout << "Redistributing vectors..." << std::endl;
405  Vector *redistribVector;
406 // std::vector<const zscalar_t *> weights;
407 // std::vector<int> weightStrides;
408  MultiVectorAdapter adapterVector(origVector); //, weights, weightStrides);
409  adapterVector.applyPartitioningSolution(*origVector, redistribVector,
410  problem.getSolution());
411 
412  RCP<Vector> redistribProd;
413  redistribProd = Tpetra::createVector<z2TestScalar,z2TestLO,z2TestGO>(
414  redistribMatrix->getRangeMap());
415 
416  // Test redistributing an integer vector with the same solution.
417  // This test is mostly to make sure compilation always works.
418  RCP<IntVector> origIntVec;
419  IntVector *redistIntVec;
420  origIntVec = Tpetra::createVector<int,z2TestLO,z2TestGO>(
421  origMatrix->getRangeMap());
422  for (size_t i = 0; i < origIntVec->getLocalLength(); i++)
423  origIntVec->replaceLocalValue(i, me);
424 
425  IntVectorAdapter int_vec_adapter(origIntVec);
426  int_vec_adapter.applyPartitioningSolution(*origIntVec, redistIntVec,
427  problem.getSolution());
428  int origIntNorm = origIntVec->norm1();
429  int redistIntNorm = redistIntVec->norm1();
430  if (me == 0) std::cout << "IntegerVectorTest: " << origIntNorm << " == "
431  << redistIntNorm << " ?";
432  if (origIntNorm != redistIntNorm) {
433  if (me == 0) std::cout << " FAIL" << std::endl;
434  haveFailure = true;
435  }
436  else if (me == 0) std::cout << " OK" << std::endl;
437  delete redistIntVec;
438 
441 
442  if (me == 0) std::cout << "Matvec original..." << std::endl;
443  origMatrix->apply(*origVector, *origProd);
444  z2TestScalar origNorm = origProd->norm2();
445  if (me == 0)
446  std::cout << "Norm of Original matvec prod: " << origNorm << std::endl;
447 
448  if (me == 0) std::cout << "Matvec redistributed..." << std::endl;
449  redistribMatrix->apply(*redistribVector, *redistribProd);
450  z2TestScalar redistribNorm = redistribProd->norm2();
451  if (me == 0)
452  std::cout << "Norm of Redistributed matvec prod: " << redistribNorm << std::endl;
453 
454  if (redistribNorm > origNorm+epsilon || redistribNorm < origNorm-epsilon) {
455  testReturn = 1;
456  haveFailure = true;
457  }
458 
459  delete redistribVector;
460  delete redistribMatrix;
461 
462  if (me == 0) {
463  if (testReturn) {
464  std::cout << "Mat-Vec product changed; FAIL" << std::endl;
465  haveFailure = true;
466  }
467  if (!haveFailure)
468  std::cout << "PASS" << std::endl;
469  }
470 
471  return testReturn;
472 }
zgno_t z2TestGO
Definition: coloring1.cpp:41
zlno_t z2TestLO
Definition: coloring1.cpp:40
#define nParts
Provides access for Zoltan2 to Xpetra::CrsMatrix data.
Tpetra::CrsMatrix< z2TestScalar, z2TestLO, z2TestGO > SparseMatrix
Definition: coloring1.cpp:44
#define NNZ_IDX
Provides access for Zoltan2 to Xpetra::CrsGraph data.
Zoltan2::XpetraCrsGraphAdapter< SparseGraph > SparseGraphAdapter
int main(int narg, char **arg)
Definition: coloring1.cpp:164
common code used by tests
typename InputTraits< User >::part_t part_t
Tpetra::Vector< z2TestScalar, z2TestLO, z2TestGO > Vector
Definition: coloring1.cpp:45
Defines the XpetraMultiVectorAdapter.
Defines XpetraCrsGraphAdapter class.
Defines the XpetraCrsMatrixAdapter class.
void applyPartitioningSolution(const User &in, User *&out, const PartitioningSolution< Adapter > &solution) const
An adapter for Xpetra::MultiVector.
Tpetra::Map::local_ordinal_type zlno_t
Zoltan2::XpetraMultiVectorAdapter< IntVector > IntVectorAdapter
const PartitioningSolution< Adapter > & getSolution()
Get the solution to the problem.
Tpetra::CrsGraph< z2TestLO, z2TestGO > SparseGraph
PartitioningProblem sets up partitioning problems for the user.
Zoltan2::XpetraCrsMatrixAdapter< SparseMatrix > SparseMatrixAdapter
Definition: coloring1.cpp:50
Tpetra::Vector< int, z2TestLO, z2TestGO > IntVector
Defines the PartitioningProblem class.
void applyPartitioningSolution(const User &in, User *&out, const PartitioningSolution< Adapter > &solution) const
zscalar_t z2TestScalar
Definition: coloring1.cpp:42
float zscalar_t
#define epsilon
Zoltan2::XpetraMultiVectorAdapter< Vector > MultiVectorAdapter
Tpetra::Map::global_ordinal_type zgno_t
void solve(bool updateInputData=true)
Direct the problem to create a solution.
Vector::node_type Node
Definition: coloring1.cpp:46
std::string testDataFilePath(".")