Zoltan2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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 
13 #include <Zoltan2_TestHelpers.hpp>
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 
24 using Teuchos::RCP;
25 
26 typedef zlno_t z2TestLO;
27 typedef zgno_t z2TestGO;
29 
30 typedef Tpetra::CrsMatrix<z2TestScalar, z2TestLO, z2TestGO> SparseMatrix_t;
31 typedef Tpetra::Vector<z2TestScalar, z2TestLO, z2TestGO> Vector;
32 typedef Vector::node_type Node;
33 
34 typedef Tpetra::MultiVector<z2TestScalar, z2TestLO, z2TestGO,znode_t> tMVector_t;
35 
36 
38 
40 
42 
43 
44 
45 int testForRCB(SparseMatrixAdapter_t &matAdapter, int myrank, part_t numparts,
46  RCP<tMVector_t> coords, RCP<const Teuchos::Comm<int> > comm);
47 int testForPHG(SparseMatrixAdapter_t &matAdapter, int myrank, part_t numparts,
48  RCP<tMVector_t> coords, RCP<const Teuchos::Comm<int> >);
49 int testForMJ(SparseMatrixAdapter_t &matAdapter, int myrank, part_t numparts,
50  RCP<tMVector_t> coords, RCP<const Teuchos::Comm<int> >);
51 
52 
55 int 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
204 bool 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..
365 int 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.
445 int 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.
528 int 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 
zgno_t z2TestGO
Definition: coloring1.cpp:41
zlno_t z2TestLO
Definition: coloring1.cpp:40
Provides access for Zoltan2 to Xpetra::CrsMatrix data.
virtual bool isPartitioningTreeBinary() const
calculate if partition tree is binary.
int analyze(Zoltan2::PartitioningProblem< SparseMatrixAdapter_t > &problem, RCP< const Teuchos::Comm< int > > comm)
Tpetra::CrsMatrix< z2TestScalar, z2TestLO, z2TestGO > SparseMatrix_t
Definition: nd.cpp:35
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.
SparseMatrixAdapter_t::part_t part_t
Defines the XpetraCrsMatrixAdapter class.
A PartitioningSolution is a solution to a partitioning problem.
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)
Zoltan2::XpetraCrsMatrixAdapter< SparseMatrix_t, tMVector_t > SparseMatrixAdapter_t
Definition: nd.cpp:43
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
Zoltan2::XpetraMultiVectorAdapter< tMVector_t > MultiVectorAdapter_t
Definition: nd.cpp:45
An adapter for Xpetra::MultiVector.
int testForRCB(SparseMatrixAdapter_t &matAdapter, int myrank, part_t numparts, RCP< tMVector_t > coords, RCP< const Teuchos::Comm< int > > comm)
Tpetra::Map::local_ordinal_type zlno_t
const PartitioningSolution< Adapter > & getSolution()
Get the solution to the problem.
PartitioningProblem sets up partitioning problems for the user.
int testForMJ(SparseMatrixAdapter_t &matAdapter, int myrank, part_t numparts, RCP< tMVector_t > coords, RCP< const Teuchos::Comm< int > >)
Defines the PartitioningProblem class.
void setCoordinateInput(VectorAdapter< UserCoord > *coordData) override
Allow user to provide additional data that contains coordinate info associated with the MatrixAdapter...
zscalar_t z2TestScalar
Definition: coloring1.cpp:42
float zscalar_t
int testForPHG(SparseMatrixAdapter_t &matAdapter, int myrank, part_t numparts, RCP< tMVector_t > coords, RCP< const Teuchos::Comm< int > >)
Tpetra::MultiVector< zscalar_t, zlno_t, zgno_t, znode_t > tMVector_t
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(".")