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
97namespace 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
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;
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
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
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)
Teuchos::RCP< matrix_t > computeNormalizedLaplacian()
typename Adapter::offset_t offset_t
int LOBPCGwrapper(const int numEigenVectors)
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::CrsGraph< lno_t, gno_t, node_t > graph_t
void partition(const RCP< PartitioningSolution< Adapter > > &solution)
Partitioning method.
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
Tpetra::Operator< scalar_t, lno_t, gno_t, node_t > op_t
typename Adapter::scalar_t weight_t
Teuchos::RCP< matrix_t > computeCombinatorialLaplacian()
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