Zoltan2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Sphynx_Research_Driver.cpp
Go to the documentation of this file.
1 // @HEADER
2 //
3 // ***********************************************************************
4 //
5 // Zoltan2: A package of combinatorial algorithms for scientific computing
6 // Copyright 2012 Sandia Corporation
7 //
8 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Redistribution and use in source and binary forms, with or without
12 // modification, are permitted provided that the following conditions are
13 // met:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 //
18 // 2. Redistributions in binary form must reproduce the above copyright
19 // notice, this list of conditions and the following disclaimer in the
20 // documentation and/or other materials provided with the distribution.
21 //
22 // 3. Neither the name of the Corporation nor the names of the
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
38 // Questions? Contact Seher Acer (sacer@sandia.gov)
39 // Erik Boman (egboman@sandia.gov)
40 // Siva Rajamanickam (srajama@sandia.gov)
41 // Jennifer Loe (jloe@sandia.gov)
42 //
43 // ***********************************************************************
44 //
45 // @HEADER
46 
47 #include "Teuchos_CommandLineProcessor.hpp"
48 #include "Tpetra_CrsMatrix.hpp"
49 #include "Tpetra_Core.hpp"
50 #include "Tpetra_KokkosCompat_DefaultNode.hpp"
52 
55 
56 #include "Teuchos_TimeMonitor.hpp"
57 #include "Teuchos_StackedTimer.hpp"
59 
60 #include <Galeri_MultiVectorTraits.hpp>
61 #include <Galeri_XpetraProblemFactory.hpp>
62 #include <Galeri_XpetraParameters.hpp>
63 #include <MatrixMarket_Tpetra.hpp>
64 
66 // This is a driver with many available options that can be used to test
67 // a variety of features of Sphynx.
68 // Note: This is research code. We do not guarantee it is without bugs.
70 
71 /* -------------------------------------------------------------------------
72  * Function: buildCrsMatrix
73  * Purpose: When the user does not input a matrix file, buildCrsMatrix will
74  * construct a Brick3D matrix in either 1, 2, or 3 dimensions. The
75  * Brick3D matrix is a 27-point difference stencil for the Laplace
76  * operator on a hex mesh.
77  * ------------------------------------------------------------------------- */
78 
79 template <typename lno_t, typename gno_t, typename scalar_t, typename nod_t>
80 int buildCrsMatrix(int xdim, int ydim, int zdim, std::string problemType,
81  const Teuchos::RCP<const Teuchos::Comm<int> > &comm,
82  Teuchos::RCP<Tpetra::CrsMatrix<scalar_t, lno_t, gno_t, nod_t>> &M_)
83 {
84  /* Print size of the mesh being constructed */
85  if (comm->getRank() == 0){
86  std::cout << "Create matrix with " << problemType;
87  std::cout << " (a " << xdim;
88  if (zdim > 0)
89  std::cout << " x " << ydim << " x " << zdim << " ";
90  else if (ydim > 0)
91  std::cout << " x" << ydim << " x 1 ";
92  else
93  std::cout << "x 1 x 1 ";
94  std::cout << " mesh)" << std::endl;
95  }
96 
97  Teuchos::CommandLineProcessor tclp;
98  Galeri::Xpetra::Parameters<gno_t> params(tclp, xdim, ydim, zdim, problemType);
99 
100  Teuchos::RCP<const Tpetra::Map<lno_t, gno_t> > map =
101  Teuchos::rcp(new Tpetra::Map<lno_t, gno_t>(params.GetNumGlobalElements(), 0, comm));
102 
103  /* Build the Brick3D matrix */
104  try{
105  Teuchos::RCP<Galeri::Xpetra::Problem<Tpetra::Map<lno_t, gno_t>,
106  Tpetra::CrsMatrix<scalar_t, lno_t, gno_t, nod_t>,
107  Tpetra::MultiVector<scalar_t, lno_t, gno_t> > > Pr=
108  Galeri::Xpetra::BuildProblem<scalar_t, lno_t, gno_t,
109  Tpetra::Map<lno_t, gno_t>,
110  Tpetra::CrsMatrix<scalar_t, lno_t, gno_t, nod_t>,
111  Tpetra::MultiVector<scalar_t, lno_t, gno_t, nod_t> >
112  (params.GetMatrixType(), map, params.GetParameterList());
113 
114  M_ = Pr->BuildMatrix();
115  }
116  catch (std::exception &e) { // Probably not enough memory
117  if(comm->getRank() == 0) std::cout << "Error returned from Galeri " << e.what() << std::endl;
118  exit(-1);
119  }
120  if (M_.is_null())
121  return 1;
122  else
123  return 0;
124 }
125 
126 template <typename adapter_type>
127  void
128 compute_edgecut(Teuchos::RCP<adapter_type> &adapter,
130 {
131  typedef typename adapter_type::user_t graph_type;
132  typedef typename graph_type::global_ordinal_type GO;
133  typedef typename graph_type::local_ordinal_type LO;
134  typedef typename graph_type::node_type NO;
135  typedef typename adapter_type::part_t PT;
136 
137  using ordinal_view_t = Kokkos::View<GO*, typename NO::device_type>;
138  using part_view_t = Kokkos::View<PT*, typename NO::device_type>;
139 
140  auto graph = adapter->getUserGraph();
141  auto rowMap = graph->getRowMap();
142  auto colMap = graph->getColMap();
143 
144  size_t numLclRows = rowMap->getLocalNumElements();
145  size_t numGblRows = rowMap->getGlobalNumElements();
146  size_t numLclCols = colMap->getLocalNumElements();
147 
148  ordinal_view_t colLocalToGlobal(Kokkos::view_alloc("colLocalToGlobal", Kokkos::WithoutInitializing), numLclCols);
149  auto colMapHost = Kokkos::create_mirror_view (Kokkos::HostSpace (), colLocalToGlobal);
150  for(size_t i = 0; i < numLclCols; ++i)
151  colMapHost[i] = colMap->getGlobalElement(i);
152  Kokkos::deep_copy (colLocalToGlobal, colMapHost);
153 
154  ordinal_view_t rowLocalToGlobal(Kokkos::view_alloc("rowLocalToGlobal", Kokkos::WithoutInitializing), numLclRows);
155  auto rowMapHost = Kokkos::create_mirror_view (Kokkos::HostSpace (), rowLocalToGlobal);
156  for(size_t i = 0; i < numLclRows; ++i)
157  rowMapHost[i] = rowMap->getGlobalElement(i);
158  Kokkos::deep_copy (rowLocalToGlobal, rowMapHost);
159 
160  part_view_t localParts("localParts", numGblRows);
161  part_view_t globalParts("globalParts", numGblRows);
162  auto localPartsHost = Kokkos::create_mirror_view(Kokkos::HostSpace(), localParts);
163 
164  auto parts = solution.getPartListView();
165  for(size_t i = 0; i < numLclRows; i++){
166  GO gi = rowMap->getGlobalElement(i);
167  localPartsHost(gi) = parts[i];
168  }
169  Kokkos::deep_copy(localParts, localPartsHost);
170 
171  auto comm = graph->getComm();
172  Teuchos::reduceAll<int, PT> (*comm, Teuchos::REDUCE_SUM, numGblRows, localParts.data(), globalParts.data());
173 
174  auto rowPtr = graph->getLocalGraphHost().row_map;
175  auto colInd = graph->getLocalGraphHost().entries;
176 
177  size_t localtotalcut = 0, totalcut = 0;
178 
179  using execution_space = typename NO::device_type::execution_space;
180  using range_policy = Kokkos::RangePolicy<execution_space, Kokkos::IndexType<LO>>;
181  Kokkos::parallel_reduce("Compute cut", range_policy(0, numLclRows),
182  KOKKOS_LAMBDA(const LO i, size_t &cut){
183 
184  const GO gRid = rowLocalToGlobal(i);
185  const PT gi = globalParts(gRid);
186 
187  const size_t start = rowPtr(i);
188  const size_t end = rowPtr(i+1);
189  for(size_t j = start; j < end; ++j) {
190 
191  const GO gCid = colLocalToGlobal(colInd(j));
192  PT gj = globalParts(gCid);
193  if(gi != gj)
194  cut += 1;
195  }
196  }, localtotalcut);
197 
198  Teuchos::reduceAll (*comm, Teuchos::REDUCE_SUM, 1, &localtotalcut, &totalcut);
199 
200  // compute imbalance
201  auto rowPtr_h = Kokkos::create_mirror_view(rowPtr);
202  Kokkos::deep_copy(rowPtr_h, rowPtr);
203  int nparts = (int)solution.getTargetGlobalNumberOfParts();
204 
205  size_t *partw = new size_t[nparts];
206  size_t *partc = new size_t[nparts];
207 
208  size_t *gpartw = new size_t[nparts];
209  size_t *gpartc = new size_t[nparts];
210 
211  for(int i = 0; i < nparts; i++){
212  partw[i] = 0; partc[i] = 0;
213  gpartw[i] = 0; gpartc[i] = 0;
214  }
215 
216  for(size_t i = 0; i < numLclRows; i++){
217  partw[parts[i]] += rowPtr_h(i+1) - rowPtr_h(i) - 1;
218  partc[parts[i]] ++;
219  }
220 
221  Teuchos::reduceAll (*comm, Teuchos::REDUCE_SUM, nparts, partw, gpartw);
222  Teuchos::reduceAll (*comm, Teuchos::REDUCE_SUM, nparts, partc, gpartc);
223 
224  size_t maxc = 0, totc = 0;
225  size_t maxw = 0, totw = 0;
226 
227  for(int i = 0; i < nparts; i++){
228  if(gpartw[i] > maxw)
229  maxw = gpartw[i];
230  if(gpartc[i] > maxc)
231  maxc = gpartc[i];
232  totw += gpartw[i];
233  totc += gpartc[i];
234  }
235 
236  double imbw = (double)maxw/((double)totw/nparts);
237  double imbc = (double)maxc/((double)totc/nparts);
238 
239  if(comm->getRank() == 0) {
240 
241  std::cout << "\n\n************************************************" << std::endl;
242  std::cout << " EDGECUT: " << totalcut << std::endl;
243  std::cout << " MAX/AVG WEIGHT: " << imbw << std::endl;
244  std::cout << " MAX/AVG COUNT: " << imbc << std::endl;
245  std::cout << "************************************************\n\n" << std::endl;
246 
247  }
248 
249 }
250 
251 int main(int narg, char *arg[])
252 {
253 
254  Tpetra::ScopeGuard tpetraScope (&narg, &arg);
255  {
256 
257  const Teuchos::RCP<const Teuchos::Comm<int>> pComm= Tpetra::getDefaultComm();
258 
259  int me = pComm->getRank();
260 
261  // Parameters
262  int nparts = 64;
263  int max_iters = 1000;
264  int block_size = -1;
265  int rand_seed =1;
266  int resFreq = 0;
267  int orthoFreq = 0;
268  std::string matrix_file = "";
269  std::string eigensolve = "LOBPCG";
270  bool parmetis = false;
271  bool pulp = false;
272 
273  int verbosity = 1;
274 
275  std::string ptype = "";
276  std::string prec = "jacobi";
277  std::string init = "random";
278  double tol = 1e-1;
279 
280  // Echo the command line
281  if (me == 0) {
282  for (int i = 0; i < narg; i++)
283  std::cout << arg[i] << " ";
284  std::cout << std::endl;
285  }
286 
287  Teuchos::CommandLineProcessor cmdp(false,true);
288  cmdp.setOption("matrix_file",&matrix_file,
289  "Path and filename of the matrix to be read.");
290  cmdp.setOption("nparts",&nparts,
291  "Number of global parts desired in the resulting partition.");
292  cmdp.setOption("rand_seed",&rand_seed,
293  "Seed for the random multivector.");
294  cmdp.setOption("max_iters",&max_iters,
295  "Maximum iters for the eigensolver.");
296  cmdp.setOption("block_size",&block_size,
297  "Block size.");
298  cmdp.setOption("verbosity", &verbosity,
299  "Verbosity level");
300  cmdp.setOption("parmetis", "sphynx", &parmetis,
301  "Whether to use parmetis.");
302  cmdp.setOption("pulp", "sphynx", &pulp,
303  "Whether to use pulp.");
304  cmdp.setOption("prec", &prec,
305  "Prec type to use.");
306  cmdp.setOption("eigensolve", &eigensolve,
307  "Eigensolver to use: LOBPCG, BlockDavidson, GeneralizedDavidson, BlockKrylovSchur or randomized.");
308  cmdp.setOption("prob", &ptype,
309  "Problem type to use. Options are combinatorial, normalized or generalized.");
310  cmdp.setOption("tol", &tol,
311  "Tolerance to use.");
312  cmdp.setOption("init", &init,
313  "Sphynx Initial guess. Options: random or constants. Default: random if randomized solver is used.");
314  cmdp.setOption("resFreq", &resFreq,
315  "(For randomized) Specify how often to check the residuals. Orthogonalization of the basis is also done.");
316  cmdp.setOption("orthoFreq", &orthoFreq,
317  "(For randomized) Specify how often to orthogonalize the basis.");
318 
319  if (cmdp.parse(narg,arg)!=Teuchos::CommandLineProcessor::PARSE_SUCCESSFUL) return -1;
320 
321  // Print the most essential options (not in the MyPL parameters later)
322  if (me == 0 ){
323  std::cout << std::endl << "--------------------------------------------------" << matrix_file << std::endl;
324  std::cout << "| Sphynx Parameters |" << matrix_file << std::endl;
325  std::cout << "--------------------------------------------------" << matrix_file << std::endl;
326  std::cout << " matrix file = " << matrix_file << std::endl;
327  std::cout << " nparts = " << nparts << std::endl;
328  std::cout << " verbosity = " << verbosity << std::endl;
329  std::cout << " parmetis = " << parmetis << std::endl;
330  std::cout << " pulp = " << pulp << std::endl;
331  std::cout << " prec = " << prec << std::endl;
332  std::cout << " eigensolver = " << eigensolve << std::endl;
333  std::cout << " prob = " << ptype << std::endl;
334  std::cout << " tol = " << tol << std::endl;
335  std::cout << " init = " << init << std::endl;
336  std::cout << " resFreq = " << resFreq << std::endl;
337  std::cout << " orthoFreq = " << orthoFreq << std::endl;
338  std::cout << "--------------------------------------------------" << matrix_file << std::endl << std::endl;
339  }
340 
341  using scalar_type = Tpetra::Details::DefaultTypes::scalar_type;
342  using local_ordinal_type = Tpetra::Details::DefaultTypes::local_ordinal_type;
343  using global_ordinal_type = Tpetra::Details::DefaultTypes::global_ordinal_type;
344  using node_type = Tpetra::Details::DefaultTypes::node_type;
345 
346  using crs_matrix_type = Tpetra::CrsMatrix<scalar_type, local_ordinal_type, global_ordinal_type, node_type>;
348  using solution_type = Zoltan2::PartitioningSolution<adapter_type>;
349 
350  // Read the input matrix
351  Teuchos::RCP<adapter_type> adapter;
352  Teuchos::RCP<crs_matrix_type> tmatrix;
353 
354  // Set the random seed and hope it goes through to Tpetra.
355  std::srand(rand_seed);
356 
357  // Read in user-specified matrix or create default Brick3D matrix (100^3)
358  std::string mtx = ".mtx", mm = ".mm", lc = ".largestComp", lc2 = ".bin";
359  if(std::equal(lc.rbegin(), lc.rend(), matrix_file.rbegin()) || std::equal(lc2.rbegin(), lc2.rend(), matrix_file.rbegin())) {
360  tmatrix = readMatrixFromBinaryFile<crs_matrix_type>(matrix_file, pComm, true, verbosity>0);
361  if(me == 0 && verbosity > 1) std::cout << "Used Seher's reader for Largest Comp." << std::endl;
362  }
363  else if(std::equal(mtx.rbegin(), mtx.rend(), matrix_file.rbegin()) || std::equal(mm.rbegin(), mm.rend(), matrix_file.rbegin())) {
364  typedef Tpetra::MatrixMarket::Reader<crs_matrix_type> reader_type;
365  reader_type r;
366  tmatrix = r.readSparseFile(matrix_file, pComm);
367  if(me == 0 && verbosity > 1) std::cout << "Used standard Matrix Market reader." << std::endl;
368  }
369  else { // Build Brick3D matrix
370  /* If the user entered something that was not an .mtx or .largestComp file,
371  * check if it is a mesh size. If invalid input, default to 100^3 Brick3D.
372  */
373  int meshdim = 100;
374  if(matrix_file != "")
375  {
376  std::string::const_iterator it = matrix_file.begin();
377  while(it != matrix_file.end() && std::isdigit(*it)) ++it;
378  if(it == matrix_file.end())
379  {
380  meshdim = std::stoi(matrix_file);
381  }
382  else {
383  if(me == 0) std::cout << "Invalid matrix file entered. Reverting to default matrix." << std::endl;
384  }
385  }
386  int ierr = buildCrsMatrix<local_ordinal_type, global_ordinal_type, scalar_type>
387  (meshdim, meshdim, meshdim, "Brick3D", pComm, tmatrix);
388  if (ierr != 0) std::cout << "Error! Brick3D failed to build!" << std::endl;
389  }
390 
391  if(me == 0 && verbosity > 0) std::cout << "Done with reading/creating the matrix." << std::endl;
392 
393  Teuchos::RCP<const Tpetra::Map<> > map = tmatrix->getMap();
394 
395  adapter = Teuchos::rcp(new adapter_type(tmatrix->getCrsGraph(), 1));
396  adapter->setVertexWeightIsDegree(0);
397 
398  // Set the Sphynx parameters
399  Teuchos::RCP<Teuchos::ParameterList> params = Teuchos::rcp(new Teuchos::ParameterList());
400  Teuchos::RCP<Teuchos::ParameterList> sphynxParams(new Teuchos::ParameterList);
401  params->set("num_global_parts", nparts);
402 
403  Teuchos::RCP<Teuchos::StackedTimer> stacked_timer;
404  stacked_timer = Teuchos::rcp(new Teuchos::StackedTimer("SphynxDriver"));
405  Teuchos::TimeMonitor::setStackedTimer(stacked_timer);
406  if(parmetis || pulp) {
407 
408  params->set("partitioning_approach", "partition");
409  params->set("imbalance_tolerance", 1.01);
410  if(parmetis) {
411  params->set("algorithm", "parmetis");
412  params->set("imbalance_tolerance", 1.01);
413  }
414  else {
415  params->set("algorithm", "pulp");
416  params->set("pulp_vert_imbalance", 1.01);
417  }
418 
419  using problem_type = Zoltan2::PartitioningProblem<adapter_type>;
420  Teuchos::RCP<problem_type> problem;
421  pComm->barrier();
422  {
423  Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("Partitioning::All"));
424  {
425  Teuchos::TimeMonitor t2(*Teuchos::TimeMonitor::getNewTimer("Partitioning::Problem"));
426  problem = Teuchos::rcp(new problem_type(adapter.getRawPtr(), params.getRawPtr(), Tpetra::getDefaultComm()));
427  }
428  {
429  Teuchos::TimeMonitor t3(*Teuchos::TimeMonitor::getNewTimer("Partitioning::Solve"));
430  problem->solve();
431  }
432  }
433  pComm->barrier();
434 
435  solution_type solution = problem->getSolution();
436  compute_edgecut<adapter_type>(adapter, solution);
437 
438  } else {
439 
440  sphynxParams->set("sphynx_verbosity", verbosity);
441  sphynxParams->set("sphynx_max_iterations", max_iters);
442  sphynxParams->set("sphynx_ortho_freq", orthoFreq);
443  sphynxParams->set("sphynx_res_freq", resFreq);
444  sphynxParams->set("sphynx_skip_preprocessing", true);
445  sphynxParams->set("sphynx_eigensolver", eigensolve);
446  if (block_size > 0) sphynxParams->set("sphynx_block_size", block_size);
447  if (ptype != "") sphynxParams->set("sphynx_problem_type", ptype);
448  if (init != "") sphynxParams->set("sphynx_initial_guess", init);
449  if (prec != "") sphynxParams->set("sphynx_preconditioner_type", prec);
450  if (tol != -1) sphynxParams->set("sphynx_tolerance", tol);
451 
452  using problem_type = Zoltan2::SphynxProblem<adapter_type>; //We found sphynx
453  Teuchos::RCP<problem_type> problem;
454  pComm->barrier();
455  {
456  Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("Partitioning::All"));
457  {
458  Teuchos::TimeMonitor t2(*Teuchos::TimeMonitor::getNewTimer("Partitioning::Problem"));
459  problem = Teuchos::rcp(new problem_type(adapter.get(), params.get(), sphynxParams, Tpetra::getDefaultComm()));
460  }
461  {
462  if(me == 0) std::cout << eigensolve << " will be used to solve the partitioning problem." << std::endl;
463  problem->solve();
464  }
465  pComm->barrier();
466  }
467  solution_type solution = problem->getSolution();
468  compute_edgecut<adapter_type>(adapter, solution);
469  }
470  stacked_timer->stopBaseTimer();
471 
472  Teuchos::RCP<Teuchos::FancyOStream> fancy2 = Teuchos::fancyOStream(Teuchos::rcpFromRef(std::cout));
473  Teuchos::FancyOStream& out2 = *fancy2;
474  Teuchos::StackedTimer::OutputOptions options;
475  options.output_fraction = options.output_histogram = options.output_minmax = true;
476  stacked_timer->report(out2, pComm, options);
477 
478  Teuchos::TimeMonitor::summarize();
479 
480  } //End Tpetra scope guard
481  return 0;
482 }
void compute_edgecut(Teuchos::RCP< adapter_type > &adapter, Zoltan2::PartitioningSolution< adapter_type > &solution)
map_t::global_ordinal_type gno_t
Definition: mapRemotes.cpp:18
Provides access for Zoltan2 to Xpetra::CrsGraph data.
int main(int narg, char **arg)
Definition: coloring1.cpp:199
int buildCrsMatrix(int xdim, int ydim, int zdim, std::string problemType, const Teuchos::RCP< const Teuchos::Comm< int > > &comm, Teuchos::RCP< Tpetra::CrsMatrix< scalar_t, lno_t, gno_t, nod_t >> &M_)
Defines XpetraCrsGraphAdapter class.
SparseMatrixAdapter_t::part_t part_t
A PartitioningSolution is a solution to a partitioning problem.
const part_t * getPartListView() const
Returns the part list corresponding to the global ID list.
map_t::local_ordinal_type lno_t
Definition: mapRemotes.cpp:17
PartitioningProblem sets up partitioning problems for the user.
Defines the PartitioningProblem class.
size_t getTargetGlobalNumberOfParts() const
Returns the global number of parts desired in the solution.
Zoltan2::BasicUserTypes< zscalar_t, zlno_t, zgno_t > user_t
Definition: Metric.cpp:74