MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_Constraint_def.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// MueLu: A package for multigrid based preconditioning
4//
5// Copyright 2012 NTESS and the MueLu contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef MUELU_CONSTRAINT_DEF_HPP
11#define MUELU_CONSTRAINT_DEF_HPP
12
13#include <Xpetra_Map.hpp>
14#include <Xpetra_MultiVector.hpp>
15#include <Xpetra_Matrix.hpp>
16#include "KokkosBatched_Copy_Internal.hpp"
17#include "Teuchos_Assert.hpp"
18#include "Xpetra_MatrixFactory.hpp"
19#include "Xpetra_MatrixMatrix.hpp"
20
21#include "MueLu_Exceptions.hpp"
22#include "MueLu_ProductOperator.hpp"
24#include "MueLu_Utilities.hpp"
25#include "MueLu_Monitor.hpp"
26
27#include "KokkosBlas1_set.hpp"
28#include "KokkosBatched_QR_FormQ_TeamVector_Internal.hpp"
29#include "KokkosBatched_ApplyQ_Decl.hpp"
30#include "KokkosBatched_SetIdentity_Decl.hpp"
31#include "KokkosBatched_SetIdentity_Impl.hpp"
32#include "Kokkos_DualView.hpp"
33#include "Kokkos_Pair.hpp"
34#include "Kokkos_UnorderedMap.hpp"
35#include "KokkosBatched_QR_Decl.hpp"
36#include "KokkosBatched_QR_Serial_Impl.hpp"
37#include "KokkosBatched_QR_TeamVector_Impl.hpp"
38#include "KokkosBatched_LU_Decl.hpp"
39#include "KokkosBatched_LU_Team_Impl.hpp"
40#include "KokkosBatched_Trsv_Decl.hpp"
41#include "KokkosBatched_Trsv_TeamVector_Impl.hpp"
42#include "KokkosBatched_Gemm_Decl.hpp"
43#include "KokkosBatched_Gemm_Team_Impl.hpp"
44#include "KokkosBatched_Gemv_Decl.hpp"
45#include "KokkosBatched_Gemv_Team_Impl.hpp"
46#include "KokkosBatched_Copy_Decl.hpp"
47#include "KokkosBatched_Copy_Impl.hpp"
48
49namespace MueLu {
50
51template <class LocalGraph, class LocalVector>
52class MinSpmMV {
53 private:
54 using local_ordinal_type = typename LocalVector::value_type;
55
56 LocalGraph lclGraph;
57 LocalVector lhs;
58 LocalVector rhs;
59
60 const local_ordinal_type MAX_VAL = KokkosKernels::ArithTraits<local_ordinal_type>::max();
61
62 public:
63 MinSpmMV(LocalGraph lclGraph_, LocalVector lhs_, LocalVector rhs_)
64 : lclGraph(lclGraph_)
65 , lhs(lhs_)
66 , rhs(rhs_) {}
67
68 KOKKOS_INLINE_FUNCTION
69 void init(bool& dst) {
70 dst = false;
71 }
72
73 KOKKOS_INLINE_FUNCTION
74 void join(bool& dst, const bool& src) {
75 dst = dst || src;
76 }
77
78 KOKKOS_INLINE_FUNCTION
79 void operator()(const local_ordinal_type i, bool& changed) const {
81 for (local_ordinal_type jj = lclGraph.row_map(i); jj < (local_ordinal_type)lclGraph.row_map(i + 1); ++jj) {
82 auto j = lclGraph.entries(jj);
83 val = Kokkos::min(val, lhs(j));
84 }
85 auto prev = rhs(i);
86 rhs(i) = val;
87 changed = changed || (prev != val);
88 }
89};
90
91template <class LocalGraph, class LocalVector>
92class MinSpmMVT {
93 private:
94 using local_ordinal_type = typename LocalVector::value_type;
95
96 LocalGraph lclGraph;
97 LocalVector lhs;
98 LocalVector rhs;
100 const local_ordinal_type MAX_VAL = KokkosKernels::ArithTraits<local_ordinal_type>::max();
101
102 public:
103 MinSpmMVT(LocalGraph lclGraph_, LocalVector lhs_, LocalVector rhs_)
104 : lclGraph(lclGraph_)
105 , lhs(lhs_)
106 , rhs(rhs_) {
107 Kokkos::deep_copy(rhs, MAX_VAL);
108 }
109
110 KOKKOS_INLINE_FUNCTION
111 void operator()(const local_ordinal_type i) const {
113 for (local_ordinal_type jj = lclGraph.row_map(i); jj < lclGraph.row_map(i + 1); ++jj) {
114 auto j = lclGraph.entries(jj);
115 Kokkos::atomic_min(&rhs(j), val);
116 }
117 }
119
120template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
122 public:
123 using CrsGraph = typename Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>;
124 using CrsMatrix = typename Xpetra::CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>;
125 using local_graph_type = typename CrsGraph::local_graph_type;
126 using local_matrix_type = typename CrsMatrix::local_matrix_type;
127 using scalar_type = typename local_matrix_type::value_type;
128 using ATS = KokkosKernels::ArithTraits<scalar_type>;
129 using magnitude_type = typename ATS::magnitudeType;
130 using magATS = KokkosKernels::ArithTraits<magnitude_type>;
131 using memory_space = typename Node::memory_space;
133 using shared_matrix = Kokkos::View<scalar_type**, typename Node::execution_space::scratch_memory_space, Kokkos::MemoryUnmanaged>;
134 using shared_vector = Kokkos::View<scalar_type*, typename Node::execution_space::scratch_memory_space, Kokkos::MemoryUnmanaged>;
135
136 BlockInverseFunctor(local_matrix_type A_, local_graph_type blocks_, LocalOrdinal maxBlocksize_, local_matrix_type invA_, Kokkos::View<bool*, memory_space> singular_)
137 : A(A_)
138 , blocks(blocks_)
139 , maxBlocksize(maxBlocksize_)
140 , invA(invA_)
141 , singular(singular_) {}
142
145 class TagApply {};
146
147 private:
148 const scalar_type zero = ATS::zero();
149 const magnitude_type mag_zero = magATS::zero();
150 const scalar_type one = ATS::one();
151
156 Kokkos::View<bool*, memory_space> singular;
157
158 public:
159 KOKKOS_INLINE_FUNCTION
160 void operator()(TagFindSingularBlocks, const typename Kokkos::TeamPolicy<typename Node::execution_space>::member_type& thread) const {
161 using member_type = typename Kokkos::TeamPolicy<typename Node::execution_space>::member_type;
162
163 auto blockId = thread.league_rank();
164 auto blockRow = blocks.rowConst(blockId);
165 auto blockSize = blockRow.length;
166
167 shared_matrix lclA(thread.team_shmem(), blockSize, blockSize);
168 shared_vector lclConst(thread.team_shmem(), blockSize);
169 shared_vector lclAConst(thread.team_shmem(), blockSize);
170
171 // Initialize lclA
172 KokkosBlas::TeamSet<member_type>::invoke(thread, zero, lclA);
173 KokkosBlas::TeamSet<member_type>::invoke(thread, one, lclConst);
174 thread.team_barrier();
175
176 // extract block from A
177 for (LocalOrdinal ii = 0; ii < blockSize; ++ii) {
178 auto i = blockRow.colidx(ii);
179 auto row = A.rowConst(i);
180 for (LocalOrdinal jj = 0; jj < row.length; ++jj) {
181 auto j = row.colidx(jj);
182 auto d = row.value(jj);
183 for (LocalOrdinal kk = 0; kk < blockSize; ++kk)
184 if (blockRow.colidx(kk) == j) {
185 lclA(ii, kk) += d;
186 break;
187 }
188 }
189 }
190
191 // lclAConst = lclA * lclConst
192 KokkosBlas::TeamGemv<member_type, KokkosBlas::Trans::NoTranspose, KokkosBlas::Algo::Gemv::Unblocked>::invoke(thread, one, lclA, lclConst, zero, lclAConst);
193 thread.team_barrier();
194
195 magnitude_type norm2 = mag_zero;
196 for (LocalOrdinal i = 0; i < blockSize; ++i) {
197 norm2 += ATS::magnitude(lclAConst(i) * lclAConst(i));
198 }
199
200 singular(blockId) = (ATS::magnitude(norm2) < ATS::epsilon());
201 }
202
203 KOKKOS_INLINE_FUNCTION
204 void operator()(TagCountSingularBlocks, const typename Kokkos::TeamPolicy<typename Node::execution_space>::member_type& thread, LocalOrdinal& numSingularBlocks) const {
205 auto blockId = thread.league_rank();
206 if (singular(blockId))
207 ++numSingularBlocks;
208 }
209
210 KOKKOS_INLINE_FUNCTION
211 void operator()(TagApply, const typename Kokkos::TeamPolicy<typename Node::execution_space>::member_type& thread) const {
212 using member_type = typename Kokkos::TeamPolicy<typename Node::execution_space>::member_type;
213
214 auto blockId = thread.league_rank();
215 auto blockRow = blocks.rowConst(blockId);
216 auto blockSize = blockRow.length;
217
218 shared_matrix lclA(thread.team_shmem(), blockSize, blockSize);
219 shared_matrix lclInvA(thread.team_shmem(), blockSize, blockSize);
220
221 const bool PseudoInverse = (!(singular.extent(0) == 0)) && singular(blockId);
222
223 // Initialize lclA
224 // If PseudoInverse, we shift the constant mode.
225 KokkosBlas::TeamSet<member_type>::invoke(thread, PseudoInverse ? one : zero, lclA);
226 thread.team_barrier();
227
228 // extract block from A
229 for (LocalOrdinal ii = 0; ii < blockSize; ++ii) {
230 auto i = blockRow.colidx(ii);
231 auto row = A.rowConst(i);
232 for (LocalOrdinal jj = 0; jj < row.length; ++jj) {
233 auto j = row.colidx(jj);
234 auto d = row.value(jj);
235 for (LocalOrdinal kk = 0; kk < blockSize; ++kk)
236 if (blockRow.colidx(kk) == j) {
237 lclA(ii, kk) += d;
238 break;
239 }
240 }
241 }
242
243 // LU
244 {
245 // LU factorization: lclA = L * U
246 KokkosBatched::TeamLU<member_type, KokkosBlas::Algo::QR::Unblocked>::invoke(thread, lclA);
247
248 // set lclInvA to identity matrix
249 KokkosBatched::TeamSetIdentity<member_type>::invoke(thread, lclInvA);
250 thread.team_barrier();
251
252 // // lclInvA = L^{-1}*lclInvA
253 for (LocalOrdinal j = 0; j < blockSize; ++j)
254 KokkosBatched::TeamVectorTrsv<member_type, KokkosBatched::Uplo::Lower, KokkosBatched::Trans::NoTranspose, KokkosBatched::Diag::Unit, KokkosBatched::Algo::Trsv::Unblocked>::invoke(thread, one, lclA, Kokkos::subview(lclInvA, Kokkos::ALL(), j));
255 thread.team_barrier();
256
257 // // lclInvA = R^{-1}*lclInvA
258 for (LocalOrdinal j = 0; j < blockSize; ++j)
259 KokkosBatched::TeamVectorTrsv<member_type, KokkosBatched::Uplo::Upper, KokkosBatched::Trans::NoTranspose, KokkosBatched::Diag::NonUnit, KokkosBatched::Algo::Trsv::Unblocked>::invoke(thread, one, lclA, Kokkos::subview(lclInvA, Kokkos::ALL(), j));
260 thread.team_barrier();
261 }
262
263 // The QR in Kokkos Kernels is broken. Once it gets fixed we can use it and remove LU.
264 //
265 // QR
266 // {
267
268 // shared_vector tau(thread.team_shmem(), blockSize);
269 // shared_vector work(thread.team_shmem(), blockSize);
270
271 // // QR factorization: lclA = Q * R
272 // KokkosBatched::TeamVectorQR<member_type, KokkosBlas::Algo::QR::Unblocked>::invoke(thread, lclA, tau, work);
273
274 // // set lclInvA to identity matrix
275 // KokkosBatched::TeamSetIdentity<member_type>::invoke(thread, lclInvA);
276 // thread.team_barrier();
277
278 // // lclInvA = Q^T*lclInvA
279 // KokkosBatched::TeamVectorApplyQ<member_type, KokkosBatched::Side::Left, KokkosBlas::Trans::Transpose, KokkosBlas::Algo::ApplyQ::Unblocked>::invoke(thread, lclA, tau, lclInvA, work);
280 // thread.team_barrier();
281
282 // // lclInvA = R^{-1}*lclInvA
283 // for (LocalOrdinal j = 0; j < blockSize; ++j)
284 // KokkosBatched::TeamVectorTrsv<member_type, KokkosBatched::Uplo::Upper, KokkosBatched::Trans::NoTranspose, KokkosBatched::Diag::NonUnit, KokkosBatched::Algo::Trsv::Unblocked>::invoke(thread, one, lclA, Kokkos::subview(lclInvA, Kokkos::ALL(), j));
285 // thread.team_barrier();
286 // }
287
288 if (PseudoInverse) {
289 // Multiply with projection that removes constant vector
290
291 // Set up projection matrix
292 for (LocalOrdinal ii = 0; ii < blockSize; ++ii) {
293 for (LocalOrdinal kk = 0; kk < blockSize; ++kk) {
294 if (ii == kk) {
295 lclA(ii, kk) = one - one / (scalar_type)blockSize;
296 } else {
297 lclA(ii, kk) = -one / (scalar_type)blockSize;
298 }
299 }
300 }
301 // Copy lclInvA to temp
302 shared_matrix temp(thread.team_shmem(), blockSize, blockSize);
303 KokkosBatched::TeamCopy<member_type, KokkosBatched::Trans::NoTranspose>::invoke(thread, lclInvA, temp);
304 thread.team_barrier();
305
306 // lclInvA = proj * lclInvA
307 KokkosBatched::TeamGemm<member_type, KokkosBatched::Trans::NoTranspose, KokkosBatched::Trans::NoTranspose, KokkosBatched::Algo::Gemm::Unblocked>::invoke(thread, one, lclA, temp, zero, lclInvA);
308 thread.team_barrier();
309 }
310
311 // write inverse of block to invA
312 for (LocalOrdinal ii = 0; ii < blockSize; ++ii) {
313 auto i = blockRow.colidx(ii);
314 auto row = invA.row(i);
315 for (LocalOrdinal jj = 0; jj < row.length; ++jj) {
316 auto j = row.colidx(jj);
317 for (LocalOrdinal kk = 0; kk < blockSize; ++kk)
318 if (blockRow.colidx(kk) == j) {
319 row.value(jj) = lclInvA(ii, kk);
320 break;
321 }
322 }
323 }
324 }
325
326 // amount of shared memory
327 size_t team_shmem_size(int /* team_size */) const {
328 return 3 * shared_matrix::shmem_size(maxBlocksize, maxBlocksize) + 2 * shared_vector::shmem_size(maxBlocksize);
329 }
330};
331
332template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
333const Teuchos::RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> Constraint<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getDomainMap() const {
334 return X_->getDomainMap();
335}
336
337template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
338const Teuchos::RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> Constraint<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getRangeMap() const {
339 return X_->getDomainMap();
340}
341
342template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
344#ifdef HAVE_MUELU_BELOS
345 Monitor m(*this, "PrepareLeastSquaresSolveBelos");
346
347 TEUCHOS_TEST_FOR_EXCEPTION(!detect_singular_blocks, Exceptions::RuntimeError, "The option \"emin: least squares solver type\" = \"Belos\" is currently only implemented for non-singular constraint solves");
348
349 problem_ = rcp(new Belos::LinearProblem<Scalar, MV, OP>());
350
351 std::vector<RCP<Operator>> ops = {X_, X_};
352 std::vector<Teuchos::ETransp> modes = {Teuchos::NO_TRANS, Teuchos::TRANS};
353 RCP<Operator> XXt = rcp(new ProductOperator(ops, modes));
354 auto belosXXt = rcp(new Belos::XpetraOp<Scalar, LocalOrdinal, GlobalOrdinal, Node>(XXt));
355
356 problem_->setOperator(belosXXt);
357 problem_->setLabel("LeastSquares");
358
359 auto belosList = rcp(new Teuchos::ParameterList());
360 belosList->set("Implicit Residual Scaling", "None");
361 belosList->set("Convergence Tolerance", 1e-16);
362 auto out = GetMueLuOStream();
363 belosList->set("Output Stream", out->getOStream());
364 // belosList->set("Verbosity", Belos::Errors + Belos::Warnings + Belos::StatusTestDetails);
365 // belosList->set("Output Frequency", 1);
366 // belosList->set("Output Style", Belos::Brief);
367
368 Belos::SolverFactory<Scalar, MV, OP> solverFactory;
369 solver_ = solverFactory.create("Pseudo Block CG", belosList);
370#else
371 TEUCHOS_TEST_FOR_EXCEPTION(true, Exceptions::RuntimeError, "Energy minimization multigrid requires Belos to be enabled.");
372#endif
373}
374
375template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
376Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
377allocateBlockDiagonalMatrix(RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> map,
378 const typename Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>::local_graph_type blocks) {
379 using graph_type = typename Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>::local_graph_type;
380 using matrix_type = typename Xpetra::CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_matrix_type;
381 using execution_space = typename Node::execution_space;
382
383 auto numRows = map->getLocalNumElements();
384 auto numBlocks = blocks.numRows();
385 typename graph_type::row_map_type::non_const_type rowptr("rowptr", numRows + 1);
386
387 LocalOrdinal nnz = 0;
388 Kokkos::parallel_reduce(
389 "MueLu::Constraint::allocateBlockDiagonalMatrix::1", Kokkos::RangePolicy<execution_space>(0, numBlocks), KOKKOS_LAMBDA(const LocalOrdinal blockId, LocalOrdinal& count) {
390 auto blockRow = blocks.rowConst(blockId);
391 auto blockSize = blockRow.length;
392
393 for (LocalOrdinal k = 0; k < blockSize; ++k) {
394 auto rowId = blockRow.colidx(k);
395 if ((decltype(numRows))rowId+2<numRows+1)
396 Kokkos::atomic_add(&rowptr(rowId+2), blockSize);
397 count += blockSize;
398 } }, nnz);
399
400 Kokkos::parallel_scan(
401 "MueLu::Constraint::allocateBlockDiagonalMatrix::3", Kokkos::RangePolicy<execution_space>(0, numRows), KOKKOS_LAMBDA(const LocalOrdinal rowId, LocalOrdinal& sum, const bool is_final) {
402 sum += rowptr(rowId+1);
403 if (is_final) {
404 rowptr(rowId+1) = sum;
405 } });
406
407 typename graph_type::entries_type::non_const_type indices("lclInvXXt_indices", nnz);
408
409 Kokkos::parallel_for(
410 "MueLu::Constraint::allocateBlockDiagonalMatrix::3", Kokkos::RangePolicy<execution_space>(0, numBlocks), KOKKOS_LAMBDA(const LocalOrdinal blockId) {
411 auto blockRow = blocks.rowConst(blockId);
412 auto blockSize = blockRow.length;
413
414 for (LocalOrdinal k = 0; k < blockSize; ++k) {
415 auto rowId = blockRow.colidx(k);
416 for (LocalOrdinal jj = 0; jj < blockSize; ++jj) {
417 auto j = blockRow.colidx(jj);
418 auto l = Kokkos::atomic_fetch_inc(&rowptr(rowId + 1));
419 indices(l) = j;
420 }
421 }
422 });
423
424 auto lclInvXXtGraph = graph_type(indices, rowptr);
425 typename matrix_type::values_type::non_const_type values("lclInvXXt_values", nnz);
426 auto lclInvXXt = matrix_type("lclInvXXt", numRows, values, lclInvXXtGraph);
427 return Xpetra::MatrixFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(lclInvXXt, map, map, map, map);
428}
429
430template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
431typename Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>::local_graph_type Constraint<Scalar, LocalOrdinal, GlobalOrdinal, Node>::FindBlocks(RCP<const Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>>& XXt) {
432 using execution_space = typename Node::execution_space;
433 using memory_space = typename Node::memory_space;
434 using range_type = Kokkos::RangePolicy<LocalOrdinal, execution_space>;
435
436 // This is a generic but less efficient implementation that discovers blocks by looping
437 // over the graph of the constraint matrix X. Using additional information, specific constraints
438 // can compute the block structure in simpler fashion.
439
440 auto numConstraints = XXt->getRowMap()->getLocalNumElements();
441
442 Kokkos::View<LocalOrdinal*, memory_space> blockIds("blockIds", numConstraints);
443 Kokkos::View<LocalOrdinal*, memory_space> blockIds2("blockIds2", numConstraints);
444
445 auto lclGraph = XXt->getLocalGraphDevice();
446
447 auto constraint_range = range_type(0, numConstraints);
448
449 // initialize blockIds with constraintIds
450 Kokkos::parallel_for(
451 "MueLu::Constraint::FindBlocks::init_blockIds", constraint_range,
452 KOKKOS_LAMBDA(const LocalOrdinal contraintId) {
453 blockIds(contraintId) = contraintId;
454 });
455 Kokkos::fence();
456
457 // loop over rows of XXt and assign min of encountered blockIds until nothing changes anymore.
458 bool changed = true;
459 bool resultsIn2 = false;
460 while (changed) {
461 changed = false;
462 if (!resultsIn2) {
463 MinSpmMV functor(lclGraph, blockIds, blockIds2);
464 Kokkos::parallel_reduce("MueLu::Constraint::FindBlocks::minSpmv1", constraint_range, functor, changed);
465 resultsIn2 = true;
466 } else {
467 MinSpmMV functor(lclGraph, blockIds2, blockIds);
468 Kokkos::parallel_reduce("MueLu::Constraint::FindBlocks::minSpmv2", constraint_range, functor, changed);
469 resultsIn2 = false;
470 }
471 }
472 if (resultsIn2)
473 Kokkos::deep_copy(blockIds, blockIds2);
474
475 // record all blockIds that are still in use
476 Kokkos::View<bool*, memory_space> blockStatus("blockStatus", numConstraints);
477 Kokkos::parallel_for(
478 "MueLu::Constraint::FindBlocks::set_blockstatus", constraint_range,
479 KOKKOS_LAMBDA(LocalOrdinal contraintId) {
480 Kokkos::atomic_store(&blockStatus(blockIds(contraintId)), true);
481 });
482 Kokkos::fence();
483
484 // renumber blockIds that are still in use consecutively
485 Kokkos::View<LocalOrdinal*, memory_space> newBlockIds("newBlockIds", numConstraints);
486 LocalOrdinal numBlocks = 0;
487 Kokkos::parallel_scan(
488 "MueLu::Constraint::FindBlocks::compute_blockIds", constraint_range,
489 KOKKOS_LAMBDA(LocalOrdinal contraintId, LocalOrdinal & blockId, const bool final) {
490 if (final)
491 newBlockIds(contraintId) = blockId;
492 if (blockStatus(contraintId))
493 ++blockId;
494 },
495 numBlocks);
496
497 // Build graph with block info
498 using graph_type = typename Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>::local_graph_type;
499 typename graph_type::row_map_type::non_const_type rowptr("blocks_rowptr", numBlocks + 1);
500 typename graph_type::entries_type::non_const_type indices("blocks_indices", numConstraints);
501
502 Kokkos::parallel_for(
503 "MueLu::Constraint::FindBlocks::count_entries_per_block", constraint_range, KOKKOS_LAMBDA(const LocalOrdinal constraintId) {
504 auto blockId = newBlockIds(blockIds(constraintId));
505 if (blockId + 2 < numBlocks + 1)
506 Kokkos::atomic_inc(&rowptr(blockId + 2));
507 });
508 Kokkos::fence();
509
510 auto block_range = range_type(0, numBlocks);
511
512 // prefix sum
513 Kokkos::parallel_scan(
514 "MueLu::Constraint::FindBlocks::prefix_sum", block_range, KOKKOS_LAMBDA(const LocalOrdinal blockId, LocalOrdinal& sum, const bool final) {
515 sum += rowptr(blockId+1);
516 if (final) {
517 rowptr(blockId+1) = sum;
518 } });
519
520 Kokkos::parallel_for(
521 "MueLu::Constraint::FindBlocks::fill", constraint_range, KOKKOS_LAMBDA(const LocalOrdinal contraintId) {
522 auto blockId = newBlockIds(blockIds(contraintId));
523 auto offset = Kokkos::atomic_fetch_inc(&rowptr(blockId + 1));
524 indices(offset) = contraintId;
525 });
526
527 graph_type blocks(indices, rowptr);
528
529 return blocks;
530}
531
532template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
534 using memory_space = typename Node::memory_space;
535 Monitor m(*this, "PrepareLeastSquaresSolveDirect");
536
537 RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>> XXt;
538 {
539 SubMonitor m2(*this, "XXt");
540 XXt = Xpetra::MatrixMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Multiply(*X_, false, *X_, true, XXt, GetOStream(Runtime0), true, true);
541 }
542
543 auto XXtgraph = XXt->getCrsGraph();
544 auto blocks = this->FindBlocks(XXtgraph);
545 invXXt_ = allocateBlockDiagonalMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>(XXt->getRowMap(), blocks);
546
547 LocalOrdinal numBlocks = blocks.numRows();
548 LocalOrdinal maxBlocksize = invXXt_->getLocalMaxNumRowEntries();
549
550 // If we pass a view of size 0 to the functor, all blocks are assumed to be non-singular.
551 Kokkos::View<bool*, memory_space> block_is_singular;
552 LocalOrdinal numSingularBlocks = 0;
553 if (detect_singular_blocks)
554 block_is_singular = Kokkos::View<bool*, memory_space>("block_is_singular", numBlocks);
555
557 functor_type functor(XXt->getLocalMatrixDevice(), blocks, maxBlocksize, invXXt_->getLocalMatrixDevice(), block_is_singular);
558
559 if (detect_singular_blocks) {
560 SubMonitor m2(*this, "singular block detection");
561 Kokkos::parallel_for("MueLu::Constraint::findSingularBlocks", Kokkos::TeamPolicy<typename Node::execution_space, typename functor_type::TagFindSingularBlocks>(numBlocks, 1), functor);
562
563 if (IsPrint(Statistics0)) {
564 Kokkos::parallel_reduce("MueLu::Constraint::countSingularBlocks", Kokkos::TeamPolicy<typename Node::execution_space, typename functor_type::TagCountSingularBlocks>(numBlocks, 1), functor, numSingularBlocks);
565 }
566 }
567
568 {
569 SubMonitor m2(*this, "inversion");
570 Kokkos::parallel_for("MueLu::Constraint::invertBlocks", Kokkos::TeamPolicy<typename Node::execution_space, typename functor_type::TagApply>(numBlocks, 1), functor);
571 }
572
573 if (IsPrint(Statistics0)) {
574 // print some stats
575
576 auto comm = invXXt_->getRowMap()->getComm();
577 GlobalOrdinal globalNumBlocks;
578 GlobalOrdinal globalNumSingularBlocks;
579 MueLu_sumAll(comm, (GlobalOrdinal)numBlocks, globalNumBlocks);
580 MueLu_sumAll(comm, (GlobalOrdinal)numSingularBlocks, globalNumSingularBlocks);
581
582 GetOStream(Statistics0) << "Least-squares problem:\n maximum block size: " << invXXt_->getGlobalMaxNumRowEntries() << "\n Number of blocks: " << globalNumBlocks << "\n Number of singular blocks: " << globalNumSingularBlocks << std::endl;
583 }
584}
585
586template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
587void Constraint<Scalar, LocalOrdinal, GlobalOrdinal, Node>::PrepareLeastSquaresSolve(const std::string& solverType, const bool detect_singular_blocks) {
588 if (solverType == "Belos")
589 PrepareLeastSquaresSolveBelos(detect_singular_blocks);
590 else if (solverType == "direct")
591 PrepareLeastSquaresSolveDirect(detect_singular_blocks);
592 else
593 TEUCHOS_TEST_FOR_EXCEPTION(true, Exceptions::RuntimeError, "solverType must be one of (Belos|direct), not \"" << solverType << "\".");
594 solverType_ = solverType;
595}
596
597template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
599 // Solve (X * X^T) * C = B
600#ifdef HAVE_MUELU_BELOS
601 problem_->setLHS(rcpFromRef(C));
602 problem_->setRHS(rcpFromRef(B));
603 TEUCHOS_ASSERT(problem_->setProblem());
604
605 solver_->setProblem(problem_);
606 solver_->solve();
607#endif
608}
609
610template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
612 // Solve (X * X^T) * C = B
613 invXXt_->apply(B, C);
614}
615
616template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
617void Constraint<Scalar, LocalOrdinal, GlobalOrdinal, Node>::LeastSquaresSolve(const MultiVector& B, MultiVector& C) const {
618 if (solverType_ == "Belos")
619 LeastSquaresSolveBelos(B, C);
620 else if (solverType_ == "direct")
621 LeastSquaresSolveDirect(B, C);
622 else
623 TEUCHOS_TEST_FOR_EXCEPTION(true, Exceptions::RuntimeError, "solverType must be one of (Belos|direct), not \"" << solverType_ << "\".");
624}
625
626template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
628 MultiVector& Projected,
629 Teuchos::ETransp mode,
630 Scalar alpha,
631 Scalar beta) const {
632 const auto one = Teuchos::ScalarTraits<Scalar>::one();
633 const auto zero = Teuchos::ScalarTraits<Scalar>::zero();
634
635 TEUCHOS_ASSERT(mode == Teuchos::NO_TRANS);
636 TEUCHOS_ASSERT(alpha == one);
637 TEUCHOS_ASSERT(beta == zero);
638
639 // Projected = P - X^T * (X * X^T)^{-1} * X * P
640 Projected = P;
641 X_->apply(P, *temp1_, Teuchos::NO_TRANS);
642 LeastSquaresSolve(*temp1_, *temp2_);
643 X_->apply(*temp2_, *temp3_, Teuchos::TRANS);
644 Projected.update(-one, *temp3_, one);
645}
646
647template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
648void Constraint<Scalar, LocalOrdinal, GlobalOrdinal, Node>::residual(const Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>& X,
649 const Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>& B,
650 Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>& R) const {
651 const auto one = Teuchos::ScalarTraits<Scalar>::one();
652 const auto zero = Teuchos::ScalarTraits<Scalar>::zero();
653
654 apply(X, R, Teuchos::NO_TRANS, one, zero);
655 R.update(one, B, -one);
656}
657
658template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
659RCP<const Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>> Constraint<Scalar, LocalOrdinal, GlobalOrdinal, Node>::GetPattern() const {
660 return Ppattern_;
661}
662
663template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
665 Ppattern_ = Ppattern;
666}
667
668template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
670 X_ = X;
671
672 // Allocate memory
673 temp1_ = MultiVectorFactory::Build(X_->getRangeMap(), 1);
674 temp2_ = MultiVectorFactory::Build(X_->getRangeMap(), 1);
675 temp3_ = MultiVectorFactory::Build(X_->getDomainMap(), 1);
676}
677
678template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
679RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>> Constraint<Scalar, LocalOrdinal, GlobalOrdinal, Node>::GetConstraintMatrix() {
680 return X_;
681}
682
683template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
685 const RCP<const CrsGraph>& pattern,
686 MultiVector& vecP) const {
687 auto lclPattern = pattern->getLocalGraphDevice();
688 auto lclPatternRowMap = pattern->getRowMap()->getLocalMap();
689 auto lclPatternColMap = pattern->getColMap()->getLocalMap();
690
691 auto lclMat = P.getLocalMatrixDevice();
692 auto lclMatRowMap = P.getRowMap()->getLocalMap();
693 auto lclMatColMap = P.getColMap()->getLocalMap();
694
695 auto lclVec = vecP.getLocalViewDevice(Tpetra::Access::OverwriteAll);
696 TEUCHOS_ASSERT(lclPattern.numRows() == (typename decltype(lclPattern)::size_type)lclMat.numRows());
697 TEUCHOS_ASSERT(lclPattern.entries.extent(0) == lclVec.extent(0));
698 Kokkos::deep_copy(lclVec, 0.);
699
700 using range_type = Kokkos::RangePolicy<LocalOrdinal, typename Node::execution_space>;
701 Kokkos::parallel_for(
702 "MueLu::Constraint::AssignMatrixEntriesToVector::filter", range_type(0, lclPattern.numRows()), KOKKOS_LAMBDA(const size_t i) {
703 auto grid = lclPatternRowMap.getGlobalElement(i);
704 auto row_mat = lclMat.rowConst(lclMatRowMap.getLocalElement(grid));
705
706 if (row_mat.length == 0)
707 return;
708
709 for (size_t jj = lclPattern.row_map(i); jj < lclPattern.row_map(i + 1); ++jj) {
710 auto clid_pattern = lclPattern.entries(jj);
711 auto cgid = lclPatternColMap.getGlobalElement(clid_pattern);
712 auto clid_mat = lclMatColMap.getLocalElement(cgid);
713 // find column index in lclMat
714 LocalOrdinal kk = 0;
715 while ((kk + 1 < row_mat.length) && (row_mat.colidx(kk) != clid_mat))
716 ++kk;
717 if (row_mat.colidx(kk) == clid_mat) {
718 lclVec(jj, 0) = row_mat.value(kk);
719 }
720 }
721 });
722}
723
724template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
726 MultiVector& vecP) const {
727 AssignMatrixEntriesToVector(P, GetPattern(), vecP);
728}
729
730template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
731RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>> Constraint<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
732 GetMatrixWithEntriesFromVector(MultiVector& vecP) const {
733 auto Ppattern = GetPattern();
734 RCP<Matrix> P = MatrixFactory::Build(Ppattern);
735 {
736 auto lclMat = P->getLocalMatrixDevice();
737 auto lclVec = vecP.getLocalViewDevice(Tpetra::Access::ReadOnly);
738 TEUCHOS_ASSERT(lclMat.values.extent(0) == lclVec.extent(0));
739 Kokkos::deep_copy(lclMat.values, Kokkos::subview(lclVec, Kokkos::ALL(), 0));
740 }
741 P->fillComplete(Ppattern->getDomainMap(), Ppattern->getRowMap());
742 return P;
743}
744
745} // namespace MueLu
746
747#endif // ifndef MUELU_CONSTRAINT_DEF_HPP
#define MueLu_sumAll(rcpComm, in, out)
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultScalar Scalar
MueLu::DefaultGlobalOrdinal GlobalOrdinal
typename CrsGraph::local_graph_type local_graph_type
KokkosKernels::ArithTraits< magnitude_type > magATS
BlockInverseFunctor(local_matrix_type A_, local_graph_type blocks_, LocalOrdinal maxBlocksize_, local_matrix_type invA_, Kokkos::View< bool *, memory_space > singular_)
Kokkos::View< scalar_type **, typename Node::execution_space::scratch_memory_space, Kokkos::MemoryUnmanaged > shared_matrix
Kokkos::View< scalar_type *, typename Node::execution_space::scratch_memory_space, Kokkos::MemoryUnmanaged > shared_vector
typename Xpetra::CrsGraph< LocalOrdinal, GlobalOrdinal, Node > CrsGraph
typename local_matrix_type::value_type scalar_type
KOKKOS_INLINE_FUNCTION void operator()(TagApply, const typename Kokkos::TeamPolicy< typename Node::execution_space >::member_type &thread) const
typename ATS::magnitudeType magnitude_type
typename Xpetra::CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > CrsMatrix
typename Node::memory_space memory_space
KOKKOS_INLINE_FUNCTION void operator()(TagFindSingularBlocks, const typename Kokkos::TeamPolicy< typename Node::execution_space >::member_type &thread) const
KokkosKernels::ArithTraits< scalar_type > ATS
Kokkos::View< bool *, memory_space > singular
KOKKOS_INLINE_FUNCTION void operator()(TagCountSingularBlocks, const typename Kokkos::TeamPolicy< typename Node::execution_space >::member_type &thread, LocalOrdinal &numSingularBlocks) const
typename CrsMatrix::local_matrix_type local_matrix_type
void LeastSquaresSolveDirect(const MultiVector &B, MultiVector &C) const
Direct solve of least-squares problem.
RCP< Matrix > GetConstraintMatrix()
virtual const RCP< const Map > getDomainMap() const
The Map associated with the domain of this operator, which must be compatible with X....
void PrepareLeastSquaresSolveDirect(bool detect_singular_blocks)
Prepare direct solution of least-squares problem.
virtual void apply(const MultiVector &P, MultiVector &Projected, Teuchos::ETransp mode=Teuchos::NO_TRANS, Scalar alpha=Teuchos::ScalarTraits< Scalar >::one(), Scalar beta=Teuchos::ScalarTraits< Scalar >::zero()) const
Apply constraint.
void PrepareLeastSquaresSolveBelos(bool detect_singular_blocks)
Prepare least-squares solve using Belos.
virtual CrsGraph::local_graph_type FindBlocks(RCP< const CrsGraph > &XXt)
void PrepareLeastSquaresSolve(const std::string &solverType, bool detect_singular_blocks=false)
void residual(const MultiVector &X, const MultiVector &B, MultiVector &R) const
Compute a residual R = B - (*this) * X.
void LeastSquaresSolve(const MultiVector &B, MultiVector &C) const
void SetConstraintsMatrix(RCP< Matrix > &X)
void LeastSquaresSolveBelos(const MultiVector &B, MultiVector &C) const
Perform least-squares solve using Belos.
RCP< Matrix > GetMatrixWithEntriesFromVector(MultiVector &vecP) const
RCP< const CrsGraph > GetPattern() const
void SetPattern(RCP< const CrsGraph > &Ppattern)
virtual const RCP< const Map > getRangeMap() const
The Map associated with the range of this operator, which must be compatible with Y....
void AssignMatrixEntriesToVector(const Matrix &P, const RCP< const CrsGraph > &pattern, MultiVector &vecP) const
Exception throws to report errors in the internal logical of the program.
MinSpmMV(LocalGraph lclGraph_, LocalVector lhs_, LocalVector rhs_)
typename LocalVector::value_type local_ordinal_type
const local_ordinal_type MAX_VAL
KOKKOS_INLINE_FUNCTION void operator()(const local_ordinal_type i, bool &changed) const
KOKKOS_INLINE_FUNCTION void init(bool &dst)
KOKKOS_INLINE_FUNCTION void join(bool &dst, const bool &src)
typename LocalVector::value_type local_ordinal_type
const local_ordinal_type MAX_VAL
KOKKOS_INLINE_FUNCTION void operator()(const local_ordinal_type i) const
MinSpmMVT(LocalGraph lclGraph_, LocalVector lhs_, LocalVector rhs_)
Timer to be used in non-factories.
Takes a sequence of operators and applies their product.
Timer to be used in non-factories. Similar to Monitor, but doesn't print object description.
Namespace for MueLu classes and methods.
@ Runtime0
One-liner description of what is happening.
@ Statistics0
Print statistics that do not involve significant additional computation.
Teuchos::RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > allocateBlockDiagonalMatrix(RCP< const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > map, const typename Xpetra::CrsGraph< LocalOrdinal, GlobalOrdinal, Node >::local_graph_type blocks)