Belos Version of the Day
Loading...
Searching...
No Matches
BelosKokkosDenseAdapter.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Belos: Block Linear Solvers Package
4//
5// Copyright 2004-2016 NTESS and the Belos contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9//
10#ifndef BELOS_KOKKOS_DENSE_MAT_TRAITS_HPP
11#define BELOS_KOKKOS_DENSE_MAT_TRAITS_HPP
12
19#include "Teuchos_Assert.hpp"
20#include "Teuchos_RCP.hpp"
21#include "Teuchos_ScalarTraits.hpp"
22#include "Teuchos_BLAS.hpp"
23#include "Teuchos_LAPACK.hpp"
25
26#include "Kokkos_DualView.hpp"
27#include "Kokkos_Random.hpp"
28#include "KokkosKernels_ArithTraits.hpp"
29#include "KokkosBlas1_scal.hpp"
30#include "KokkosBlas1_axpby.hpp"
31
32#include <vector>
33
34namespace Belos {
35
37 template<typename V>
38 void kokkos_transpose(const V& dst, const V& src)
39 {
40 Kokkos::parallel_for(Kokkos::MDRangePolicy<typename V::execution_space, Kokkos::Rank<2>>({0, 0}, {dst.extent(0), dst.extent(1)}),
41 KOKKOS_LAMBDA(int i, int j)
42 {
43 dst(i, j) = KokkosKernels::ArithTraits<typename V::non_const_value_type>::conj( src(j, i) );
44 });
45 }
46
48 template<class Scalar, class DM>
49 class KokkosDenseSolver : public DenseSolver<Scalar, DM>
50 {
51 public:
52 typedef typename KokkosKernels::ArithTraits<Scalar>::val_type IST; //Impl Scalar Type, as used in Tpetra
54
56
57
59
61 virtual ~KokkosDenseSolver() {}
63
65
66
68
72
74
75
78 using DenseSolver<Scalar, DM>::setSPD;
79
81
84
86
90
92
93
95
98 int factor()
99 {
100 int INFO = 0;
101
102 // Only factor matrix if it is new, otherwise factors have been computed
103 if (newMatrix_)
104 {
105 Teuchos::LAPACK<int,Scalar> lapack;
106
107 int M = DMT::GetNumRows(*A_);
108 int N = DMT::GetNumCols(*A_);
109 int Min_MN = TEUCHOS_MIN(M,N);
110 int LDA = DMT::GetStride(*A_);
111
112 IPIV_.resize( Min_MN );
113
114 DMT::SyncDeviceToHost(*A_);
115 Scalar * Aptr = DMT::GetRawHostPtr(*A_);
116
117 if (equilibrate_)
118 {
119 // Compute equilibration scalings
120 R_.resize(M);
121 if (!spd_)
122 C_.resize(N);
123
124 MagnitudeType ROWCND, COLCND, AMAX;
125 if (spd_)
126 lapack.POEQU (M, Aptr, LDA, &R_[0], &ROWCND, &AMAX, &INFO);
127 else
128 lapack.GEEQU (M, N, Aptr, LDA, &R_[0], &C_[0], &ROWCND, &COLCND, &AMAX, &INFO);
129
130 if (INFO)
131 return INFO;
132
133 // Apply equilibration to matrix
134 if (spd_) {
135 Scalar * ptr = 0;
136 for (int j=0; j<N; j++) {
137 ptr = Aptr + j*LDA;
138 Scalar s1 = R_[j];
139 for (int i=0; i<=j; i++) {
140 *ptr = *ptr*s1*R_[i];
141 ptr++;
142 }
143 }
144 }
145 else {
146 Scalar * ptr = 0;
147 for (int j=0; j<N; j++) {
148 ptr = Aptr + j*LDA;
149 Scalar s1 = C_[j];
150 for (int i=0; i<M; i++) {
151 *ptr = *ptr*s1*R_[i];
152 ptr++;
153 }
154 }
155 }
156 }
157
158 // Compute LU factor
159 if (spd_) {
160 lapack.POTRF('U', M, Aptr, LDA, &INFO);
161 }
162 else {
163 lapack.GETRF(M, N, Aptr, LDA, &IPIV_[0], &INFO);
164 }
165
166 DMT::SyncHostToDevice(*A_);
167 }
168
169 return INFO;
170 }
171
173
176 int solve()
177 {
178 bool transpose = (TRANS_ != Teuchos::NO_TRANS) ? true : false;
179
180 DMT::SyncDeviceToHost(*X_);
181
182 // LAPACK overwrites RHS vector with solution vector, so copy if necessary
183 if (B_ != X_)
184 DMT::Assign(*X_, *B_); // Copy B to X if needed
185
186 // Since B_ = X_, perform operations on X_.
187
188 int M = DMT::GetNumRows(*X_);
189 int NRHS = DMT::GetNumCols(*X_);
190 int LDX = DMT::GetStride(*X_);
191 Scalar * X = DMT::GetRawHostPtr(*X_);
192
193 if (equilibrate_)
194 {
195 // Apply equilibration scalings to RHS vector
196 MagnitudeType * R_tmp = (transpose && !spd_) ? &C_[0] : &R_[0];
197
198 Scalar * ptr = 0;
199 for (int j=0; j<NRHS; j++) {
200 ptr = X + j*LDX;
201 for (int i=0; i<M; i++) {
202 *ptr = *ptr*R_tmp[i];
203 ptr++;
204 }
205 }
206 }
207
208 int INFO = 0;
209
210 int LDA = DMT::GetStride(*A_);
211 Scalar * Aptr = DMT::GetRawHostPtr(*A_);
212 Teuchos::LAPACK<int,Scalar> lapack;
213
214 if (spd_) {
215 lapack.POTRS('U', M, NRHS, Aptr, LDA, X, LDX, &INFO);
216 }
217 else {
218 lapack.GETRS(Teuchos::ETranspChar[TRANS_], M, NRHS,
219 Aptr, LDA, &IPIV_[0], X, LDX, &INFO);
220 }
221
222 if (equilibrate_)
223 {
224 // Apply equilibration scalings to X vector
225 MagnitudeType * C_tmp = (spd_ || transpose) ? &R_[0] : &C_[0];
226
227 Scalar * ptr = 0;
228 for (int j=0; j<NRHS; j++) {
229 ptr = X + j*LDX;
230 for (int i=0; i<M; i++) {
231 *ptr = *ptr*C_tmp[i];
232 ptr++;
233 }
234 }
235 }
236
237 // Synchronize solution vector to the device
238 DMT::SyncHostToDevice(*X_);
239
240 return INFO;
241 }
243
244 private:
245
246 typedef typename Teuchos::ScalarTraits<Scalar>::magnitudeType MagnitudeType;
247
248 std::vector<int> IPIV_;
249 std::vector<MagnitudeType> R_, C_;
250
253 using DenseSolver<Scalar, DM>::TRANS_;
254 using DenseSolver<Scalar, DM>::spd_;
255
256 using DenseSolver<Scalar, DM>::A_;
257 using DenseSolver<Scalar, DM>::X_;
258 using DenseSolver<Scalar, DM>::B_;
259
260 };
261
263 //
264 template<class Scalar, class... Properties>
265 class DenseMatTraits<Scalar, Kokkos::DualView<typename KokkosKernels::ArithTraits<Scalar>::val_type **,Properties...>>{
266
267 public:
268 typedef typename KokkosKernels::ArithTraits<Scalar>::val_type IST;
269 using DM = Kokkos::DualView<IST**,Properties...>;
270
272
277 static Teuchos::RCP<DM> Create() {
278 return Teuchos::rcp(new DM("BelosDenseCreate",0,0));
279 }
280
287 static Teuchos::RCP<DM>
288 Create( const int numRows, const int numCols, bool initZero = true) {
289 if(initZero){
290 return Teuchos::rcp(new DM("BelosDenseCreate2",numRows,numCols));
291 }
292 else {
293 return Teuchos::rcp(new DM(Kokkos::view_alloc(Kokkos::WithoutInitializing,"BelosDenseCreate2"),numRows,numCols));
294 }
295 }
296
301 static Teuchos::RCP<DM>
302 CreateCopy(const DM & dm, bool transpose=false)
303 {
304 Teuchos::RCP<DM> tmpCopyRCP = Teuchos::null;
305
306 if (transpose) {
307 // want tmpCopyRCP to end up as dm^H in the end. Prefer doing transpose on device
308 tmpCopyRCP = Teuchos::rcp(new DM
309 (Kokkos::view_alloc(Kokkos::WithoutInitializing,"BelosDenseCreateCopy"),dm.extent_int(1),dm.extent_int(0)));
310 if(tmpCopyRCP->need_sync_device()) {
311 // tmpCopyRCP is only up to date on the host
312 kokkos_transpose(tmpCopyRCP->view_host(), dm.view_host());
313 tmpCopyRCP->clear_sync_state();
314 tmpCopyRCP->modify_host();
315 }
316 else {
317 kokkos_transpose(tmpCopyRCP->view_device(), dm.view_device());
318 tmpCopyRCP->clear_sync_state();
319 tmpCopyRCP->modify_device();
320 }
321 }
322 else {
323 tmpCopyRCP = Teuchos::rcp(new DM
324 (Kokkos::view_alloc(Kokkos::WithoutInitializing,"BelosDenseCreateCopy"),dm.extent_int(0),dm.extent_int(1)));
325 Kokkos::deep_copy(*tmpCopyRCP, dm);
326 }
327 return tmpCopyRCP;
328 }
329
340 static Scalar* GetRawHostPtr(DM & dm ) {
341 dm.sync_host();
342 dm.modify_host();
343 return reinterpret_cast<Scalar*>(dm.view_host().data());
344 //TODO: Is there any way that the user could hold on to this pointer...
345 // and everything works fine the first time they pass to LAPACK.
346 // But then... they call MvTimesMatAddMv which syncs to device.
347 // But then they keep the same pointer and pass to LAPACK again.
348 // Then they call MvTimesMatAddMv... but since they didn't call this
349 // function again, we miss the sync... See thread with Heidi on this.
350 }
351
353 static Scalar const * GetConstRawHostPtr(const DM & dm ) {
354 // CAG: This is a bit naughty.
355 const_cast<DM*>(&dm)->sync_host();
356 return reinterpret_cast<Scalar const *>(dm.view_host().data());
357 }
358
360 // Row and column indexing is zero-based.
361 static Teuchos::RCP<DM>
362 Subview( DM & source, int numRows, int numCols, int startRow=0, int startCol=0){
363 return Teuchos::rcp(new DM(source,
364 Kokkos::pair<int,int>(startRow,startRow+numRows), Kokkos::pair<int,int>(startCol,startCol+numCols)));
365 }
366
367 static Teuchos::RCP<const DM>
368 SubviewConst( const DM& source, int numRows, int numCols, int startRow=0, int startCol=0){
369 return Teuchos::rcp(new DM(source,
370 Kokkos::pair<int,int>(startRow,startRow+numRows), Kokkos::pair<int,int>(startCol,startCol+numCols)));
371 }
372
374 static Teuchos::RCP<DM>
375 SubviewCopy( const DM& source, int numRows, int numCols, int startRow=0, int startCol=0){
376 //Maaybe we could get away with just a host copy here??
377 //Hmmm... but it says we return a dual view.
378 //Maybe it should return a dual view with only host data copied in. Require sync to work on device.
379 //This is related to the functionality of the Assign function.
380 auto tmpViewRCP = Teuchos::rcp(new DM
381 (Kokkos::view_alloc(Kokkos::WithoutInitializing,"BelosDenseSubViewCopy"),numRows,numCols));
382 // I am keeping this where it copies the whole view on host and device because:
383 // a) I feel like we might be inviting some weird bugs later if we don't.
384 // But TODO Clarify to developer that this function needs to work on both host and device.
385 // b) It's not a host-device copy or vice versa. Its a copy from device to same device and from host to host.
386 // So shouldn't add much extra overhead.
387 Kokkos::deep_copy(*tmpViewRCP, Kokkos::subview(source,
388 Kokkos::pair<int,int>(startRow,startRow+numRows), Kokkos::pair<int,int>(startCol,startCol+numCols)));
389 return tmpViewRCP;
390 }
392
394
396 static int GetNumRows( const DM& dm ) {
397 return dm.extent_int(0);
398 }
399
401 static int GetNumCols( const DM& dm ) {
402 return dm.extent_int(1);
403 }
404
406 static int GetStride( const DM& dm ) {
407 // Note: We force LayoutLeft, which is column major, so the stride_0 is always 1.
408 // (This is the distance between two elts in same col, different rows.)
409 // Lapack wants stride_1, the distance from one col to the next col if we stay
410 // in the same row.
411 int strides[8]; // There are 8 possible strides and all will be returned
412 dm.stride(strides);
413 return strides[1];
414 //return dm.stride_1(); //This shortcut doesn't work for dualView.
415 }
416
418
420
421 /* \brief Reshaping method for changing the size of \c dm to have \c numRows rows and \c numCols columns.
422 * All values will be initialized to zero if the final argument is true.
423 * If the final argument is fale, the previous entries in
424 * the matrix will be maintained. For new entries that did not exist in the previous matrix, values will
425 * contain noise from memory.
426 */
427 static void Reshape( DM& dm, const int numRows, const int numCols, bool initZero = false) {
428 if(initZero){
429 dm.realloc(numRows,numCols);
430 Kokkos::deep_copy(dm.view_device(), 0.0);
431 dm.modify_device();
432 } else{
433 dm.resize(numRows,numCols); //keeps values in old array.
434 }
435 }
436
438
440
442 static Scalar & Value( DM& dm, const int i, const int j )
443 {
444 // Mark as modified on host, since we don't know if it will be.
445 dm.sync_host();
446 dm.modify_host();
447 return reinterpret_cast<Scalar&>((dm.view_host())(i,j));
448 }
449
451 static const Scalar & ValueConst( const DM& dm, const int i, const int j ) {
452 // CAG: This is a bit naughty.
453 const_cast<DM*>(&dm)->sync_host();
454 return reinterpret_cast<Scalar const &>((dm.view_host())(i,j));
455 }
456
458 //
459 // \note The only Belos function that results in a need to sync to
460 // host is MvTransMv. You MUST call SyncDeviceToHost before calling
461 // any other DenseMatTraits functions after a call to MvTransMv.
462 // All DenseMatTraits functions assume the necessary data is on host
463 // and perform computations only on the host.
464 //
465 static void SyncDeviceToHost(DM & dm) {
466 if(dm.need_sync_host()){
467 if(dm.view_host().span_is_contiguous() && dm.view_device().span_is_contiguous()){
468 dm.sync_host();}
469 else{
470 DM compat_view("compat view",dm.extent_int(0),dm.extent_int(1));
471 Kokkos::deep_copy(compat_view,dm);
472 compat_view.sync_host();
473 Kokkos::deep_copy(dm,compat_view);
474 dm.clear_sync_state();
475 }
476 }
477 }
478
479 static void SyncHostToDevice(DM & dm) {
480 if(dm.need_sync_device()){
481 if(dm.view_host().span_is_contiguous() && dm.view_device().span_is_contiguous()){
482 dm.sync_device();
483 }
484 else{
485 DM compat_view("compat view",dm.extent_int(0),dm.extent_int(1));
486 Kokkos::deep_copy(compat_view,dm);
487 compat_view.sync_device();
488 Kokkos::deep_copy(dm,compat_view);
489 dm.clear_sync_state();
490 }
491 }
492 }
494
495
497 static void Add( DM& thisDM, const DM& sourceDM) {
498 thisDM.sync_device();
499 // CAG: This is a bit naughty.
500 const_cast<DM*>(&sourceDM)->sync_device();
501 KokkosBlas::axpy(1.0,sourceDM.view_device(), thisDM.view_device()); //axpy(alpha,x,y), y = y + alpha*x
502 thisDM.modify_device();
503 }
504
506 static void PutScalar( DM& dm, Scalar value = Teuchos::ScalarTraits<Scalar>::zero()){
507 dm.clear_sync_state();
508 Kokkos::deep_copy( dm.view_device(), value);
509 dm.modify_device();
510 }
511
513 static void Scale( DM& dm, Scalar value) {
514 dm.sync_device();
515 KokkosBlas::scal( dm.view_device(), value, dm.view_device());
516 dm.modify_device();
517 }
518
521 static void Randomize( DM& dm) {
522 int rand_seed = std::rand();
523 Kokkos::Random_XorShift64_Pool<> pool(rand_seed);
524 dm.clear_sync_state();
525 Kokkos::fill_random(dm.view_device(), pool, -1,1);
526 dm.modify_device();
527 }
528
530 static void Assign( DM& dest, const DM& source) {
531 Kokkos::deep_copy(dest,source);
532 }
533
535 static typename Teuchos::ScalarTraits<Scalar>::magnitudeType NormFrobenius(const DM& dm) {
536 using KAT = KokkosKernels::ArithTraits<IST>;
537 using mag_t = typename KAT::mag_type;
538 // CAG: This is a bit naughty.
539 const_cast<DM*>(&dm)->sync_device();
541 Kokkos::parallel_reduce(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {dm.extent(0), dm.extent(1)}),
542 KOKKOS_LAMBDA(size_t i, size_t j, mag_t& lfrobNorm)
543 {
544 mag_t absVal = KAT::abs((dm.view_device())(i, j));
546 }, frobNorm);
547 return Kokkos::sqrt(frobNorm);
548 }
549
551 static typename Teuchos::ScalarTraits<Scalar>::magnitudeType NormOne(const DM& dm) {
552 using KAT = KokkosKernels::ArithTraits<IST>;
553 using mag_t = typename KAT::mag_type;
554 // CAG: This is a bit naughty.
555 const_cast<DM*>(&dm)->sync_device();
556 mag_t max_sum = 0;
557
558 Kokkos::parallel_reduce(dm.extent(1), KOKKOS_LAMBDA(const int j, mag_t& norm) {
559 mag_t sum = 0;
560 for(int i = 0; i < dm.extent_int(0); i++){ //rows
561 sum += KAT::abs((dm.view_device())(i,j));
562 }
563 norm = Kokkos::max(norm, sum);
564 }, Kokkos::Max<mag_t>(max_sum));
565 return KAT::abs(max_sum);
566 }
568
570
572 static Teuchos::RCP<DenseSolver<Scalar, DM>>
574
575 Teuchos::RCP<DenseSolver<Scalar, DM>> newSolver
576 = Teuchos::rcp( new KokkosDenseSolver<Scalar, DM>() );
577 return newSolver;
578 }
580
581 };
582
583} // namespace Belos
584
585#endif // end file BELOS_KOKKOS_DENSE_MAT_TRAITS_HPP
static Teuchos::RCP< DM > Subview(DM &source, int numRows, int numCols, int startRow=0, int startCol=0)
Returns an RCP to a Kokkos::DualView which has a subview of the given Kokkos::DualView.
static Teuchos::RCP< DM > SubviewCopy(const DM &source, int numRows, int numCols, int startRow=0, int startCol=0)
Returns a deep copy of the requested subview.
static Teuchos::RCP< DenseSolver< Scalar, DM > > createDenseSolver()
Returns a dense solver object for the dense matrix.
static Teuchos::RCP< DM > Create(const int numRows, const int numCols, bool initZero=true)
Creates a new empty Kokkos::DualView containing and numRows rows and numCols columns....
static Teuchos::RCP< DM > CreateCopy(const DM &dm, bool transpose=false)
Create a new copy DM, possibly transposed.
static const Scalar & ValueConst(const DM &dm, const int i, const int j)
Access a const reference to the (i,j) entry of dm, e_i^T dm e_j.
static Scalar & Value(DM &dm, const int i, const int j)
Access a reference to the (i,j) entry of dm, e_i^T dm e_j.
static Teuchos::RCP< const DM > SubviewConst(const DM &source, int numRows, int numCols, int startRow=0, int startCol=0)
static Teuchos::ScalarTraits< Scalar >::magnitudeType NormOne(const DM &dm)
Returns the one-norm of the dense matrix.
static void PutScalar(DM &dm, Scalar value=Teuchos::ScalarTraits< Scalar >::zero())
Fill all entries with value. Value is zero if not specified.
static void Add(DM &thisDM, const DM &sourceDM)
Adds sourceDM to thisDM and returns answer in thisDM.
static Teuchos::ScalarTraits< Scalar >::magnitudeType NormFrobenius(const DM &dm)
Returns the Frobenius norm of the dense matrix.
Virtual base class which defines basic traits for the multi-vector type.
void setSPD(bool flag)
Set if dense matrix is symmetric positive definite.
void solveWithTransposeFlag(Teuchos::ETransp trans)
All subsequent function calls will work with the transpose-type set by this method (Teuchos::NO_TRANS...
virtual int setMatrix(const Teuchos::RCP< DM > &A)
Sets the pointers for coefficient matrix.
virtual int setVectors(const Teuchos::RCP< DM > &X, const Teuchos::RCP< DM > &B)
Sets the pointers for left and right hand side vector(s).
void factorWithEquilibration(bool flag)
Causes equilibration to be called just before the matrix factorization as part of the call to factor.
Full specialization of Belos::DenseMatSolver for Kokkos::DualView.
virtual ~KokkosDenseSolver()
KokkosDenseSolver destructor.
int solve()
Computes the solution X to AX = B for the this matrix and the B provided.
int factor()
Computes the in-place LU factorization of the matrix.
KokkosDenseSolver()
Default constructor; matrix should be set using setMatrix(), LHS and RHS set with setVectors().
KokkosKernels::ArithTraits< Scalar >::val_type IST
DenseMatTraits< Scalar, DM > DMT
Alternative run-time polymorphic interface for operators.
void kokkos_transpose(const V &dst, const V &src)
Helper function for copying Kokkos::DualView into conjugate Kokkos::DualView.

Generated for Belos by doxygen 1.9.8