Zoltan2
Zoltan2_Sphynx.hpp
Go to the documentation of this file.
1 // @HEADER
2 //
3 // ***********************************************************************
4 //
5 // Sphynx
6 // Copyright 2020 National Technology & Engineering
7 // Solutions of Sandia, LLC (NTESS).
8 //
9 // Under the terms of Contract DE-NA0003525 with NTESS,
10 // the U.S. Government retains certain rights in this software.
11 //
12 // Redistribution and use in source and binary forms, with or without
13 // modification, are permitted provided that the following conditions are
14 // met:
15 //
16 // 1. Redistributions of source code must retain the above copyright
17 // notice, this list of conditions and the following disclaimer.
18 //
19 // 2. Redistributions in binary form must reproduce the above copyright
20 // notice, this list of conditions and the following disclaimer in the
21 // documentation and/or other materials provided with the distribution.
22 //
23 // 3. Neither the name of the Corporation nor the names of the
24 // contributors may be used to endorse or promote products derived from
25 // this software without specific prior written permission.
26 //
27 // THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY
28 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE
31 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
33 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
34 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
35 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
36 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 //
39 // Questions? Contact Seher Acer (sacer@sandia.gov)
40 // Erik Boman (egboman@sandia.gov)
41 // Siva Rajamanickam (srajama@sandia.gov)
42 // Karen Devine (kddevin@sandia.gov)
43 //
44 // ***********************************************************************
45 //
46 // @HEADER
47 
48 #ifndef _ZOLTAN2_SPHYNXALGORITHM_HPP_
49 #define _ZOLTAN2_SPHYNXALGORITHM_HPP_
50 
51 
53 // This file contains the Sphynx algorithm.
54 //
55 // Sphynx is a graph partitioning algorithm that is based on a spectral method.
56 // It has three major steps:
57 // 1) compute the Laplacian matrix of the input graph,
58 // 2) compute logK+1 eigenvectors on the Laplacian matrix,
59 // 3) use eigenvector entries as coordinates and compute a K-way partition on
60 // them using a geometric method.
61 //
62 // Step1: Sphynx provides three eigenvalue problems and hence Laplacian matrix:
63 // i) combinatorial (Lx = \lambdax, where L = A - D)
64 // ii) generalized (Lx = \lambdaDx, where L = A - D)
65 // iii) normalized (L_nx, \lambdax, where Ln = D^{-1/2}LD^{-1/2}
66 // and L = A - D)
67 //
68 // Step2: Sphynx calls the LOBPCG algorithm provided in Anasazi to obtain
69 // logK+1 eigenvectors.
70 // Step3: Sphynx calls the MJ algorithm provided in Zoltan2Core to compute the
71 // partition.
73 
74 #include "Zoltan2Sphynx_config.h"
75 
78 
81 
82 #include "AnasaziLOBPCGSolMgr.hpp"
83 #include "AnasaziBasicEigenproblem.hpp"
84 #include "AnasaziTpetraAdapter.hpp"
85 
86 #include "BelosLinearProblem.hpp"
87 #include "BelosTpetraOperator.hpp"
88 
89 #include "Ifpack2_Factory.hpp"
90 
91 #include "Teuchos_TimeMonitor.hpp"
92 
93 #ifdef HAVE_ZOLTAN2SPHYNX_MUELU
94 #include "MueLu_CreateTpetraPreconditioner.hpp"
95 #endif
96 
97 namespace Zoltan2 {
98 
99  template <typename Adapter>
100  class Sphynx : public Algorithm<Adapter>
101  {
102 
103  public:
104 
105  using scalar_t = double; // Sphynx with scalar_t=double obtains better cutsize
106  using lno_t = typename Adapter::lno_t;
107  using gno_t = typename Adapter::gno_t;
108  using node_t = typename Adapter::node_t;
109  using offset_t = typename Adapter::offset_t;
110  using part_t = typename Adapter::part_t;
111  using weight_t = typename Adapter::scalar_t;
112 
113  using graph_t = Tpetra::CrsGraph<lno_t, gno_t, node_t>;
114  using matrix_t = Tpetra::CrsMatrix<scalar_t, lno_t, gno_t, node_t>;
115  using mvector_t = Tpetra::MultiVector<scalar_t, lno_t, gno_t, node_t>;
116  using op_t = Tpetra::Operator<scalar_t, lno_t, gno_t, node_t>;
117 
119 
123 
124  // Takes the graph from the input adapter and computes the Laplacian matrix
125  Sphynx(const RCP<const Environment> &env,
126  const RCP<Teuchos::ParameterList> &params,
127  const RCP<const Comm<int> > &comm,
128  const RCP<const XpetraCrsGraphAdapter<graph_t> > &adapter) :
129  env_(env), params_(params), comm_(comm), adapter_(adapter)
130  {
131 
132  // Don't compute the Laplacian if the number of requested parts is 1
133  const Teuchos::ParameterEntry *pe = params_->getEntryPtr("num_global_parts");
134  numGlobalParts_ = pe->getValue<int>(&numGlobalParts_);
135  if(numGlobalParts_ > 1){
136 
137  Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("Sphynx::Laplacian"));
138 
139  // The verbosity is common throughout the algorithm, better to check and set now
140  pe = params_->getEntryPtr("sphynx_verbosity");
141  if (pe)
142  verbosity_ = pe->getValue<int>(&verbosity_);
143 
144  // Do we need to pre-process the input?
145  pe = params_->getEntryPtr("sphynx_skip_preprocessing");
146  if (pe)
147  skipPrep_ = pe->getValue<bool>(&skipPrep_);
148 
149  // Get the graph from XpetraCrsGraphAdapter if skipPrep_ is true
150  // We assume the graph has all of the symmetric and diagonal entries
151  if(skipPrep_)
152  graph_ = adapter_->getUserGraph();
153  else {
154  throw std::runtime_error("\nSphynx Error: Preprocessing has not been implemented yet.\n");
155  }
156 
157  // Check if the graph is regular
159 
160  // Set Sphynx defaults: preconditioner, problem type, tolerance, initial vectors.
161  setDefaults();
162 
163  // Compute the Laplacian matrix
165 
166  if(problemType_ == GENERALIZED)
168 
169  }
170  }
171 
175 
176  void partition(const RCP<PartitioningSolution<Adapter> > &solution);
177 
178 
179  int LOBPCGwrapper(const int numEigenVectors);
180 
181  template<typename problem_t>
182  void setPreconditioner(Teuchos::RCP<problem_t> &problem);
183 
184  template<typename problem_t>
185  void setMueLuPreconditioner(Teuchos::RCP<problem_t> &problem);
186 
187  template<typename problem_t>
188  void setJacobiPreconditioner(Teuchos::RCP<problem_t> &problem);
189 
190  template<typename problem_t>
191  void setPolynomialPreconditioner(Teuchos::RCP<problem_t> &problem);
192 
193 
194  void eigenvecsToCoords(Teuchos::RCP<mvector_t> &eigenVectors,
195  int computedNumEv,
196  Teuchos::RCP<mvector_t> &coordinates);
197 
198 
199  void computeWeights(std::vector<const weight_t *> vecweights,
200  std::vector<int> strides);
201 
202 
203  void MJwrapper(const Teuchos::RCP<const mvector_t> &coordinates,
204  std::vector<const weight_t *> weights,
205  std::vector<int> strides,
206  const Teuchos::RCP<PartitioningSolution<Adapter>> &solution);
207 
208 
212 
214  // Determine input graph's regularity = maxDegree/AvgDegree < 10.
215  // If Laplacian type is not specified, then use combinatorial for regular
216  // and generalized for irregular.
217  // MueLu settings will differ depending on the regularity, too.
219  {
220  // Get the row pointers in the host
221  auto rowOffsets = graph_->getLocalGraphDevice().row_map;
222  auto rowOffsets_h = Kokkos::create_mirror_view(rowOffsets);
223  Kokkos::deep_copy(rowOffsets_h, rowOffsets);
224 
225  // Get size information
226  const size_t numGlobalEntries = graph_->getGlobalNumEntries();
227  const size_t numLocalRows = graph_->getNodeNumRows();
228  const size_t numGlobalRows = graph_->getGlobalNumRows();
229 
230  // Compute local maximum degree
231  size_t localMax = 0;
232  for(size_t i = 0; i < numLocalRows; i++){
233  if(rowOffsets_h(i+1) - rowOffsets_h(i) - 1 > localMax)
234  localMax = rowOffsets_h(i+1) - rowOffsets_h(i) - 1;
235  }
236 
237  // Compute global maximum degree
238  size_t globalMax = 0;
239  Teuchos::reduceAll<int, size_t> (*comm_, Teuchos::REDUCE_MAX, 1, &localMax, &globalMax);
240 
241  double avg = static_cast<double>(numGlobalEntries-numGlobalRows)/numGlobalRows;
242  double maxOverAvg = static_cast<double>(globalMax)/avg;
243 
244  // Use generalized Laplacian on irregular graphs
245  irregular_ = false;
246  if(maxOverAvg > 10) {
247  irregular_ = true;
248  }
249 
250  // Let the user know about what we determined if verbose
251  if(verbosity_ > 0) {
252  if(comm_->getRank() == 0) {
253  std::cout << "Determining Regularity -- max degree: " << globalMax
254  << ", avg degree: " << avg << ", max/avg: " << globalMax/avg << std::endl
255  << "Determined Regularity -- regular: " << !irregular_ << std::endl;
256 
257  }
258  }
259  }
260 
262  // If preconditioner type is not specified:
263  // use muelu if it is enabled, and jacobi otherwise.
264  // If eigenvalue problem type is not specified:
265  // use combinatorial for regular and
266  // normalized for irregular with polynomial preconditioner,
267  // generalized for irregular with other preconditioners.
268  // If convergence tolerance is not specified:
269  // use 1e-3 for regular with jacobi and polynomial, and 1e-2 otherwise.
270  // If how to decide the initial vectors is not specified:
271  // use random for regular and constant for irregular
272  void setDefaults()
273  {
274 
275  // Set the default preconditioner to muelu if it is enabled, jacobi otherwise.
276  precType_ = "jacobi";
277 #ifdef HAVE_ZOLTAN2SPHYNX_MUELU
278  precType_ = "muelu";
279 #endif
280 
281  // Override the preconditioner type with the user's preference
282  const Teuchos::ParameterEntry *pe = params_->getEntryPtr("sphynx_preconditioner_type");
283  if (pe) {
284  precType_ = pe->getValue<std::string>(&precType_);
285  if(precType_ != "muelu" && precType_ != "jacobi" && precType_ != "polynomial")
286  throw std::runtime_error("\nSphynx Error: " + precType_ + " is not recognized as a preconditioner.\n"
287  + " Possible values: muelu (if enabled), jacobi, and polynomial\n");
288  }
289 
290  // Set the default problem type
291  problemType_ = COMBINATORIAL;
292  if(irregular_) {
293  if(precType_ == "polynomial")
294  problemType_ = NORMALIZED;
295  else
296  problemType_ = GENERALIZED;
297  }
298 
299  // Override the problem type with the user's preference
300  pe = params_->getEntryPtr("sphynx_problem_type");
301  if (pe) {
302  std::string probType = "";
303  probType = pe->getValue<std::string>(&probType);
304 
305  if(probType == "combinatorial")
306  problemType_ = COMBINATORIAL;
307  else if(probType == "generalized")
308  problemType_ = GENERALIZED;
309  else if(probType == "normalized")
310  problemType_ = NORMALIZED;
311  else
312  throw std::runtime_error("\nSphynx Error: " + probType + " is not recognized as a problem type.\n"
313  + " Possible values: combinatorial, generalized, and normalized.\n");
314  }
315 
316 
317  // Set the default for tolerance
318  tolerance_ = 1.0e-2;
319  if(!irregular_ && (precType_ == "jacobi" || precType_ == "polynomial"))
320  tolerance_ = 1.0e-3;
321 
322 
323  // Override the tolerance with the user's preference
324  pe = params_->getEntryPtr("sphynx_tolerance");
325  if (pe)
326  tolerance_ = pe->getValue<scalar_t>(&tolerance_);
327 
328 
329  // Set the default for initial vectors
330  randomInit_ = true;
331  if(irregular_)
332  randomInit_ = false;
333 
334  // Override the initialization method with the user's preference
335  pe = params_->getEntryPtr("sphynx_initial_guess");
336  if (pe) {
337  std::string initialGuess = "";
338  initialGuess = pe->getValue<std::string>(&initialGuess);
339 
340  if(initialGuess == "random")
341  randomInit_ = true;
342  else if(initialGuess == "constants")
343  randomInit_ = false;
344  else
345  throw std::runtime_error("\nSphynx Error: " + initialGuess + " is not recognized as an option for initial guess.\n"
346  + " Possible values: random and constants.\n");
347  }
348 
349  }
350 
352  // Compute the Laplacian matrix depending on the eigenvalue problem type.
353  // There are 3 options for the type: combinatorial, generalized, and normalized.
354  // Combinatorial and generalized share the same Laplacian but generalized
355  // also needs a degree matrix.
357  {
358 
359  if(problemType_ == NORMALIZED)
360  laplacian_ = computeNormalizedLaplacian();
361  else
362  laplacian_ = computeCombinatorialLaplacian();
363  }
364 
366  // Compute a diagonal matrix with the vertex degrees in the input graph
368  {
369 
370  // Get the row pointers in the host
371  auto rowOffsets = graph_->getLocalGraphHost().row_map;
372 
373  // Create the degree matrix with max row size set to 1
374  Teuchos::RCP<matrix_t> degMat(new matrix_t (graph_->getRowMap(),
375  graph_->getRowMap(),
376  1, Tpetra::StaticProfile));
377 
378  scalar_t *val = new scalar_t[1];
379  lno_t *ind = new lno_t[1];
380  lno_t numRows = static_cast<lno_t>(graph_->getNodeNumRows());
381 
382  // Insert the diagonal entries as the degrees
383  for (lno_t i = 0; i < numRows; ++i) {
384  val[0] = rowOffsets(i+1) - rowOffsets(i) - 1;
385  ind[0] = i;
386  degMat->insertLocalValues(i, 1, val, ind);
387  }
388  delete [] val;
389  delete [] ind;
390 
391  degMat->fillComplete(graph_->getDomainMap(), graph_->getRangeMap());
392 
393  degMatrix_ = degMat;
394  }
395 
397  // Compute the combinatorial Laplacian: L = D - A.
398  // l_ij = degree of vertex i if i = j
399  // l_ij = -1 if i != j and a_ij != 0
400  // l_ij = 0 if i != j and a_ij = 0
401  Teuchos::RCP<matrix_t> computeCombinatorialLaplacian()
402  {
403  using range_policy = Kokkos::RangePolicy<
404  typename node_t::device_type::execution_space, Kokkos::IndexType<lno_t>>;
405  using values_view_t = Kokkos::View<scalar_t*, typename node_t::device_type>;
406  using offset_view_t = Kokkos::View<size_t*, typename node_t::device_type>;
407 
408  const size_t numEnt = graph_->getNodeNumEntries();
409  const size_t numRows = graph_->getNodeNumRows();
410 
411  // Create new values for the Laplacian, initialize to -1
412  values_view_t newVal (Kokkos::view_alloc("CombLapl::val", Kokkos::WithoutInitializing), numEnt);
413  Kokkos::deep_copy(newVal, -1);
414 
415  // Get the diagonal offsets
416  offset_view_t diagOffsets(Kokkos::view_alloc("Diag Offsets", Kokkos::WithoutInitializing), numRows);
417  graph_->getLocalDiagOffsets(diagOffsets);
418 
419  // Get the row pointers in the host
420  auto rowOffsets = graph_->getLocalGraphDevice().row_map;
421 
422  // Compute the diagonal entries as the vertex degrees
423  Kokkos::parallel_for("Combinatorial Laplacian", range_policy(0, numRows),
424  KOKKOS_LAMBDA(const lno_t i){
425  newVal(rowOffsets(i) + diagOffsets(i)) = rowOffsets(i+1) - rowOffsets(i) - 1;
426  }
427  );
428  Kokkos::fence ();
429 
430  // Create the Laplacian maatrix using the input graph and with the new values
431  Teuchos::RCP<matrix_t> laplacian (new matrix_t(graph_, newVal));
432  laplacian->fillComplete (graph_->getDomainMap(), graph_->getRangeMap());
433 
434  return laplacian;
435 
436  }
437 
438 
440  // Compute the normalized Laplacian: L_N = D^{-1/2} L D^{-1/2}, where L = D - A.
441  // l_ij = 1
442  // l_ij = -1/(sqrt(deg(v_i))sqrt(deg(v_j)) if i != j and a_ij != 0
443  // l_ij = 0 if i != j and a_ij = 0
444  Teuchos::RCP<matrix_t> computeNormalizedLaplacian()
445  {
446  using range_policy = Kokkos::RangePolicy<
447  typename node_t::device_type::execution_space, Kokkos::IndexType<lno_t>>;
448  using values_view_t = Kokkos::View<scalar_t*, typename node_t::device_type>;
449  using offset_view_t = Kokkos::View<size_t*, typename node_t::device_type>;
450  using vector_t = Tpetra::Vector<scalar_t, lno_t, gno_t, node_t>;
451  using dual_view_t = typename vector_t::dual_view_type;
452  using KAT = Kokkos::Details::ArithTraits<scalar_t>;
453 
454  const size_t numEnt = graph_->getNodeNumEntries();
455  const size_t numRows = graph_->getNodeNumRows();
456 
457  // Create new values for the Laplacian, initialize to -1
458  values_view_t newVal (Kokkos::view_alloc("NormLapl::val", Kokkos::WithoutInitializing), numEnt);
459  Kokkos::deep_copy(newVal, -1);
460 
461  // D^{-1/2}
462  dual_view_t dv ("MV::DualView", numRows, 1);
463  auto deginvsqrt = dv.d_view;
464 
465  // Get the diagonal offsets
466  offset_view_t diagOffsets(Kokkos::view_alloc("Diag Offsets", Kokkos::WithoutInitializing), numRows);
467  graph_->getLocalDiagOffsets(diagOffsets);
468 
469  // Get the row pointers
470  auto rowOffsets = graph_->getLocalGraphDevice().row_map;
471 
472  // Compute the diagonal entries as the vertex degrees
473  Kokkos::parallel_for("Combinatorial Laplacian", range_policy(0, numRows),
474  KOKKOS_LAMBDA(const lno_t i){
475  newVal(rowOffsets(i) + diagOffsets(i)) = rowOffsets(i+1) - rowOffsets(i) - 1;
476  deginvsqrt(i,0) = 1.0/KAT::sqrt(rowOffsets(i+1) - rowOffsets(i) - 1);
477  }
478  );
479  Kokkos::fence ();
480 
481  // Create the Laplacian graph using the same graph structure with the new values
482  Teuchos::RCP<matrix_t> laplacian (new matrix_t(graph_, newVal));
483  laplacian->fillComplete (graph_->getDomainMap(), graph_->getRangeMap());
484 
485  // Create the vector for D^{-1/2}
486  vector_t degInvSqrt(graph_->getRowMap(), dv);
487 
488  // Normalize the laplacian matrix as D^{-1/2} L D^{-1/2}
489  laplacian->leftScale(degInvSqrt);
490  laplacian->rightScale(degInvSqrt);
491 
492  return laplacian;
493  }
494 
498 
499  private:
500 
501  // User-provided members
502  const Teuchos::RCP<const Environment> env_;
503  Teuchos::RCP<Teuchos::ParameterList> params_;
504  const Teuchos::RCP<const Teuchos::Comm<int> > comm_;
505  const Teuchos::RCP<const Adapter> adapter_;
506 
507  // Internal members
508  int numGlobalParts_;
509  Teuchos::RCP<const graph_t> graph_;
510  Teuchos::RCP<matrix_t> laplacian_;
511  Teuchos::RCP<const matrix_t> degMatrix_;
512  Teuchos::RCP<mvector_t> eigenVectors_;
513 
514  bool irregular_; // decided internally
515  std::string precType_; // obtained from user params or decided internally
516  problemType problemType_; // obtained from user params or decided internally
517  double tolerance_; // obtained from user params or decided internally
518  bool randomInit_; // obtained from user params or decided internally
519  int verbosity_; // obtained from user params
520  bool skipPrep_; // obtained from user params
521  };
522 
523 
524 
528 
529 
531  // Compute a partition using the Laplacian matrix (and possibly the degree
532  // matrix as well). First call LOBPCG to compute logK+1 eigenvectors, then
533  // transform the eigenvectors to coordinates, and finally call MJ to compute
534  // a partition on the coordinates.
535  template <typename Adapter>
537  {
538  // Return a trivial solution if only one part is requested
539  if(numGlobalParts_ == 1) {
540 
541  size_t numRows =adapter_->getUserGraph()->getNodeNumRows();
542  Teuchos::ArrayRCP<part_t> parts(numRows,0);
543  solution->setParts(parts);
544 
545  return;
546 
547  }
548 
549  // The number of eigenvectors to be computed
550  int numEigenVectors = (int) log2(numGlobalParts_)+1;
551 
552  // Compute the eigenvectors using LOBPCG
553  int computedNumEv = Sphynx::LOBPCGwrapper(numEigenVectors);
554 
555  if(computedNumEv <= 1) {
556  throw
557  std::runtime_error("\nAnasazi Error: LOBPCGSolMgr::solve() returned unconverged.\n"
558  "Sphynx Error: LOBPCG could not compute any eigenvectors.\n"
559  " Increase either max iters or tolerance.\n");
560 
561  }
562 
563  // Transform the eigenvectors into coordinates
564  Teuchos::RCP<mvector_t> coordinates;
565  Sphynx::eigenvecsToCoords(eigenVectors_, computedNumEv, coordinates);
566 
567  // Get the weights from the adapter
568  std::vector<const weight_t *> weights;
569  std::vector<int> wstrides;
570  Sphynx::computeWeights(weights, wstrides);
571 
572 
573  // Compute the partition using MJ on coordinates
574  Sphynx::MJwrapper(coordinates, weights, wstrides, solution);
575 
576  }
577 
578 
580  // Call LOBPCG on the Laplacian matrix.
581  template <typename Adapter>
582  int Sphynx<Adapter>::LOBPCGwrapper(const int numEigenVectors)
583  {
584 
585  Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("Sphynx::LOBPCG"));
586 
587  // Set defaults for the parameters
588  std::string which = "SR";
589  std::string ortho = "SVQB";
590  bool relConvTol = false;
591  int maxIterations = 1000;
592  bool isHermitian = true;
593  bool relLockTol = false;
594  bool lock = false;
595  bool useFullOrtho = true;
596 
597  // Information to output in a verbose run
598  int numfailed = 0;
599  int iter = 0;
600  double solvetime = 0;
601 
602  // Get the user values for the parameters
603  const Teuchos::ParameterEntry *pe;
604 
605  pe = params_->getEntryPtr("sphynx_maxiterations");
606  if (pe)
607  maxIterations = pe->getValue<int>(&maxIterations);
608 
609  pe = params_->getEntryPtr("sphynx_use_full_ortho");
610  if (pe)
611  useFullOrtho = pe->getValue<bool>(&useFullOrtho);
612 
613 
614  // Set Anasazi verbosity level
615  int anasaziVerbosity = Anasazi::Errors + Anasazi::Warnings;
616  if (verbosity_ >= 1) // low
617  anasaziVerbosity += Anasazi::FinalSummary + Anasazi::TimingDetails;
618  if (verbosity_ >= 2) // medium
619  anasaziVerbosity += Anasazi::IterationDetails;
620  if (verbosity_ >= 3) // high
621  anasaziVerbosity += Anasazi::StatusTestDetails
622  + Anasazi::OrthoDetails
623  + Anasazi::Debug;
624 
625 
626  // Create the parameter list to pass into solver
627  Teuchos::ParameterList anasaziParams;
628  anasaziParams.set("Verbosity", anasaziVerbosity);
629  anasaziParams.set("Which", which);
630  anasaziParams.set("Convergence Tolerance", tolerance_);
631  anasaziParams.set("Maximum Iterations", maxIterations);
632  anasaziParams.set("Block Size", numEigenVectors);
633  anasaziParams.set("Relative Convergence Tolerance", relConvTol);
634  anasaziParams.set("Orthogonalization", ortho);
635  anasaziParams.set("Use Locking", lock);
636  anasaziParams.set("Relative Locking Tolerance", relLockTol);
637  anasaziParams.set("Full Ortho", useFullOrtho);
638 
639  // Create and set initial vectors
640  RCP<mvector_t> ivec( new mvector_t(laplacian_->getRangeMap(), numEigenVectors));
641 
642  if (randomInit_) {
643 
644  // 0-th vector constant 1, rest random
645  Anasazi::MultiVecTraits<scalar_t, mvector_t>::MvRandom(*ivec);
646  ivec->getVectorNonConst(0)->putScalar(1.);
647 
648  }
649  else { // This implies we will use constant initial vectors.
650 
651  // 0-th vector constant 1, other vectors constant per block
652  // Create numEigenVectors blocks, but only use numEigenVectors-1 of them.
653  // This assures orthogonality.
654  ivec->getVectorNonConst(0)->putScalar(1.);
655  for (int j = 1; j < numEigenVectors; j++)
656  ivec->getVectorNonConst(j)->putScalar(0.);
657 
658  auto map = laplacian_->getRangeMap();
659  gno_t blockSize = map->getGlobalNumElements() / numEigenVectors;
660  TEUCHOS_TEST_FOR_EXCEPTION(blockSize <= 0, std::runtime_error, "Blocksize too small for \"constants\" initial guess. Try \"random\".");
661 
662  for (size_t lid = 0; lid < ivec->getLocalLength(); lid++) {
663  gno_t gid = map->getGlobalElement(lid);
664  for (int j = 1; j < numEigenVectors; j++){
665  if (((j-1)*blockSize <= gid) && (j*blockSize > gid))
666  ivec->replaceLocalValue(lid,j,1.);
667  }
668  }
669  }
670 
671  // Create the eigenproblem to be solved
672  using problem_t = Anasazi::BasicEigenproblem<scalar_t, mvector_t, op_t>;
673  Teuchos::RCP<problem_t> problem (new problem_t(laplacian_, ivec));
674  problem->setHermitian(isHermitian);
675  problem->setNEV(numEigenVectors);
676 
677 
678  // Set preconditioner
679  Sphynx::setPreconditioner(problem);
680 
681  if(problemType_ == Sphynx::GENERALIZED)
682  problem->setM(degMatrix_);
683 
684  // Inform the eigenproblem that you are finished passing it information
685  bool boolret = problem->setProblem();
686  if (boolret != true) {
687  throw std::runtime_error("\nAnasazi::BasicEigenproblem::setProblem() returned with error.\n");
688  }
689 
690  // Set LOBPCG
691  using solver_t = Anasazi::LOBPCGSolMgr<scalar_t, mvector_t, op_t>;
692  solver_t solver(problem, anasaziParams);
693 
694  if (verbosity_ > 0 && comm_->getRank() == 0)
695  anasaziParams.print(std::cout);
696 
697  // Solve the problem
698  if (verbosity_ > 0 && comm_->getRank() == 0)
699  std::cout << "Beginning the LOBPCG solve..." << std::endl;
700  Anasazi::ReturnType returnCode = solver.solve();
701 
702  // Check convergence, niters, and solvetime
703  if (returnCode != Anasazi::Converged) {
704  ++numfailed;
705  }
706  iter = solver.getNumIters();
707  solvetime = (solver.getTimers()[0])->totalElapsedTime();
708 
709 
710  // Retrieve the solution
711  using solution_t = Anasazi::Eigensolution<scalar_t, mvector_t>;
712  solution_t sol = problem->getSolution();
713  eigenVectors_ = sol.Evecs;
714  int numev = sol.numVecs;
715 
716  // Summarize iteration counts and solve time
717  if (verbosity_ > 0 && comm_->getRank() == 0) {
718  std::cout << std::endl;
719  std::cout << "LOBPCG SUMMARY" << std::endl;
720  std::cout << "Failed to converge: " << numfailed << std::endl;
721  std::cout << "No of iterations : " << iter << std::endl;
722  std::cout << "Solve time: " << solvetime << std::endl;
723  std::cout << "No of comp. vecs. : " << numev << std::endl;
724  }
725 
726  return numev;
727 
728  }
729 
730 
731 
733  template <typename Adapter>
734  template <typename problem_t>
735  void Sphynx<Adapter>::setPreconditioner(Teuchos::RCP<problem_t> &problem)
736  {
737 
738  // Set the preconditioner
739  if(precType_ == "muelu") {
741  }
742  else if(precType_ == "polynomial") {
744  }
745  else if(precType_ == "jacobi") {
747  }
748  // else: do we want a case where no preconditioning is applied?
749  }
750 
752  template <typename Adapter>
753  template <typename problem_t>
754  void Sphynx<Adapter>::setMueLuPreconditioner(Teuchos::RCP<problem_t> &problem)
755  {
756 
757 #ifdef HAVE_ZOLTAN2SPHYNX_MUELU
758  Teuchos::ParameterList paramList;
759  if(verbosity_ == 0)
760  paramList.set("verbosity", "none");
761  else if(verbosity_ == 1)
762  paramList.set("verbosity", "low");
763  else if(verbosity_ == 2)
764  paramList.set("verbosity", "medium");
765  else if(verbosity_ == 3)
766  paramList.set("verbosity", "high");
767  else if(verbosity_ >= 4)
768  paramList.set("verbosity", "extreme");
769 
770  paramList.set("smoother: type", "CHEBYSHEV");
771  Teuchos::ParameterList smootherParamList;
772  smootherParamList.set("chebyshev: degree", 3);
773  smootherParamList.set("chebyshev: ratio eigenvalue", 7.0);
774  smootherParamList.set("chebyshev: eigenvalue max iterations", irregular_ ? 100 : 10);
775  paramList.set("smoother: params", smootherParamList);
776  paramList.set("use kokkos refactor", true);
777  paramList.set("transpose: use implicit", true);
778 
779  if(irregular_) {
780 
781  paramList.set("multigrid algorithm", "unsmoothed");
782 
783  paramList.set("coarse: type", "CHEBYSHEV");
784  Teuchos::ParameterList coarseParamList;
785  coarseParamList.set("chebyshev: degree", 3);
786  coarseParamList.set("chebyshev: ratio eigenvalue", 7.0);
787  paramList.set("coarse: params", coarseParamList);
788 
789  paramList.set("max levels", 5);
790  paramList.set("aggregation: drop tol", 0.40);
791 
792  }
793 
794  using prec_t = MueLu::TpetraOperator<scalar_t, lno_t, gno_t, node_t>;
795  Teuchos::RCP<prec_t> prec = MueLu::CreateTpetraPreconditioner<
796  scalar_t, lno_t, gno_t, node_t>(laplacian_, paramList);
797 
798  problem->setPrec(prec);
799 
800 #else
801  throw std::runtime_error("\nSphynx Error: MueLu requested but not compiled into Trilinos.\n");
802 #endif
803 
804  }
805 
807  template <typename Adapter>
808  template <typename problem_t>
809  void Sphynx<Adapter>::setPolynomialPreconditioner(Teuchos::RCP<problem_t> &problem)
810  {
811  int verbosity2 = Belos::Errors;
812  if(verbosity_ == 1)
813  verbosity2 += Belos::Warnings;
814  else if(verbosity_ == 2)
815  verbosity2 += Belos::Warnings + Belos::FinalSummary;
816  else if(verbosity_ == 3)
817  verbosity2 += Belos::Warnings + Belos::FinalSummary + Belos::TimingDetails;
818  else if(verbosity_ >= 4)
819  verbosity2 += Belos::Warnings + Belos::FinalSummary + Belos::TimingDetails
820  + Belos::StatusTestDetails;
821 
822  Teuchos::ParameterList paramList;
823  paramList.set("Polynomial Type", "Roots");
824  paramList.set("Orthogonalization","ICGS");
825  paramList.set("Maximum Degree", laplacian_->getGlobalNumRows() > 100 ? 25 : 5);
826  paramList.set("Polynomial Tolerance", 1.0e-6 );
827  paramList.set("Verbosity", verbosity2 );
828  paramList.set("Random RHS", false );
829  paramList.set("Outer Solver", "");
830  paramList.set("Timer Label", "Belos Polynomial Solve" );
831 
832  // Construct a linear problem for the polynomial solver manager
833  using lproblem_t = Belos::LinearProblem<scalar_t, mvector_t, op_t>;
834  Teuchos::RCP<lproblem_t> innerPolyProblem(new lproblem_t());
835  innerPolyProblem->setOperator(laplacian_);
836 
837  using btop_t = Belos::TpetraOperator<scalar_t>;
838  Teuchos::RCP<btop_t> polySolver(new btop_t(innerPolyProblem,
839  Teuchos::rcpFromRef(paramList),
840  "GmresPoly", true));
841  problem->setPrec(polySolver);
842  }
843 
845  template <typename Adapter>
846  template <typename problem_t>
847  void Sphynx<Adapter>::setJacobiPreconditioner(Teuchos::RCP<problem_t> &problem)
848  {
849 
850  Teuchos::RCP<Ifpack2::Preconditioner<scalar_t, lno_t, gno_t, node_t>> prec;
851  std::string precType = "RELAXATION";
852 
853  prec = Ifpack2::Factory::create<matrix_t> (precType, laplacian_);
854 
855  Teuchos::ParameterList precParams;
856  precParams.set("relaxation: type", "Jacobi");
857  precParams.set("relaxation: fix tiny diagonal entries", true);
858  precParams.set("relaxation: min diagonal value", 1.0e-8);
859 
860  prec->setParameters(precParams);
861  prec->initialize();
862  prec->compute();
863 
864  problem->setPrec(prec);
865 
866  }
867 
869  // Transform the computed eigenvectors into coordinates
870  template <typename Adapter>
871  void Sphynx<Adapter>::eigenvecsToCoords(Teuchos::RCP<mvector_t> &eigenVectors,
872  int computedNumEv,
873  Teuchos::RCP<mvector_t> &coordinates)
874  {
875  // Extract the meaningful eigenvectors by getting rid of the first one
876  Teuchos::Array<size_t> columns (computedNumEv-1);
877  for (int j = 0; j < computedNumEv-1; ++j) {
878  columns[j] = j+1;
879  }
880  coordinates = eigenVectors->subCopy (columns());
881  coordinates->setCopyOrView (Teuchos::View);
882 
883  }
884 
885 
887  // If user provided some weights, use them by getting them from the adapter.
888  // If user didn't provide weights but told us to use degree as weight, do so.
889  // If user neither provided weights nor told us what to do, use degree as weight.
890  template <typename Adapter>
891  void Sphynx<Adapter>::computeWeights(std::vector<const weight_t *> vecweights,
892  std::vector<int> strides)
893  {
894 
895  int numWeights = adapter_->getNumWeightsPerVertex();
896  int numConstraints = numWeights > 0 ? numWeights : 1;
897 
898  size_t myNumVertices = adapter_->getLocalNumVertices();
899  weight_t ** weights = new weight_t*[numConstraints];
900  for(int j = 0; j < numConstraints; j++)
901  weights[j] = new weight_t[myNumVertices];
902 
903  // Will be needed if we use degree as weight
904  const offset_t *offset;
905  const gno_t *columnId;
906 
907  // If user hasn't set any weights, use vertex degrees as weights
908  if(numWeights == 0) {
909 
910  // Compute the weight of vertex i as the number of nonzeros in row i.
911  adapter_->getEdgesView(offset, columnId);
912  for (size_t i = 0; i < myNumVertices; i++)
913  weights[0][i] = offset[i+1] - offset[i] - 1;
914 
915  vecweights.push_back(weights[0]);
916  strides.push_back(1);
917  }
918  else {
919 
920  // Use the weights if there are any already set in the adapter
921  for(int j = 0; j < numConstraints; j++) {
922 
923  if(adapter_->useDegreeAsVertexWeight(j)) {
924  // Compute the weight of vertex i as the number of nonzeros in row i.
925  adapter_->getEdgesView(offset, columnId);
926  for (size_t i = 0; i < myNumVertices; i++)
927  weights[j][i] = offset[i+1] - offset[i];
928  }
929  else{
930  int stride;
931  const weight_t *wgt = NULL;
932  adapter_->getVertexWeightsView(wgt, stride, j);
933 
934  for (size_t i = 0; i < myNumVertices; i++)
935  weights[j][i] = wgt[i];
936  }
937 
938  vecweights.push_back(weights[j]);
939  strides.push_back(1);
940 
941  }
942  }
943 
944  }
945 
946 
948  // Compute a partition by calling MJ on given coordinates with given weights
949  template <typename Adapter>
950  void Sphynx<Adapter>::MJwrapper(const Teuchos::RCP<const mvector_t> &coordinates,
951  std::vector<const weight_t *> weights,
952  std::vector<int> strides,
953  const Teuchos::RCP<PartitioningSolution<Adapter>> &solution)
954  {
955 
956  Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("Sphynx::MJ"));
957 
958  using mvector_adapter_t = Zoltan2::XpetraMultiVectorAdapter<mvector_t>;
963 
964 
965  size_t myNumVertices = coordinates->getLocalLength();
966 
967  // Create the base adapter for the multivector adapter
968  Teuchos::RCP<mvector_adapter_t> adapcoordinates(new mvector_adapter_t(coordinates,
969  weights,
970  strides));
971  Teuchos::RCP<const base_adapter_t> baseAdapter =
972  Teuchos::rcp(dynamic_cast<const base_adapter_t *>(adapcoordinates.get()), false);
973 
974  // Create the coordinate model using the base multivector adapter
975  Zoltan2::modelFlag_t flags;
976  Teuchos::RCP<const cmodel_t> coordModel (new cmodel_t(baseAdapter, env_, comm_, flags));
977 
978  // Create the MJ object
979  Teuchos::RCP<const Comm<int>> comm2 = comm_;
980  Teuchos::RCP<mj_t> mj(new mj_t(env_, comm2, coordModel));
981 
982  // Partition with MJ
983  Teuchos::RCP<solution_t> vectorsolution( new solution_t(env_, comm2, 1, mj));
984  mj->partition(vectorsolution);
985 
986  // Transform the solution
987  Teuchos::ArrayRCP<part_t> parts(myNumVertices);
988  for(size_t i = 0; i < myNumVertices; i++) {
989  parts[i] = vectorsolution->getPartListView()[i];
990  }
991  solution->setParts(parts);
992 
993  }
994 
995 } // namespace Zoltan2
996 
997 #endif
Contains the Multi-jagged algorthm.
Defines the CoordinateModel classes.
Defines XpetraCrsGraphAdapter class.
Defines the XpetraMultiVectorAdapter.
Algorithm defines the base class for all algorithms.
Adapter::part_t part_t
Adapter::scalar_t scalar_t
This class provides geometric coordinates with optional weights to the Zoltan2 algorithm.
A PartitioningSolution is a solution to a partitioning problem.
void setMueLuPreconditioner(Teuchos::RCP< problem_t > &problem)
typename Adapter::offset_t offset_t
int LOBPCGwrapper(const int numEigenVectors)
Tpetra::CrsGraph< lno_t, gno_t, node_t > graph_t
void partition(const RCP< PartitioningSolution< Adapter > > &solution)
Partitioning method.
Teuchos::RCP< matrix_t > computeNormalizedLaplacian()
void eigenvecsToCoords(Teuchos::RCP< mvector_t > &eigenVectors, int computedNumEv, Teuchos::RCP< mvector_t > &coordinates)
Tpetra::MultiVector< scalar_t, lno_t, gno_t, node_t > mvector_t
typename Adapter::node_t node_t
void MJwrapper(const Teuchos::RCP< const mvector_t > &coordinates, std::vector< const weight_t * > weights, std::vector< int > strides, const Teuchos::RCP< PartitioningSolution< Adapter >> &solution)
Tpetra::Operator< scalar_t, lno_t, gno_t, node_t > op_t
Teuchos::RCP< matrix_t > computeCombinatorialLaplacian()
typename Adapter::scalar_t weight_t
void setPreconditioner(Teuchos::RCP< problem_t > &problem)
void setPolynomialPreconditioner(Teuchos::RCP< problem_t > &problem)
void setJacobiPreconditioner(Teuchos::RCP< problem_t > &problem)
Tpetra::CrsMatrix< scalar_t, lno_t, gno_t, node_t > matrix_t
Sphynx(const RCP< const Environment > &env, const RCP< Teuchos::ParameterList > &params, const RCP< const Comm< int > > &comm, const RCP< const XpetraCrsGraphAdapter< graph_t > > &adapter)
void computeWeights(std::vector< const weight_t * > vecweights, std::vector< int > strides)
Provides access for Zoltan2 to Xpetra::CrsGraph data.
Multi Jagged coordinate partitioning algorithm.
map_t::local_ordinal_type lno_t
Definition: mapRemotes.cpp:17
map_t::global_ordinal_type gno_t
Definition: mapRemotes.cpp:18
Zoltan2::BaseAdapter< userTypes_t > base_adapter_t
Created by mbenlioglu on Aug 31, 2020.
std::bitset< NUM_MODEL_FLAGS > modelFlag_t
static RCP< tMVector_t > coordinates
static ArrayRCP< ArrayRCP< zscalar_t > > weights
SparseMatrixAdapter_t::part_t part_t