Zoltan2
Loading...
Searching...
No Matches
partitioningTree.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 <iostream>
15#include <limits>
16#include <Teuchos_ParameterList.hpp>
17#include <Teuchos_RCP.hpp>
18#include <Teuchos_FancyOStream.hpp>
19#include <Teuchos_CommandLineProcessor.hpp>
20#include <Tpetra_CrsMatrix.hpp>
21#include <Tpetra_Vector.hpp>
22#include <MatrixMarket_Tpetra.hpp>
23
24using Teuchos::RCP;
25
29
30typedef Tpetra::CrsMatrix<z2TestScalar, z2TestLO, z2TestGO> SparseMatrix_t;
31typedef Tpetra::Vector<z2TestScalar, z2TestLO, z2TestGO> Vector;
32typedef Vector::node_type Node;
33
34typedef Tpetra::MultiVector<z2TestScalar, z2TestLO, z2TestGO,znode_t> tMVector_t;
35
36
38
40
42
43
44
45int testForRCB(SparseMatrixAdapter_t &matAdapter, int myrank, part_t numparts,
46 RCP<tMVector_t> coords, RCP<const Teuchos::Comm<int> > comm);
47int testForPHG(SparseMatrixAdapter_t &matAdapter, int myrank, part_t numparts,
48 RCP<tMVector_t> coords, RCP<const Teuchos::Comm<int> >);
49int testForMJ(SparseMatrixAdapter_t &matAdapter, int myrank, part_t numparts,
50 RCP<tMVector_t> coords, RCP<const Teuchos::Comm<int> >);
51
52
55int main(int narg, char** arg)
56{
57 Tpetra::ScopeGuard tscope(&narg, &arg);
58 Teuchos::RCP<const Teuchos::Comm<int> > comm = Tpetra::getDefaultComm();
59 int me = comm->getRank();
60
62
63 std::string inputFile = ""; // Matrix Market or Zoltan file to read
64 std::string inputPath = testDataFilePath; // Directory with input file
65 bool distributeInput = true;
66 int success = 0;
67 part_t numParts = 8;
68
69
71 // Read run-time options.
73 Teuchos::CommandLineProcessor cmdp (false, false);
74 cmdp.setOption("inputPath", &inputPath,
75 "Path to the MatrixMarket or Zoltan file to be read; "
76 "if not specified, a default path will be used.");
77 cmdp.setOption("inputFile", &inputFile,
78 "Name of the Matrix Market or Zoltan file to read; "
79 "");
80 cmdp.setOption("distribute", "no-distribute", &distributeInput,
81 "for Zoltan input files only, "
82 "indicate whether or not to distribute "
83 "input across the communicator");
84 cmdp.setOption("numParts", &numParts,
85 "Global number of parts;");
86
87 Teuchos::CommandLineProcessor::EParseCommandLineReturn
88 parseReturn= cmdp.parse( narg, arg );
89
90 if( parseReturn == Teuchos::CommandLineProcessor::PARSE_HELP_PRINTED )
91 {
92 return 0;
93 }
95
97 // Construct matrix from file
99 RCP<UserInputForTests> uinput;
100
101 if (inputFile != "") // Input file specified; read a matrix
102 {
103 uinput = rcp(new UserInputForTests(inputPath, inputFile, comm,
104 true, distributeInput));
105 }
106 else
107 {
108 std::cout << "Input file must be specified." << std::endl;
109 }
110
111 RCP<SparseMatrix_t> origMatrix = uinput->getUITpetraCrsMatrix();
112
113
114 if (me == 0)
115 {
116 std::cout << "NumRows = " << origMatrix->getGlobalNumRows() << std::endl
117 << "NumNonzeros = " << origMatrix->getGlobalNumEntries() << std::endl
118 << "NumProcs = " << comm->getSize() << std::endl
119 << "NumParts = " << numParts << std::endl;
120 }
121
122 if (origMatrix->getGlobalNumRows() < 40)
123 {
124 Teuchos::FancyOStream out(Teuchos::rcp(&std::cout,false));
125 origMatrix->describe(out, Teuchos::VERB_EXTREME);
126 }
128
130 // Get coordinates, corresponding to graph vertices
132 RCP<tMVector_t> coords;
133 try
134 {
135 coords = uinput->getUICoordinates();
136 }
137 catch(...)
138 {
139 if (me == 0)
140 std::cout << "FAIL: get coordinates" << std::endl;
141 return 1;
142 }
144
148 SparseMatrixAdapter_t matAdapter(origMatrix);
149
150 MultiVectorAdapter_t *ca = NULL;
151
152 try
153 {
154 ca = new MultiVectorAdapter_t(coords);
155 }
156 catch(...)
157 {
158 if (me == 0)
159 std::cout << "FAIL: vector adapter" << std::endl;
160 return 1;
161 }
162
163 matAdapter.setCoordinateInput(ca);
165
166 // MDM - MJ disabled - not implemented yet
167 // int errMJ = testForMJ(matAdapter, me, numParts, coords, comm); -check err
168 int errRCB = testForRCB(matAdapter, me, numParts, coords, comm);
169 int errPHG = testForPHG(matAdapter, me, numParts, coords, comm);
170
171 // Currently just for internal development - sweep from 1 - 25 numParts
172 // and validate each to check for any bad results.
173 // #define BRUTE_FORCE_SEARCH
174 #ifdef BRUTE_FORCE_SEARCH
175 for(int setParts = 1; setParts <= 25; ++setParts) {
176 // numParts is a parameter so should not be set like for for production
177 // code - this is just a 2nd development check to run try a bunch of tests
178 numParts = setParts;
179 std::cout << "Brute force testing num parts: " << numParts << std::endl;
180 // MJ not implemented yet
181 // if(testForMJ(matAdapter, me, numParts, coords, comm) != 0) { return 1; }
182 if(testForRCB(matAdapter, me, numParts, coords, comm) != 0) { return 1; }
183 if(testForPHG(matAdapter, me, numParts, coords, comm) != 0) { return 1; }
184 }
185 #endif
186
187 delete ca;
188
189 // if(errMJ==0)
190 if(errRCB==0)
191 if(errPHG==0)
192 {
193 std::cout << "PASS" << std::endl;
194 return success;
195 }
196 return 1;
197}
199
200
202// Runs validation on the results to make sure the arrays are sensible
204bool validate(part_t numTreeVerts,
205 std::vector<part_t> permPartNums,
206 std::vector<part_t> splitRangeBeg,
207 std::vector<part_t> splitRangeEnd,
208 std::vector<part_t> treeVertParents
209)
210{
211 // by design numTreeVerts does not include the root
212 if(numTreeVerts != static_cast<part_t>(splitRangeBeg.size()) - 1) {
213 return false;
214 }
215 if(numTreeVerts != static_cast<part_t>(splitRangeEnd.size()) - 1) {
216 return false;
217 }
218 if(numTreeVerts != static_cast<part_t>(treeVertParents.size()) - 1) {
219 return false;
220 }
221 // now search every node and validate it's properties
222 for(part_t n = 0; n <= numTreeVerts; ++n) {
223 if(n < static_cast<part_t>(permPartNums.size())) {
224 // terminal so children should just be range 1
225 if(splitRangeEnd[n] != splitRangeBeg[n] + 1) {
226 std::cout << "Invalid terminal - range should be 1" << std::endl;
227 return false;
228 }
229 if(splitRangeBeg[n] != n) {
230 std::cout << "Invalid terminal - not pointing to myself!" << std::endl;
231 return false;
232 }
233 }
234 else {
235 part_t beg = splitRangeBeg[n];
236 part_t end = splitRangeEnd[n];
237 part_t count = end - beg;
238 std::vector<bool> findChildren(count, false);
239 for(part_t n2 = 0; n2 <= numTreeVerts; ++n2) {
240 if(treeVertParents[n2] == n) {
241 part_t beg2 = splitRangeBeg[n2];
242 part_t end2 = splitRangeEnd[n2];
243 for(part_t q = beg2; q < end2; ++q) {
244 part_t setIndex = q - beg; // rel to parent so beg, not beg2
245 if(setIndex < 0 || setIndex >= count) {
246 std::cout << "Found bad child index - invalid results!" << std::endl;
247 return false;
248 }
249 if(findChildren[setIndex]) {
250 std::cout << "Found child twice - invalid results!" << std::endl;
251 return false;
252 }
253 findChildren[setIndex] = true;
254 }
255 }
256 }
257 for(part_t q = 0; q < count; ++q) {
258 if(!findChildren[q]) {
259 std::cout << "Did not find all children. Invalid results!" << std::endl;
260 return false;
261 }
262 }
263 }
264 if(n == numTreeVerts) {
265 // this is the root
266 if(splitRangeBeg[n] != 0) {
267 std::cout << "Root must start at 0!" << std::endl;
268 return false;
269 }
270 if(splitRangeEnd[n] != static_cast<part_t>(permPartNums.size())) {
271 std::cout << "Root must contain all parts!" << std::endl;
272 return false;
273 }
274 }
275 }
276 return true;
277}
279
281// General test function to analyze solution and print out some information
284 RCP<const Teuchos::Comm<int> > comm)
285{
286 part_t numTreeVerts = 0;
287 std::vector<part_t> permPartNums; // peritab in Scotch
288 std::vector<part_t> splitRangeBeg;
289 std::vector<part_t> splitRangeEnd;
290 std::vector<part_t> treeVertParents;
291
293 problem.getSolution();
294
295 solution.getPartitionTree(numTreeVerts,permPartNums,splitRangeBeg,
296 splitRangeEnd,treeVertParents);
297
298 comm->barrier(); // for tidy output...
299 if(comm->getRank() == 0) { // for now just plot for rank 0
300
301 // print the acquired information about the tree
302
303 // Header
304 std::cout << std::endl << "Printing partition tree info..." << std::endl << std::endl;
305
306 // numTreeVerts
307 std::cout << " numTreeVerts: " << numTreeVerts << std::endl << std::endl;
308
309 // array index values 0 1 2 3 ...
310 std::cout << " part array index:";
311 for(part_t n = 0; n < static_cast<part_t>(permPartNums.size()); ++n) {
312 std::cout << std::setw(4) << n << " ";
313 }
314 std::cout << std::endl;
315
316 // permParNums
317 std::cout << " permPartNums: ";
318 for(part_t n = 0; n < static_cast<part_t>(permPartNums.size()); ++n) {
319 std::cout << std::setw(4) << permPartNums[n] << " ";
320 }
321 std::cout << std::endl << std::endl;
322
323 // node index values 0 1 2 3 ...
324 std::cout << " node index: ";
325 for(part_t n = 0; n < static_cast<part_t>(splitRangeBeg.size()); ++n) {
326 std::cout << std::setw(4) << n << " ";
327 }
328 std::cout << std::endl;
329
330 // splitRangeBeg
331 std::cout << " splitRangeBeg: ";
332 for(part_t n = 0; n < static_cast<part_t>(splitRangeBeg.size()); ++n) {
333 std::cout << std::setw(4) << splitRangeBeg[n] << " ";
334 }
335 std::cout << std::endl;
336
337 // splitRangeEnd
338 std::cout << " splitRangeEnd: ";
339 for(part_t n = 0; n < static_cast<part_t>(splitRangeEnd.size()); ++n) {
340 std::cout << std::setw(4) << splitRangeEnd[n] << " ";
341 }
342 std::cout << std::endl;
343
344 // treeVertParents
345 std::cout << " treeVertParents: ";
346 for(part_t n = 0; n < static_cast<part_t>(treeVertParents.size()); ++n) {
347 std::cout << std::setw(4) << treeVertParents[n] << " ";
348 }
349 std::cout << std::endl << std::endl;
350 }
351 comm->barrier(); // for tidy output...
352
353 if(!validate(numTreeVerts, permPartNums, splitRangeBeg, splitRangeEnd,
354 treeVertParents)) {
355 return 1;
356 }
357
358 return 0;
359}
360
362// Test partitioning tree access for RCB partitioning. This partitioning tree
363// should be binary..
365int testForRCB(SparseMatrixAdapter_t &matAdapter, int me, part_t numParts,
366 RCP<tMVector_t> coords, RCP<const Teuchos::Comm<int> > comm)
367{
368
372 Teuchos::ParameterList params;
373
374 params.set("num_global_parts", numParts);
375 params.set("partitioning_approach", "partition");
376 params.set("algorithm", "rcb");
377 params.set("keep_partition_tree", true);
378
380
384 Zoltan2::PartitioningProblem<SparseMatrixAdapter_t> problem(&matAdapter, &params);
385
386 try
387 {
388 if (me == 0) std::cout << "Calling solve() " << std::endl;
389 problem.solve();
390 if (me == 0) std::cout << "Done solve() " << std::endl;
391 }
392 catch (std::runtime_error &e)
393 {
394 std::cout << "Runtime exception returned from solve(): " << e.what();
395 if (!strncmp(e.what(), "BUILD ERROR", 11)) {
396 // Catching build errors as exceptions is OK in the tests
397 std::cout << " PASS" << std::endl;
398 return 0;
399 }
400 else {
401 // All other runtime_errors are failures
402 std::cout << " FAIL" << std::endl;
403 return -1;
404 }
405 }
406 catch (std::logic_error &e)
407 {
408 std::cout << "Logic exception returned from solve(): " << e.what()
409 << " FAIL" << std::endl;
410 return -1;
411 }
412 catch (std::bad_alloc &e)
413 {
414 std::cout << "Bad_alloc exception returned from solve(): " << e.what()
415 << " FAIL" << std::endl;
416 return -1;
417 }
418 catch (std::exception &e)
419 {
420 std::cout << "Unknown exception returned from solve(). " << e.what()
421 << " FAIL" << std::endl;
422 return -1;
423 }
425
426
428
429 bool binary = solution.isPartitioningTreeBinary();
430
431 if (binary == false)
432 {
433 std::cout << "RCB should produce a binary partitioning tree. FAIL" << std::endl;
434 return -1;
435 }
436
437 return analyze(problem, comm);
438}
440
442// Test partitioning tree access for PHG partitioning. This partitioning tree
443// should be balanced.
445int testForPHG(SparseMatrixAdapter_t &matAdapter, int me, part_t numParts,
446 RCP<tMVector_t> coords, RCP<const Teuchos::Comm<int> > comm)
447{
448
452 Teuchos::ParameterList params;
453
454 params.set("num_global_parts", numParts);
455 params.set("partitioning_approach", "partition");
456 params.set("algorithm", "zoltan");
457 params.set("keep_partition_tree", true);
458
459 Teuchos::ParameterList &zparams = params.sublist("zoltan_parameters",false);
460 zparams.set("LB_METHOD","phg");
461 zparams.set("FINAL_OUTPUT", "1");
462
464
468 Zoltan2::PartitioningProblem<SparseMatrixAdapter_t> problem(&matAdapter, &params);
469
470 try
471 {
472 if (me == 0) std::cout << "Calling solve() " << std::endl;
473 problem.solve();
474 if (me == 0) std::cout << "Done solve() " << std::endl;
475 }
476 catch (std::runtime_error &e)
477 {
478 std::cout << "Runtime exception returned from solve(): " << e.what();
479 if (!strncmp(e.what(), "BUILD ERROR", 11)) {
480 // Catching build errors as exceptions is OK in the tests
481 std::cout << " PASS" << std::endl;
482 return 0;
483 }
484 else {
485 // All other runtime_errors are failures
486 std::cout << " FAIL" << std::endl;
487 return -1;
488 }
489 }
490 catch (std::logic_error &e)
491 {
492 std::cout << "Logic exception returned from solve(): " << e.what()
493 << " FAIL" << std::endl;
494 return -1;
495 }
496 catch (std::bad_alloc &e)
497 {
498 std::cout << "Bad_alloc exception returned from solve(): " << e.what()
499 << " FAIL" << std::endl;
500 return -1;
501 }
502 catch (std::exception &e)
503 {
504 std::cout << "Unknown exception returned from solve(). " << e.what()
505 << " FAIL" << std::endl;
506 return -1;
507 }
509
511
512 bool binary = solution.isPartitioningTreeBinary();
513
514 if (binary == false)
515 {
516 std::cout << "PHG should produce a binary partitioning tree. FAIL" << std::endl;
517 return -1;
518 }
519
520 return analyze(problem, comm);
521}
523
525// Test partitioning tree access for MJ partitioning. This partitioning tree
526// should be balanced.
528int testForMJ(SparseMatrixAdapter_t &matAdapter, int me, part_t numParts,
529 RCP<tMVector_t> coords, RCP<const Teuchos::Comm<int> > comm)
530{
531
535 Teuchos::ParameterList params;
536
537 params.set("num_global_parts", numParts);
538 params.set("algorithm", "multijagged");
539 params.set("rectilinear", true);
540 // params.set("mj_keep_part_boxes", true); // allows getPartBoxesView on solution
541 params.set("keep_partition_tree", true);
542
544
548 Zoltan2::PartitioningProblem<SparseMatrixAdapter_t> problem(&matAdapter, &params);
549
550 try
551 {
552 if (me == 0) std::cout << "Calling solve() " << std::endl;
553 problem.solve();
554 if (me == 0) std::cout << "Done solve() " << std::endl;
555 }
556 catch (std::runtime_error &e)
557 {
558 std::cout << "Runtime exception returned from solve(): " << e.what();
559 if (!strncmp(e.what(), "BUILD ERROR", 11)) {
560 // Catching build errors as exceptions is OK in the tests
561 std::cout << " PASS" << std::endl;
562 return 0;
563 }
564 else {
565 // All other runtime_errors are failures
566 std::cout << " FAIL" << std::endl;
567 return -1;
568 }
569 }
570 catch (std::logic_error &e)
571 {
572 std::cout << "Logic exception returned from solve(): " << e.what()
573 << " FAIL" << std::endl;
574 return -1;
575 }
576 catch (std::bad_alloc &e)
577 {
578 std::cout << "Bad_alloc exception returned from solve(): " << e.what()
579 << " FAIL" << std::endl;
580 return -1;
581 }
582 catch (std::exception &e)
583 {
584 std::cout << "Unknown exception returned from solve(). " << e.what()
585 << " FAIL" << std::endl;
586 return -1;
587 }
589
591
592 // copied from the MultiJaggedTest.cpp to inspect boxes
593 // To activate set mj_keep_part_boxes true above
594 /*
595 std::vector<Zoltan2::coordinateModelPartBox<scalar_t, part_t> >
596 & pBoxes = solution.getPartBoxesView();
597 int coordDim = coords->getNumVectors();
598 std::cout << std::endl;
599 std::cout << "Plot final boxes..." << std::endl;
600 for (size_t i = 0; i < pBoxes.size(); i++) {
601 zscalar_t *lmin = pBoxes[i].getlmins();
602 zscalar_t *lmax = pBoxes[i].getlmaxs();;
603 int dim = pBoxes[i].getDim();
604 std::set<int> * pNeighbors = pBoxes[i].getNeighbors();
605 std::vector<int> * pGridIndices = pBoxes[i].getGridIndices();
606
607 std::cout << me << " pBox " << i << " pid " << pBoxes[i].getpId()
608 << " dim: " << dim
609 << " (" << lmin[0] << "," << lmin[1] << ") "
610 << "x"
611 << " (" << lmax[0] << "," << lmax[1] << ")" << std::endl;
612 }
613 */
614
615 bool binary = solution.isPartitioningTreeBinary();
616
617 if (binary == true)
618 {
619 std::cout << "MJ should not produce a binary partitioning tree for this problem. FAIL" << std::endl;
620 return -1;
621 }
622
623 return analyze(problem, comm);
624}
626
627
628
629
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 the XpetraCrsMatrixAdapter class.
Defines the XpetraMultiVectorAdapter.
int main()
typename InputTraits< User >::part_t part_t
void setCoordinateInput(VectorAdapter< UserCoord > *coordData) override
Allow user to provide additional data that contains coordinate info associated with the MatrixAdapter...
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.
A PartitioningSolution is a solution to a partitioning problem.
virtual bool isPartitioningTreeBinary() const
calculate if partition tree is binary.
void getPartitionTree(part_t &numTreeVerts, std::vector< part_t > &permPartNums, std::vector< part_t > &splitRangeBeg, std::vector< part_t > &splitRangeEnd, std::vector< part_t > &treeVertParents) const
get the partition tree - fill the relevant arrays
Provides access for Zoltan2 to Xpetra::CrsMatrix data.
Tpetra::MultiVector< zscalar_t, zlno_t, zgno_t, znode_t > tMVector_t
Vector::node_type Node
zlno_t z2TestLO
Tpetra::CrsMatrix< z2TestScalar, z2TestLO, z2TestGO > SparseMatrix_t
int analyze(Zoltan2::PartitioningProblem< SparseMatrixAdapter_t > &problem, RCP< const Teuchos::Comm< int > > comm)
bool validate(part_t numTreeVerts, std::vector< part_t > permPartNums, std::vector< part_t > splitRangeBeg, std::vector< part_t > splitRangeEnd, std::vector< part_t > treeVertParents)
int testForRCB(SparseMatrixAdapter_t &matAdapter, int myrank, part_t numparts, RCP< tMVector_t > coords, RCP< const Teuchos::Comm< int > > comm)
Zoltan2::XpetraMultiVectorAdapter< tMVector_t > MultiVectorAdapter_t
Zoltan2::XpetraCrsMatrixAdapter< SparseMatrix_t, tMVector_t > SparseMatrixAdapter_t
int testForPHG(SparseMatrixAdapter_t &matAdapter, int myrank, part_t numparts, RCP< tMVector_t > coords, RCP< const Teuchos::Comm< int > >)
Tpetra::Vector< z2TestScalar, z2TestLO, z2TestGO > Vector
zgno_t z2TestGO
SparseMatrixAdapter_t::part_t part_t
zscalar_t z2TestScalar
int testForMJ(SparseMatrixAdapter_t &matAdapter, int myrank, part_t numparts, RCP< tMVector_t > coords, RCP< const Teuchos::Comm< int > >)