Zoltan2
Loading...
Searching...
No Matches
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>;
959 using base_adapter_t = typename mvector_adapter_t::base_adapter_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.
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::lno_t lno_t
typename Adapter::scalar_t weight_t
Teuchos::RCP< matrix_t > computeCombinatorialLaplacian()
typename Adapter::gno_t gno_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
typename Adapter::part_t part_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
map_t::global_ordinal_type gno_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