Zoltan2
Loading...
Searching...
No Matches
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
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
25using 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
44
45typedef Tpetra::CrsMatrix<z2TestScalar, z2TestLO, z2TestGO> SparseMatrix;
46typedef Tpetra::CrsGraph<z2TestLO, z2TestGO> SparseGraph;
47typedef Tpetra::Vector<z2TestScalar, z2TestLO, z2TestGO> Vector;
48typedef Vector::node_type Node;
49
53
54
55// Integer vector
56typedef Tpetra::Vector<int, z2TestLO, z2TestGO> IntVector;
58
59#define epsilon 0.00000001
60#define NNZ_IDX 1
61
63int 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}
#define nParts
Defines the PartitioningProblem class.
common code used by tests
float zscalar_t
Tpetra::Map ::local_ordinal_type zlno_t
std::string testDataFilePath(".")
Tpetra::Map ::global_ordinal_type zgno_t
Defines XpetraCrsGraphAdapter class.
Defines the XpetraCrsMatrixAdapter class.
Defines the XpetraMultiVectorAdapter.
int main()
typename InputTraits< User >::part_t part_t
PartitioningProblem sets up partitioning problems for the user.
const PartitioningSolution< Adapter > & getSolution()
Get the solution to the problem.
void solve(bool updateInputData=true)
Direct the problem to create a solution.
Provides access for Zoltan2 to Xpetra::CrsGraph data.
void setVertexWeightIsDegree(int idx)
Specify an index for which the vertex weight should be the degree of the vertex.
void setVertexWeights(const scalar_t *val, int stride, int idx)
Provide a pointer to vertex weights.
void setEdgeWeights(const scalar_t *val, int stride, int idx)
Provide a pointer to edge weights.
Provides access for Zoltan2 to Xpetra::CrsMatrix data.
void applyPartitioningSolution(const User &in, User *&out, const PartitioningSolution< Adapter > &solution) const
void applyPartitioningSolution(const User &in, User *&out, const PartitioningSolution< Adapter > &solution) const
Tpetra::CrsMatrix< z2TestScalar, z2TestLO, z2TestGO > SparseMatrix
Definition coloring1.cpp:44
Tpetra::Vector< z2TestScalar, z2TestLO, z2TestGO > Vector
Definition coloring1.cpp:45
zgno_t z2TestGO
Definition coloring1.cpp:41
zscalar_t z2TestScalar
Definition coloring1.cpp:42
Tpetra::Vector< int, z2TestLO, z2TestGO > IntVector
#define epsilon
Vector::node_type Node
zlno_t z2TestLO
Tpetra::CrsGraph< z2TestLO, z2TestGO > SparseGraph
Zoltan2::XpetraMultiVectorAdapter< IntVector > IntVectorAdapter
#define NNZ_IDX
Tpetra::CrsMatrix< z2TestScalar, z2TestLO, z2TestGO > SparseMatrix
Zoltan2::XpetraMultiVectorAdapter< Vector > MultiVectorAdapter
Zoltan2::XpetraCrsMatrixAdapter< SparseMatrix > SparseMatrixAdapter
Tpetra::Vector< z2TestScalar, z2TestLO, z2TestGO > Vector
zgno_t z2TestGO
Tpetra::Vector< int, z2TestLO, z2TestGO > IntVector
zscalar_t z2TestScalar
Zoltan2::XpetraCrsGraphAdapter< SparseGraph > SparseGraphAdapter