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// (Alternately, uses the experimental Randomized eigensolver.)
71// Step3: Sphynx calls the MJ algorithm provided in Zoltan2Core to compute the
72// partition.
74
75#include "Zoltan2Sphynx_config.h"
76
79
82
83#include "AnasaziLOBPCGSolMgr.hpp"
84//#include "AnasaziRandomizedSolMgr.hpp" //TODO: Uncomment when randomized solver available.
85#include "AnasaziBasicEigenproblem.hpp"
86#include "AnasaziTpetraAdapter.hpp"
87
88#include "BelosLinearProblem.hpp"
89#include "BelosTpetraOperator.hpp"
90
91#include "Ifpack2_Factory.hpp"
92
93#include "Teuchos_TimeMonitor.hpp"
94
95#ifdef HAVE_ZOLTAN2SPHYNX_MUELU
96#include "MueLu_CreateTpetraPreconditioner.hpp"
97#endif
98
99namespace Zoltan2 {
100
101 template <typename Adapter>
102 class Sphynx : public Algorithm<Adapter>
103 {
104
105 public:
106
107 using scalar_t = double; // Sphynx with scalar_t=double obtains better cutsize
108 using lno_t = typename Adapter::lno_t;
109 using gno_t = typename Adapter::gno_t;
110 using node_t = typename Adapter::node_t;
111 using offset_t = typename Adapter::offset_t;
112 using part_t = typename Adapter::part_t;
113 using weight_t = typename Adapter::scalar_t;
114
115 using graph_t = Tpetra::CrsGraph<lno_t, gno_t, node_t>;
116 using matrix_t = Tpetra::CrsMatrix<scalar_t, lno_t, gno_t, node_t>;
117 using mvector_t = Tpetra::MultiVector<scalar_t, lno_t, gno_t, node_t>;
118 using op_t = Tpetra::Operator<scalar_t, lno_t, gno_t, node_t>;
119
121
125
126 // Takes the graph from the input adapter and computes the Laplacian matrix
127 Sphynx(const RCP<const Environment> &env,
128 const RCP<Teuchos::ParameterList> &params,
129 const RCP<Teuchos::ParameterList> &sphynxParams,
130 const RCP<const Comm<int> > &comm,
131 const RCP<const XpetraCrsGraphAdapter<graph_t> > &adapter) :
132 env_(env), params_(params), sphynxParams_(sphynxParams), comm_(comm), adapter_(adapter)
133 {
134
135 // Don't compute the Laplacian if the number of requested parts is 1
136 const Teuchos::ParameterEntry *pe = params_->getEntryPtr("num_global_parts");
137 numGlobalParts_ = pe->getValue<int>(&numGlobalParts_);
138 if(numGlobalParts_ > 1){
139
140 Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("Sphynx::Laplacian"));
141
142 // The verbosity is common throughout the algorithm, better to check and set now
143 pe = sphynxParams_->getEntryPtr("sphynx_verbosity");
144 if (pe)
145 verbosity_ = pe->getValue<int>(&verbosity_);
146
147 // Do we need to pre-process the input?
148 pe = sphynxParams_->getEntryPtr("sphynx_skip_preprocessing");
149 if (pe)
150 skipPrep_ = pe->getValue<bool>(&skipPrep_);
151
152 // Get the graph from XpetraCrsGraphAdapter if skipPrep_ is true
153 // We assume the graph has all of the symmetric and diagonal entries
154 if(skipPrep_)
155 graph_ = adapter_->getUserGraph();
156 else {
157 throw std::runtime_error("\nSphynx Error: Preprocessing has not been implemented yet.\n");
158 }
159
160 // Check if the graph is regular
162
163 // Set Sphynx defaults: preconditioner, problem type, tolerance, initial vectors.
164 setDefaults();
165
166 // Compute the Laplacian matrix
168
169 if(problemType_ == GENERALIZED)
171
172 }
173 }
174
178
179 void partition(const Teuchos::RCP<PartitioningSolution<Adapter> > &solution);
180
181 int AnasaziWrapper(const int numEigenVectors);
182
183 template<typename problem_t>
184 void setPreconditioner(Teuchos::RCP<problem_t> &problem);
185
186 template<typename problem_t>
187 void setMueLuPreconditioner(Teuchos::RCP<problem_t> &problem);
188
189 template<typename problem_t>
190 void setJacobiPreconditioner(Teuchos::RCP<problem_t> &problem);
191
192 template<typename problem_t>
193 void setPolynomialPreconditioner(Teuchos::RCP<problem_t> &problem);
194
195 void eigenvecsToCoords(Teuchos::RCP<mvector_t> &eigenVectors,
196 int computedNumEv,
197 Teuchos::RCP<mvector_t> &coordinates);
198
199 void computeWeights(std::vector<const weight_t *> vecweights,
200 std::vector<int> strides);
201
202 void MJwrapper(const Teuchos::RCP<const mvector_t> &coordinates,
203 std::vector<const weight_t *> weights,
204 std::vector<int> strides,
205 const Teuchos::RCP<PartitioningSolution<Adapter>> &solution);
206
207 void setUserEigenvectors(const Teuchos::RCP<mvector_t> &userEvects);
208
209 Teuchos::RCP<mvector_t> getSphynxEigenvectors();
213
215 // Determine input graph's regularity = maxDegree/AvgDegree < 10.
216 // If Laplacian type is not specified, then use combinatorial for regular
217 // and generalized for irregular.
218 // MueLu settings will differ depending on the regularity, too.
220 {
221 // Get the row pointers in the host
222 auto rowOffsets = graph_->getLocalGraphDevice().row_map;
223 auto rowOffsets_h = Kokkos::create_mirror_view(rowOffsets);
224 Kokkos::deep_copy(rowOffsets_h, rowOffsets);
225
226 // Get size information
227 const size_t numGlobalEntries = graph_->getGlobalNumEntries();
228 const size_t numLocalRows = graph_->getLocalNumRows();
229 const size_t numGlobalRows = graph_->getGlobalNumRows();
230
231 // Compute local maximum degree
232 size_t localMax = 0;
233 for(size_t i = 0; i < numLocalRows; i++){
234 if(rowOffsets_h(i+1) - rowOffsets_h(i) - 1 > localMax)
235 localMax = rowOffsets_h(i+1) - rowOffsets_h(i) - 1;
236 }
237
238 // Compute global maximum degree
239 size_t globalMax = 0;
240 Teuchos::reduceAll<int, size_t> (*comm_, Teuchos::REDUCE_MAX, 1, &localMax, &globalMax);
241
242 double avg = static_cast<double>(numGlobalEntries-numGlobalRows)/numGlobalRows;
243 double maxOverAvg = static_cast<double>(globalMax)/avg;
244
245 // Use generalized Laplacian on irregular graphs
246 irregular_ = false;
247 if(maxOverAvg > 10) {
248 irregular_ = true;
249 }
250
251 // Let the user know about what we determined if verbose
252 if(verbosity_ > 0) {
253 if(comm_->getRank() == 0) {
254 std::cout << "Determining Regularity -- max degree: " << globalMax
255 << ", avg degree: " << avg << ", max/avg: " << globalMax/avg << std::endl
256 << "Determined Regularity -- regular: " << !irregular_ << std::endl;
257
258 }
259 }
260 }
261
263 // If preconditioner type is not specified:
264 // use muelu if it is enabled, and jacobi otherwise.
265 // If eigenvalue problem type is not specified:
266 // use combinatorial for regular and
267 // normalized for irregular with polynomial preconditioner,
268 // generalized for irregular with other preconditioners.
269 // If convergence tolerance is not specified:
270 // use 1e-3 for regular with jacobi and polynomial, and 1e-2 otherwise.
271 // If how to decide the initial vectors is not specified:
272 // use random for regular and constant for irregular
274 {
275
276 // Set the default preconditioner to muelu if it is enabled, jacobi otherwise.
277 precType_ = "jacobi";
278#ifdef HAVE_ZOLTAN2SPHYNX_MUELU
279 precType_ = "muelu";
280#endif
281
282 // Override the preconditioner type with the user's preference
283 const Teuchos::ParameterEntry *pe = sphynxParams_->getEntryPtr("sphynx_preconditioner_type");
284 if (pe) {
285 precType_ = pe->getValue<std::string>(&precType_);
286 if(precType_ != "muelu" && precType_ != "jacobi" && precType_ != "polynomial")
287 throw std::runtime_error("\nSphynx Error: " + precType_ + " is not recognized as a preconditioner.\n"
288 + " Possible values: muelu (if enabled), jacobi, and polynomial\n");
289 }
290
291 solverType_ = sphynxParams_->get("sphynx_eigensolver","LOBPCG");
292 TEUCHOS_TEST_FOR_EXCEPTION(!(solverType_ == "LOBPCG" || solverType_ == "randomized"),
293 std::invalid_argument, "Sphynx: sphynx_eigensolver must be set to LOBPCG or randomized.");
294
295 // Set the default problem type
296 problemType_ = COMBINATORIAL;
297 if(irregular_) {
298 if(precType_ == "polynomial")
299 problemType_ = NORMALIZED;
300 else
301 problemType_ = GENERALIZED;
302 }
303
304 // Override the problem type with the user's preference
305 pe = sphynxParams_->getEntryPtr("sphynx_problem_type");
306 if (pe) {
307 std::string probType = "";
308 probType = pe->getValue<std::string>(&probType);
309
310 if(probType == "combinatorial")
311 problemType_ = COMBINATORIAL;
312 else if(probType == "generalized")
313 problemType_ = GENERALIZED;
314 else if(probType == "normalized")
315 problemType_ = NORMALIZED;
316 else
317 throw std::runtime_error("\nSphynx Error: " + probType + " is not recognized as a problem type.\n"
318 + " Possible values: combinatorial, generalized, and normalized.\n");
319 }
320
321
322 // Set the default for tolerance
323 tolerance_ = 1.0e-2;
324 if(!irregular_ && (precType_ == "jacobi" || precType_ == "polynomial"))
325 tolerance_ = 1.0e-3;
326
327
328 // Override the tolerance with the user's preference
329 pe = sphynxParams_->getEntryPtr("sphynx_tolerance");
330 if (pe)
331 tolerance_ = pe->getValue<scalar_t>(&tolerance_);
332
333
334 // Set the default for initial vectors
335 randomInit_ = true;
336 if(irregular_)
337 randomInit_ = false;
338
339 // Override the initialization method with the user's preference
340 pe = sphynxParams_->getEntryPtr("sphynx_initial_guess");
341 if (pe) {
342 std::string initialGuess = "";
343 initialGuess = pe->getValue<std::string>(&initialGuess);
344
345 if(initialGuess == "random")
346 randomInit_ = true;
347 else if(initialGuess == "constants")
348 randomInit_ = false;
349 else
350 throw std::runtime_error("\nSphynx Error: " + initialGuess + " is not recognized as an option for initial guess.\n"
351 + " Possible values: random and constants.\n");
352 }
353
354 }
355
357 // Compute the Laplacian matrix depending on the eigenvalue problem type.
358 // There are 3 options for the type: combinatorial, generalized, and normalized.
359 // Combinatorial and generalized share the same Laplacian but generalized
360 // also needs a degree matrix.
362 {
363 if(solverType_ == "randomized")
364 laplacian_ = computeNormalizedLaplacian(true);
365 else if(problemType_ == NORMALIZED)
366 laplacian_ = computeNormalizedLaplacian();
367 else
368 laplacian_ = computeCombinatorialLaplacian();
369 }
370
372 // Compute a diagonal matrix with the vertex degrees in the input graph
374 {
375
376 // Get the row pointers in the host
377 auto rowOffsets = graph_->getLocalGraphHost().row_map;
378
379 // Create the degree matrix with max row size set to 1
380 Teuchos::RCP<matrix_t> degMat(new matrix_t (graph_->getRowMap(),
381 graph_->getRowMap(),
382 1));
383
384 scalar_t *val = new scalar_t[1];
385 lno_t *ind = new lno_t[1];
386 lno_t numRows = static_cast<lno_t>(graph_->getLocalNumRows());
387
388 // Insert the diagonal entries as the degrees
389 for (lno_t i = 0; i < numRows; ++i) {
390 val[0] = rowOffsets(i+1) - rowOffsets(i) - 1;
391 ind[0] = i;
392 degMat->insertLocalValues(i, 1, val, ind);
393 }
394 delete [] val;
395 delete [] ind;
396
397 degMat->fillComplete(graph_->getDomainMap(), graph_->getRangeMap());
398
399 degMatrix_ = degMat;
400 }
401
403 // Compute the combinatorial Laplacian: L = D - A.
404 // l_ij = degree of vertex i if i = j
405 // l_ij = -1 if i != j and a_ij != 0
406 // l_ij = 0 if i != j and a_ij = 0
407 Teuchos::RCP<matrix_t> computeCombinatorialLaplacian()
408 {
409 using range_policy = Kokkos::RangePolicy<
410 typename node_t::device_type::execution_space, Kokkos::IndexType<lno_t>>;
411 using values_view_t = Kokkos::View<scalar_t*, typename node_t::device_type>;
412 using offset_view_t = Kokkos::View<size_t*, typename node_t::device_type>;
413
414 const size_t numEnt = graph_->getLocalNumEntries();
415 const size_t numRows = graph_->getLocalNumRows();
416
417 // Create new values for the Laplacian, initialize to -1
418 values_view_t newVal (Kokkos::view_alloc("CombLapl::val", Kokkos::WithoutInitializing), numEnt);
419 Kokkos::deep_copy(newVal, -1);
420
421 // Get the diagonal offsets
422 offset_view_t diagOffsets(Kokkos::view_alloc("Diag Offsets", Kokkos::WithoutInitializing), numRows);
423 graph_->getLocalDiagOffsets(diagOffsets);
424
425 // Get the row pointers in the host
426 auto rowOffsets = graph_->getLocalGraphDevice().row_map;
427
428 // Compute the diagonal entries as the vertex degrees
429 Kokkos::parallel_for("Combinatorial Laplacian", range_policy(0, numRows),
430 KOKKOS_LAMBDA(const lno_t i){
431 newVal(rowOffsets(i) + diagOffsets(i)) = rowOffsets(i+1) - rowOffsets(i) - 1;
432 }
433 );
434 Kokkos::fence ();
435
436 // Create the Laplacian matrix using the input graph and with the new values
437 Teuchos::RCP<matrix_t> laplacian (new matrix_t(graph_, newVal));
438 laplacian->fillComplete (graph_->getDomainMap(), graph_->getRangeMap());
439
440 return laplacian;
441 }
442
444 // For AHat = false:
445 // Compute the normalized Laplacian: L_N = D^{-1/2} L D^{-1/2}, where L = D - A.
446 // l_ij = 1
447 // l_ij = -1/(sqrt(deg(v_i))sqrt(deg(v_j)) if i != j and a_ij != 0
448 // l_ij = 0 if i != j and a_ij = 0
449 //
450 // For AHat = true:
451 // AHat is turned to true if (and only if) using the randomized Eigensolver.
452 // For the randomized Eigensolver, we find eigenvalues of the matrix
453 // AHat = 2*I - L_N, and this is the matrix computed by this function.
454 Teuchos::RCP<matrix_t> computeNormalizedLaplacian(bool AHat = false)
455 {
456 using range_policy = Kokkos::RangePolicy<
457 typename node_t::device_type::execution_space, Kokkos::IndexType<lno_t>>;
458 using values_view_t = Kokkos::View<scalar_t*, typename node_t::device_type>;
459 using offset_view_t = Kokkos::View<size_t*, typename node_t::device_type>;
460 using vector_t = Tpetra::Vector<scalar_t, lno_t, gno_t, node_t>;
461 using dual_view_t = typename vector_t::dual_view_type;
462 using KAT = Kokkos::ArithTraits<scalar_t>;
463
464 const size_t numEnt = graph_->getLocalNumEntries();
465 const size_t numRows = graph_->getLocalNumRows();
466
467 // Create new values for the Laplacian, initialize to -1
468 values_view_t newVal (Kokkos::view_alloc("NormLapl::val", Kokkos::WithoutInitializing), numEnt);
469 if(AHat){
470 Kokkos::deep_copy(newVal, 1);
471 }
472 else{
473 Kokkos::deep_copy(newVal, -1);
474 }
475
476 // D^{-1/2}
477 dual_view_t dv ("MV::DualView", numRows, 1);
478 auto deginvsqrt = dv.d_view;
479
480 // Get the diagonal offsets
481 offset_view_t diagOffsets(Kokkos::view_alloc("Diag Offsets", Kokkos::WithoutInitializing), numRows);
482 graph_->getLocalDiagOffsets(diagOffsets);
483
484 // Get the row pointers
485 auto rowOffsets = graph_->getLocalGraphDevice().row_map;
486
487 if(!AHat){
488 // Compute the diagonal entries as the vertex degrees
489 Kokkos::parallel_for("Combinatorial Laplacian", range_policy(0, numRows),
490 KOKKOS_LAMBDA(const lno_t i){
491 newVal(rowOffsets(i) + diagOffsets(i)) = rowOffsets(i+1) - rowOffsets(i) - 1;
492 deginvsqrt(i,0) = 1.0/KAT::sqrt(rowOffsets(i+1) - rowOffsets(i) - 1);
493 }
494 );
495 Kokkos::fence ();
496 }
497 else{
498 // Put 0's on the diagonal of A plus shift by +I -> 1's on diagonal
499 Kokkos::parallel_for("Zero Diagonal", range_policy(0, numRows),
500 KOKKOS_LAMBDA(const lno_t i){
501 newVal(rowOffsets(i) + diagOffsets(i)) = 1.0;
502 deginvsqrt(i,0) = 1.0/KAT::sqrt(rowOffsets(i+1) - rowOffsets(i) - 1);
503 }
504 );
505 Kokkos::fence ();
506 }
507
508 // Create the Laplacian graph using the same graph structure with the new values
509 Teuchos::RCP<matrix_t> laplacian (new matrix_t(graph_, newVal));
510 laplacian->fillComplete (graph_->getDomainMap(), graph_->getRangeMap());
511
512 // Create the vector for D^{-1/2}
513 vector_t degInvSqrt(graph_->getRowMap(), dv);
514
515 // Normalize the laplacian matrix as D^{-1/2} L D^{-1/2}
516 laplacian->leftScale(degInvSqrt);
517 laplacian->rightScale(degInvSqrt);
518
519 return laplacian;
520 }
521
525
526 private:
527
528 // User-provided members
529 const Teuchos::RCP<const Environment> env_;
530 Teuchos::RCP<Teuchos::ParameterList> params_;
531 Teuchos::RCP<Teuchos::ParameterList> sphynxParams_;
532 const Teuchos::RCP<const Teuchos::Comm<int> > comm_;
533 const Teuchos::RCP<const Adapter> adapter_;
534
535 // Internal members
536 int numGlobalParts_;
537 Teuchos::RCP<const graph_t> graph_;
538 Teuchos::RCP<matrix_t> laplacian_;
539 Teuchos::RCP<const matrix_t> degMatrix_;
540 Teuchos::RCP<mvector_t> eigenVectors_;
541
542 bool irregular_; // decided internally
543 std::string precType_; // obtained from user params or decided internally
544 std::string solverType_; // obtained from user params or decided internally
545 problemType problemType_; // obtained from user params or decided internally
546 double tolerance_; // obtained from user params or decided internally
547 bool randomInit_; // obtained from user params or decided internally
548 int verbosity_; // obtained from user params
549 bool skipPrep_; // obtained from user params
550 };
551
552
553
557
559 // Allows the user to manually set eigenvectors for the Sphynx partitioner
560 // to use rather than solving for them with Anasazi. Mainly intended
561 // for debugging purposes.
563 template <typename Adapter>
564 void Sphynx<Adapter>::setUserEigenvectors(const Teuchos::RCP<mvector_t> &userEvects)
565 {
566 eigenVectors_ = userEvects;
567 }
568
570 // Returns an RCP containing a deep copy of the eigenvectors used by Sphynx.
572 template <typename Adapter>
573 Teuchos::RCP<Tpetra::MultiVector<double, typename Adapter::lno_t, typename Adapter::gno_t, typename Adapter::node_t> >
575 {
576 return Anasazi::MultiVecTraits<scalar_t, mvector_t>::CloneCopy(eigenVectors_);
577 }
578
580 // Compute a partition using the Laplacian matrix (and possibly the degree
581 // matrix as well). First call LOBPCG (or Randomized solver)
582 // to compute logK+1 eigenvectors, then
583 // transform the eigenvectors to coordinates, and finally call MJ to compute
584 // a partition on the coordinates.
585 template <typename Adapter>
587 {
588 // Return a trivial solution if only one part is requested
589 if(numGlobalParts_ == 1) {
590
591 size_t numRows =adapter_->getUserGraph()->getLocalNumRows();
592 Teuchos::ArrayRCP<part_t> parts(numRows,0);
593 solution->setParts(parts);
594
595 return;
596
597 }
598
599 // The number of eigenvectors to be computed
600 int numEigenVectors = (int) log2(numGlobalParts_)+1;
601 int computedNumEv;
602
603 if(eigenVectors_ == Teuchos::null){
604 // Compute the eigenvectors using LOBPCG
605 // or Randomized eigensolver
606 computedNumEv = Sphynx::AnasaziWrapper(numEigenVectors);
607
608 if(computedNumEv <= 1 && solverType_ == "LOBPCG") {
609 throw
610 std::runtime_error("\nAnasazi Error: LOBPCGSolMgr::solve() returned unconverged.\n"
611 "Sphynx Error: LOBPCG could not compute any eigenvectors.\n"
612 " Increase either max iters or tolerance.\n");
613 }
614 }
615 else{
616 // Use eigenvectors provided by user.
617 computedNumEv = (int) eigenVectors_->getNumVectors();
618 if(computedNumEv <= numEigenVectors) {
619 throw
620 std::runtime_error("\nSphynx Error: Number of eigenvectors given by user\n"
621 " is less than number of Eigenvectors needed for partition." );
622 }
623 }
624
625 // Transform the eigenvectors into coordinates
626 Teuchos::RCP<mvector_t> coordinates;
627
628 Sphynx::eigenvecsToCoords(eigenVectors_, computedNumEv, coordinates);
629
630 // Get the weights from the adapter
631 std::vector<const weight_t *> weights;
632 std::vector<int> wstrides;
634
635
636 // Compute the partition using MJ on coordinates
637 Sphynx::MJwrapper(coordinates, weights, wstrides, solution);
638
639 }
640
642 // Call LOBPCG on the Laplacian matrix.
643 // Or use the randomized eigensolver.
644 template <typename Adapter>
645 int Sphynx<Adapter>::AnasaziWrapper(const int numEigenVectors)
646 {
647
648 Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("Sphynx::Anasazi"));
649
650 // Set defaults for the parameters
651 // and get user-set values.
652 std::string which = (solverType_ == "randomized" ? "LM" : "SR");
653 std::string ortho = "SVQB";
654 bool relConvTol = false;
655 int maxIterations = sphynxParams_->get("sphynx_max_iterations",1000);
656 int blockSize = sphynxParams_->get("sphynx_block_size",numEigenVectors);
657 bool isHermitian = true;
658 bool relLockTol = false;
659 bool lock = false;
660 bool useFullOrtho = sphynxParams_->get("sphynx_use_full_ortho",true);
661 // Information to output in a verbose run
662 int numfailed = 0;
663 int iter = 0;
664 double solvetime = 0;
665
666 // Set Anasazi verbosity level
667 int anasaziVerbosity = Anasazi::Errors + Anasazi::Warnings;
668 if (verbosity_ >= 1) // low
669 anasaziVerbosity += Anasazi::FinalSummary + Anasazi::TimingDetails;
670 if (verbosity_ >= 2) // medium
671 anasaziVerbosity += Anasazi::IterationDetails;
672 if (verbosity_ >= 3) // high
673 anasaziVerbosity += Anasazi::StatusTestDetails + Anasazi::OrthoDetails
674 + Anasazi::Debug;
675
676 // Create the parameter list to pass into solver
677 Teuchos::ParameterList anasaziParams;
678 anasaziParams.set("Verbosity", anasaziVerbosity);
679 anasaziParams.set("Which", which);
680 anasaziParams.set("Convergence Tolerance", tolerance_);
681 anasaziParams.set("Maximum Iterations", maxIterations);
682 anasaziParams.set("Block Size", blockSize);
683 anasaziParams.set("Relative Convergence Tolerance", relConvTol);
684 anasaziParams.set("Orthogonalization", ortho);
685 anasaziParams.set("Use Locking", lock);
686 anasaziParams.set("Relative Locking Tolerance", relLockTol);
687 anasaziParams.set("Full Ortho", useFullOrtho);
688
689 // Create and set initial vectors
690 Teuchos::RCP<mvector_t> ivec( new mvector_t(laplacian_->getRangeMap(), numEigenVectors));
691
692 if (randomInit_) {
693 // 0-th vector constant 1, rest random
694 Anasazi::MultiVecTraits<scalar_t, mvector_t>::MvRandom(*ivec);
695 ivec->getVectorNonConst(0)->putScalar(1.);
696 }
697 else { // This implies we will use constant initial vectors.
698
699 // 0-th vector constant 1, other vectors constant per block
700 // Create numEigenVectors blocks, but only use numEigenVectors-1 of them.
701 // This assures orthogonality.
702 ivec->getVectorNonConst(0)->putScalar(1.);
703 for (int j = 1; j < numEigenVectors; j++)
704 ivec->getVectorNonConst(j)->putScalar(0.);
705
706 auto map = laplacian_->getRangeMap();
707 gno_t blkSize = map->getGlobalNumElements() / numEigenVectors;
708 TEUCHOS_TEST_FOR_EXCEPTION(blkSize <= 0, std::runtime_error, "Blocksize too small for \"constants\" initial guess. Try \"random\".");
709
710 for (size_t lid = 0; lid < ivec->getLocalLength(); lid++) {
711 gno_t gid = map->getGlobalElement(lid);
712 for (int j = 1; j < numEigenVectors; j++){
713 if (((j-1)*blkSize <= gid) && (j*blkSize > gid))
714 ivec->replaceLocalValue(lid,j,1.);
715 }
716 }
717 }
718
719 // Create the eigenproblem to be solved
720 using problem_t = Anasazi::BasicEigenproblem<scalar_t, mvector_t, op_t>;
721 Teuchos::RCP<problem_t> problem (new problem_t(laplacian_, ivec));
722 problem->setHermitian(isHermitian);
723 problem->setNEV(numEigenVectors);
724
725 if(solverType_ == "LOBPCG"){
726
727 // Set preconditioner
729
730 if(problemType_ == Sphynx::GENERALIZED)
731 problem->setM(degMatrix_);
732 }
733
734 // Inform the eigenproblem that you are finished passing it information
735 bool boolret = problem->setProblem();
736 if (boolret != true) {
737 throw std::runtime_error("\nAnasazi::BasicEigenproblem::setProblem() returned with error.\n");
738 }
739 // Set Eigensolver
740 Teuchos::RCP<Anasazi::SolverManager<scalar_t, mvector_t, op_t>> solver;
741
742 if(solverType_ == "LOBPCG"){
743 solver = Teuchos::rcp(new Anasazi::LOBPCGSolMgr<scalar_t, mvector_t, op_t>(problem, anasaziParams));
744 }
745 else{
746 //solver = Teuchos::rcp(new Anasazi::Experimental::RandomizedSolMgr<scalar_t, mvector_t, op_t>(problem, anasaziParams));
747 //TODO: Uncomment when randomized solver available.
748 throw std::runtime_error("\nInvalid solver type.\n");
749 }
750
751 if (verbosity_ > 0 && comm_->getRank() == 0)
752 anasaziParams.print(std::cout);
753
754 // Solve the problem
755 if (verbosity_ > 0 && comm_->getRank() == 0)
756 std::cout << "Beginning the Anasazi solve..." << std::endl;
757 Anasazi::ReturnType returnCode = solver->solve();
758
759 // Check convergence, niters, and solvetime
760 if (returnCode != Anasazi::Converged) {
761 ++numfailed;
762 }
763 iter = solver->getNumIters();
764 solvetime = (solver->getTimers()[0])->totalElapsedTime();
765
766
767 // Retrieve the solution
768 using solution_t = Anasazi::Eigensolution<scalar_t, mvector_t>;
769 solution_t sol = problem->getSolution();
770 eigenVectors_ = sol.Evecs;
771 int numev = sol.numVecs;
772
773 // Summarize iteration counts and solve time
774 if (verbosity_ > 0 && comm_->getRank() == 0) {
775 std::cout << std::endl;
776 std::cout << "ANASAZI SUMMARY" << std::endl;
777 std::cout << "Failed to converge: " << numfailed << std::endl;
778 std::cout << "No of iterations : " << iter << std::endl;
779 std::cout << "Solve time: " << solvetime << std::endl;
780 std::cout << "No of comp. vecs. : " << numev << std::endl;
781 }
782 std::cout << "Returning from Anasazi Wrapper." << std::endl;
783 return numev;
784
785 }
786
787
788
790 template <typename Adapter>
791 template <typename problem_t>
792 void Sphynx<Adapter>::setPreconditioner(Teuchos::RCP<problem_t> &problem)
793 {
794 std::cout << "precType_ is: " << precType_ << std::endl;
795 // Set the preconditioner
796 if(precType_ == "muelu") {
798 }
799 else if(precType_ == "polynomial") {
801 }
802 else if(precType_ == "jacobi") {
804 }
805 // else: do we want a case where no preconditioning is applied?
806 }
807
809 template <typename Adapter>
810 template <typename problem_t>
811 void Sphynx<Adapter>::setMueLuPreconditioner(Teuchos::RCP<problem_t> &problem)
812 {
813#ifdef HAVE_ZOLTAN2SPHYNX_MUELU
814 Teuchos::ParameterList paramList;
815 if(verbosity_ == 0)
816 paramList.set("verbosity", "none");
817 else if(verbosity_ == 1)
818 paramList.set("verbosity", "low");
819 else if(verbosity_ == 2)
820 paramList.set("verbosity", "medium");
821 else if(verbosity_ == 3)
822 paramList.set("verbosity", "high");
823 else if(verbosity_ >= 4)
824 paramList.set("verbosity", "extreme");
825
826 paramList.set("smoother: type", "CHEBYSHEV");
827 Teuchos::ParameterList smootherParamList;
828 smootherParamList.set("chebyshev: degree", 3);
829 smootherParamList.set("chebyshev: ratio eigenvalue", 7.0);
830 smootherParamList.set("chebyshev: eigenvalue max iterations", irregular_ ? 100 : 10);
831 paramList.set("smoother: params", smootherParamList);
832 paramList.set("use kokkos refactor", true);
833 paramList.set("transpose: use implicit", true);
834
835 if(irregular_) {
836
837 paramList.set("multigrid algorithm", "unsmoothed");
838
839 paramList.set("coarse: type", "CHEBYSHEV");
840 Teuchos::ParameterList coarseParamList;
841 coarseParamList.set("chebyshev: degree", 3);
842 coarseParamList.set("chebyshev: ratio eigenvalue", 7.0);
843 paramList.set("coarse: params", coarseParamList);
844
845 paramList.set("max levels", 5);
846 paramList.set("aggregation: drop tol", 0.40);
847
848 }
849 using prec_t = MueLu::TpetraOperator<scalar_t, lno_t, gno_t, node_t>;
850 Teuchos::RCP<prec_t> prec = MueLu::CreateTpetraPreconditioner<
851 scalar_t, lno_t, gno_t, node_t>(laplacian_, paramList);
852
853 problem->setPrec(prec);
854
855#else
856 throw std::runtime_error("\nSphynx Error: MueLu requested but not compiled into Trilinos.\n");
857#endif
858 }
859
861 template <typename Adapter>
862 template <typename problem_t>
863 void Sphynx<Adapter>::setPolynomialPreconditioner(Teuchos::RCP<problem_t> &problem)
864 {
865 int verbosity2 = Belos::Errors;
866 if(verbosity_ == 1)
867 verbosity2 += Belos::Warnings;
868 else if(verbosity_ == 2)
869 verbosity2 += Belos::Warnings + Belos::FinalSummary;
870 else if(verbosity_ == 3)
871 verbosity2 += Belos::Warnings + Belos::FinalSummary + Belos::TimingDetails;
872 else if(verbosity_ >= 4)
873 verbosity2 += Belos::Warnings + Belos::FinalSummary + Belos::TimingDetails
874 + Belos::StatusTestDetails;
875
876 Teuchos::ParameterList paramList;
877 paramList.set("Polynomial Type", "Roots");
878 paramList.set("Orthogonalization","ICGS");
879 paramList.set("Maximum Degree", laplacian_->getGlobalNumRows() > 100 ? 25 : 5);
880 paramList.set("Polynomial Tolerance", 1.0e-6 );
881 paramList.set("Verbosity", verbosity2 );
882 paramList.set("Random RHS", false );
883 paramList.set("Outer Solver", "");
884 paramList.set("Timer Label", "Belos Polynomial Solve" );
885
886 // Construct a linear problem for the polynomial solver manager
887 using lproblem_t = Belos::LinearProblem<scalar_t, mvector_t, op_t>;
888 Teuchos::RCP<lproblem_t> innerPolyProblem(new lproblem_t());
889 innerPolyProblem->setOperator(laplacian_);
890
891 using btop_t = Belos::TpetraOperator<scalar_t, lno_t, gno_t, node_t>;
892 Teuchos::RCP<btop_t> polySolver(new btop_t(innerPolyProblem,
893 Teuchos::rcpFromRef(paramList),
894 "GmresPoly", true));
895 problem->setPrec(polySolver);
896 }
897
899 template <typename Adapter>
900 template <typename problem_t>
901 void Sphynx<Adapter>::setJacobiPreconditioner(Teuchos::RCP<problem_t> &problem)
902 {
903
904 Teuchos::RCP<Ifpack2::Preconditioner<scalar_t, lno_t, gno_t, node_t>> prec;
905 std::string precType = "RELAXATION";
906
907 prec = Ifpack2::Factory::create<matrix_t> (precType, laplacian_);
908
909 Teuchos::ParameterList precParams;
910 precParams.set("relaxation: type", "Jacobi");
911 precParams.set("relaxation: fix tiny diagonal entries", true);
912 precParams.set("relaxation: min diagonal value", 1.0e-8);
913
914 prec->setParameters(precParams);
915 prec->initialize();
916 prec->compute();
917
918 problem->setPrec(prec);
919
920 }
921
923 // Transform the computed eigenvectors into coordinates
924 template <typename Adapter>
925 void Sphynx<Adapter>::eigenvecsToCoords(Teuchos::RCP<mvector_t> &eigenVectors,
926 int computedNumEv,
927 Teuchos::RCP<mvector_t> &coordinates)
928 {
929 // Extract the meaningful eigenvectors by getting rid of the first one
930 Teuchos::Array<size_t> columns (computedNumEv-1);
931 for (int j = 0; j < computedNumEv-1; ++j) {
932 columns[j] = j+1;
933 }
934 coordinates = eigenVectors->subCopy (columns());
935 coordinates->setCopyOrView (Teuchos::View);
936 }
937
938
940 // If user provided some weights, use them by getting them from the adapter.
941 // If user didn't provide weights but told us to use degree as weight, do so.
942 // If user neither provided weights nor told us what to do, use degree as weight.
943 template <typename Adapter>
944 void Sphynx<Adapter>::computeWeights(std::vector<const weight_t *> vecweights,
945 std::vector<int> strides)
946 {
947
948 int numWeights = adapter_->getNumWeightsPerVertex();
949 int numConstraints = numWeights > 0 ? numWeights : 1;
950
951 size_t myNumVertices = adapter_->getLocalNumVertices();
952 weight_t ** weights = new weight_t*[numConstraints];
953 for(int j = 0; j < numConstraints; j++)
954 weights[j] = new weight_t[myNumVertices];
955
956 // Will be needed if we use degree as weight
957 const offset_t *offset;
958 const gno_t *columnId;
959
960 // If user hasn't set any weights, use vertex degrees as weights
961 if(numWeights == 0) {
962
963 // Compute the weight of vertex i as the number of nonzeros in row i.
964 adapter_->getEdgesView(offset, columnId);
965 for (size_t i = 0; i < myNumVertices; i++)
966 weights[0][i] = offset[i+1] - offset[i] - 1;
967
968 vecweights.push_back(weights[0]);
969 strides.push_back(1);
970 }
971 else {
972
973 // Use the weights if there are any already set in the adapter
974 for(int j = 0; j < numConstraints; j++) {
975
976 if(adapter_->useDegreeAsVertexWeight(j)) {
977 // Compute the weight of vertex i as the number of nonzeros in row i.
978 adapter_->getEdgesView(offset, columnId);
979 for (size_t i = 0; i < myNumVertices; i++)
980 weights[j][i] = offset[i+1] - offset[i];
981 }
982 else{
983 int stride;
984 const weight_t *wgt = NULL;
985 adapter_->getVertexWeightsView(wgt, stride, j);
986
987 for (size_t i = 0; i < myNumVertices; i++)
988 weights[j][i] = wgt[i];
989 }
990
991 vecweights.push_back(weights[j]);
992 strides.push_back(1);
993
994 }
995 }
996
997 }
998
999
1001 // Compute a partition by calling MJ on given coordinates with given weights
1002 template <typename Adapter>
1003 void Sphynx<Adapter>::MJwrapper(const Teuchos::RCP<const mvector_t> &coordinates,
1004 std::vector<const weight_t *> weights,
1005 std::vector<int> strides,
1006 const Teuchos::RCP<PartitioningSolution<Adapter>> &solution)
1007 {
1008
1009 Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("Sphynx::MJ"));
1010
1011 using mvector_adapter_t = Zoltan2::XpetraMultiVectorAdapter<mvector_t>;
1012 using base_adapter_t = typename mvector_adapter_t::base_adapter_t;
1015
1016 size_t myNumVertices = coordinates->getLocalLength();
1017
1018 // Create the base adapter for the multivector adapter
1019 Teuchos::RCP<mvector_adapter_t> adapcoordinates(new mvector_adapter_t(coordinates,
1020 weights,
1021 strides));
1022 Teuchos::RCP<const base_adapter_t> baseAdapter =
1023 Teuchos::rcp(dynamic_cast<const base_adapter_t *>(adapcoordinates.get()), false);
1024
1025 // Create the coordinate model using the base multivector adapter
1027
1028 // Create the MJ object
1029 Teuchos::RCP<const Comm<int>> comm2 = comm_;
1030 Teuchos::RCP<mj_t> mj(new mj_t(env_, comm2, baseAdapter));
1031
1032 // Partition with MJ
1033 Teuchos::RCP<solution_t> vectorsolution( new solution_t(env_, comm2, 1, mj));
1034 mj->partition(vectorsolution);
1035
1036 // Transform the solution
1037 Teuchos::ArrayRCP<part_t> parts(myNumVertices);
1038 for(size_t i = 0; i < myNumVertices; i++) {
1039 parts[i] = vectorsolution->getPartListView()[i];
1040 }
1041 solution->setParts(parts);
1042 }
1043
1044} // namespace Zoltan2
1045
1046#endif
Contains the Multi-jagged algorthm.
Defines the CoordinateModel classes.
Defines XpetraCrsGraphAdapter class.
Defines the XpetraMultiVectorAdapter.
Algorithm defines the base class for all algorithms.
virtual void map(const RCP< MappingSolution< Adapter > > &)
Mapping method.
A PartitioningSolution is a solution to a partitioning problem.
Sphynx(const RCP< const Environment > &env, const RCP< Teuchos::ParameterList > &params, const RCP< Teuchos::ParameterList > &sphynxParams, const RCP< const Comm< int > > &comm, const RCP< const XpetraCrsGraphAdapter< graph_t > > &adapter)
void setMueLuPreconditioner(Teuchos::RCP< problem_t > &problem)
Teuchos::RCP< mvector_t > getSphynxEigenvectors()
typename Adapter::offset_t offset_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::CrsGraph< lno_t, gno_t, node_t > graph_t
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
void setUserEigenvectors(const Teuchos::RCP< mvector_t > &userEvects)
typename Adapter::node_t node_t
Tpetra::Operator< scalar_t, lno_t, gno_t, node_t > op_t
typename Adapter::lno_t lno_t
int AnasaziWrapper(const int numEigenVectors)
typename Adapter::scalar_t weight_t
void partition(const Teuchos::RCP< PartitioningSolution< Adapter > > &solution)
Teuchos::RCP< matrix_t > computeCombinatorialLaplacian()
Teuchos::RCP< matrix_t > computeNormalizedLaplacian(bool AHat=false)
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
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.
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