Amesos2 - Direct Sparse Solver Interfaces Version of the Day
Amesos2_MatrixAdapter_def.hpp
1// @HEADER
2// *****************************************************************************
3// Amesos2: Templated Direct Sparse Solver Package
4//
5// Copyright 2011 NTESS and the Amesos2 contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10
11#ifndef AMESOS2_MATRIXADAPTER_DEF_HPP
12#define AMESOS2_MATRIXADAPTER_DEF_HPP
13#include <Tpetra_Util.hpp>
14#include "Amesos2_MatrixAdapter_decl.hpp"
15#include "Amesos2_ConcreteMatrixAdapter_def.hpp"
16//#include "Amesos2_ConcreteMatrixAdapter.hpp"
17
18#define TESTING_AMESOS2_WITH_TPETRA_REMOVE_UVM
19#if defined(TESTING_AMESOS2_WITH_TPETRA_REMOVE_UVM)
20#include "KokkosSparse_Utils.hpp"
21#include "KokkosSparse_SortCrs.hpp"
22#include "KokkosKernels_Sorting.hpp"
23#endif
24
25namespace Amesos2 {
26
27
28 template < class Matrix >
29 MatrixAdapter<Matrix>::MatrixAdapter(const Teuchos::RCP<Matrix> m)
30 : mat_(m)
31 {
32 comm_ = static_cast<const adapter_t*>(this)->getComm_impl();
33 col_map_ = static_cast<const adapter_t*>(this)->getColMap_impl();
34 row_map_ = static_cast<const adapter_t*>(this)->getRowMap_impl();
35 }
36
37 template < class Matrix >
38 template<typename KV_S, typename KV_GO, typename KV_GS>
39 void
40 MatrixAdapter<Matrix>::getCrs_kokkos_view(KV_S & nzval,
41 KV_GO & colind,
42 KV_GS & rowptr,
43 typename MatrixAdapter<Matrix>::global_size_t& nnz,
44 const Teuchos::Ptr<const map_t> rowmap,
45 EStorage_Ordering ordering,
46 EDistribution distribution) const
47 {
48 help_getCrs_kokkos_view(nzval, colind, rowptr,
49 nnz, rowmap, distribution, ordering,
50 typename adapter_t::get_crs_spec());
51 }
52
53 template < class Matrix >
54 template<typename KV_S, typename KV_GO, typename KV_GS>
55 void
56 MatrixAdapter<Matrix>::getCrs_kokkos_view(KV_S & nzval,
57 KV_GO & colind,
58 KV_GS & rowptr,
59 typename MatrixAdapter<Matrix>::global_size_t& nnz,
60 EDistribution distribution,
61 EStorage_Ordering ordering) const
62 {
63 const Teuchos::RCP<const map_t> rowmap
64 = Util::getDistributionMap<local_ordinal_t,global_ordinal_t,global_size_t,node_t>(distribution,
65 this->getGlobalNumRows(),
66 this->getComm());
67 this->getCrs_kokkos_view(nzval, colind, rowptr, nnz, Teuchos::ptrInArg(*rowmap), ordering, distribution);
68 }
69
70 template < class Matrix >
71 template<typename KV_S, typename KV_GO, typename KV_GS>
72 void
73 MatrixAdapter<Matrix>::getCcs_kokkos_view(KV_S & nzval,
74 KV_GO & rowind,
75 KV_GS & colptr,
76 typename MatrixAdapter<Matrix>::global_size_t& nnz,
77 const Teuchos::Ptr<const map_t> colmap,
78 EStorage_Ordering ordering,
79 EDistribution distribution) const
80 {
81 help_getCcs_kokkos_view(nzval, rowind, colptr,
82 nnz, colmap, distribution, ordering,
83 typename adapter_t::get_ccs_spec());
84 }
85
86 template < class Matrix >
87 template<typename KV_S, typename KV_GO, typename KV_GS>
88 void
89 MatrixAdapter<Matrix>::getCcs_kokkos_view(KV_S & nzval,
90 KV_GO & rowind,
91 KV_GS & colptr,
92 typename MatrixAdapter<Matrix>::global_size_t& nnz,
93 EDistribution distribution,
94 EStorage_Ordering ordering) const
95 {
96 const Teuchos::RCP<const map_t> colmap
97 = Util::getDistributionMap<local_ordinal_t,global_ordinal_t,global_size_t,node_t>(distribution,
98 this->getGlobalNumCols(),
99 this->getComm());
100 this->getCcs_kokkos_view(nzval, rowind, colptr, nnz, Teuchos::ptrInArg(*colmap), ordering, distribution);
101 }
102
103
104 template < class Matrix >
105 typename MatrixAdapter<Matrix>::global_size_t
107 {
108 return static_cast<const adapter_t*>(this)->getGlobalNumRows_impl();
109 }
110
111 template < class Matrix >
112 typename MatrixAdapter<Matrix>::global_size_t
114 {
115 return static_cast<const adapter_t*>(this)->getGlobalNumCols_impl();
116 }
117
118 template < class Matrix >
119 typename MatrixAdapter<Matrix>::global_size_t
121 {
122 // Kokkos adapter is for serial only testing right now and will not
123 // create row_map_
124 return (row_map_ != Teuchos::null) ? row_map_->getIndexBase() : 0;
125 }
126
127 template < class Matrix >
128 typename MatrixAdapter<Matrix>::global_size_t
130 {
131 // Kokkos adapter is for serial only testing right now and will not
132 // create col_map_
133 return (col_map_ != Teuchos::null) ? col_map_->getIndexBase() : 0;
134 }
135
136 template < class Matrix >
137 typename MatrixAdapter<Matrix>::global_size_t
139 {
140 return static_cast<const adapter_t*>(this)->getGlobalNNZ_impl();
141 }
142
143 template < class Matrix >
144 size_t
146 {
147 return row_map_->getLocalNumElements(); // TODO: verify
148 }
149
150 template < class Matrix >
151 size_t
153 {
154 return col_map_->getLocalNumElements();
155 }
156
157 template < class Matrix >
158 size_t
160 {
161 return static_cast<const adapter_t*>(this)->getLocalNNZ_impl();
162 }
163
164 // NDE: This is broken for Epetra_CrsMatrix
165 template < class Matrix >
166 std::string
168 {
169 std::ostringstream oss;
170 oss << "Amesos2::MatrixAdapter wrapping: ";
171 oss << mat_->description(); // NDE: This is not defined in Epetra_CrsMatrix, only in Tpetra::CrsMatrix
172 return oss.str();
173 }
174
175 template < class Matrix >
176 void
177 MatrixAdapter<Matrix>::describe(Teuchos::FancyOStream &out,
178 const Teuchos::EVerbosityLevel verbLevel) const
179 {
180 // (implemented for Epetra::CrsMatrix & Tpetra::CrsMatrix)
181 return static_cast<const adapter_t*>(this)->describe(out, verbLevel);
182 }
184 template < class Matrix >
185 template < class KV >
187 {
188 return static_cast<const adapter_t*>(this)->getSparseRowPtr_kokkos_view(view);
190
191 template < class Matrix >
192 template < class KV >
194 {
195 return static_cast<const adapter_t*>(this)->getSparseColInd_kokkos_view(view);
196 }
197
198 template < class Matrix >
199 template < class KV >
202 return static_cast<const adapter_t*>(this)->getSparseValues_kokkos_view(view);
203 }
204
205 /******************************
206 * Private method definitions *
207 ******************************/
208 template < class Matrix >
209 template<typename KV_S, typename KV_GO, typename KV_GS>
210 void
212 KV_GO & colind,
213 KV_GS & rowptr,
214 typename MatrixAdapter<Matrix>::global_size_t& nnz,
215 const Teuchos::Ptr<const map_t> rowmap,
216 EDistribution distribution,
217 EStorage_Ordering ordering,
218 no_special_impl nsi) const
219 {
220
221 //Added void to remove parameter not used warning
222 ((void)nsi);
223 do_getCrs_kokkos_view(nzval, colind, rowptr,
224 nnz, rowmap, distribution, ordering,
225 typename adapter_t::major_access());
227
228 template < class Matrix >
229 template<typename KV_S, typename KV_GO, typename KV_GS>
230 void
232 KV_GO & colind,
233 KV_GS & rowptr,
234 typename MatrixAdapter<Matrix>::global_size_t& nnz,
235 const Teuchos::Ptr<const map_t> rowmap,
236 EDistribution distribution,
238 row_access ra) const
239 {
240 // Kokkos adapter will be serial and won't have the rowmap.
241 // Tacho for example wouldn't ever call this in serial but Cholmod will
242 // call ccs and want to convert using this.
243 // If the kokkos adapter is extended to multiple ranks then this will
244 // need to change.
245 if(this->row_map_ == Teuchos::null) {
246 this->returnValues_kokkos_view(nzval);
247 this->returnRowPtr_kokkos_view(rowptr);
248 this->returnColInd_kokkos_view(colind);
249 nnz = nzval.size();
250 return;
251 }
252
253 using Teuchos::rcp;
254 using Teuchos::RCP;
255 using Teuchos::ArrayView;
256 using Teuchos::OrdinalTraits;
257
258 ((void) ra);
259
260 RCP<const type> get_mat;
261 if( *rowmap == *this->row_map_ && distribution != CONTIGUOUS_AND_ROOTED ){
262 // No need to redistribute
263 get_mat = rcp(this,false); // non-owning
264 } else {
265 get_mat = get(rowmap, distribution);
266 }
267 // RCP<const type> get_mat = get(rowmap);
268
269 // rmap may not necessarily check same as rowmap because rmap may
270 // have been constructued with Tpetra's "expert" constructor,
271 // which assumes that the map points are non-contiguous.
272 //
273 // TODO: There may be some more checking between the row map
274 // compatibility, but things are working fine now.
275
276 RCP<const map_t> rmap = get_mat->getRowMap();
277 ArrayView<const global_ordinal_t> node_elements = rmap->getLocalElementList();
278 //if( node_elements.size() == 0 ) return; // no more contribution
279 typename ArrayView<const global_ordinal_t>::iterator row_it, row_end;
280 row_end = node_elements.end();
281
282 size_t rowptr_ind = OrdinalTraits<size_t>::zero();
283 global_ordinal_t rowInd = OrdinalTraits<global_ordinal_t>::zero();
284
285 // For rowptr we can just make a mirror and deep_copy at the end
286 typename KV_GS::host_mirror_type host_rowptr = Kokkos::create_mirror_view(rowptr);
287
288 #if !defined(TESTING_AMESOS2_WITH_TPETRA_REMOVE_UVM)
289 // Note nzval, colind, and rowptr will not all be in the same memory space.
290 // Currently only Cholmod exercises this code which has all the arrays on host,
291 // so this will need extension and testing when we have a solver using device here.
292 Kokkos::View<scalar_t*, Kokkos::HostSpace>
293 mat_nzval(Kokkos::ViewAllocateWithoutInitializing("mat_nzval"), nzval.size());
294
295 Kokkos::View<global_ordinal_t*, Kokkos::HostSpace>
296 mat_colind(Kokkos::ViewAllocateWithoutInitializing("mat_colind"), colind.size());
297
298 ArrayView<scalar_t> nzval_arrayview(mat_nzval.data(), nzval.size());
299 ArrayView<global_ordinal_t> colind_arrayview(mat_colind.data(), colind.size());
300
301 for( row_it = node_elements.begin(); row_it != row_end; ++row_it ){
302 host_rowptr(rowptr_ind++) = rowInd;
303 size_t rowNNZ = get_mat->getGlobalRowNNZ(*row_it);
304 size_t nnzRet = OrdinalTraits<size_t>::zero();
305 ArrayView<global_ordinal_t> colind_view = colind_arrayview.view(rowInd,rowNNZ);
306 ArrayView<scalar_t> nzval_view = nzval_arrayview.view(rowInd,rowNNZ);
307
308 get_mat->getGlobalRowCopy(*row_it, colind_view, nzval_view, nnzRet);
309
310 for (size_t rr = 0; rr < nnzRet ; rr++) {
311 colind_view[rr] -= rmap->getIndexBase();
312 }
313
314 // It was suggested that instead of sorting each row's indices
315 // individually, that we instead do a double-transpose at the
316 // end, which would also lead to the indices being sorted.
317 if( ordering == SORTED_INDICES ) {
318 Tpetra::sort2(colind_view.begin(), colind_view.end(), nzval_view.begin());
319 }
320
321 TEUCHOS_TEST_FOR_EXCEPTION( rowNNZ != nnzRet,
322 std::runtime_error,
323 "Number of values returned different from "
324 "number of values reported");
325 rowInd += rowNNZ;
326 }
327 host_rowptr(rowptr_ind) = nnz = rowInd;
328
329 deep_copy_or_assign_view(nzval, mat_nzval);
330 deep_copy_or_assign_view(colind, mat_colind);
331 deep_copy_or_assign_view(rowptr, host_rowptr);
332 #else
333 // create temporary views to hold colind and nvals (TODO: allocate as much as needed, also for rowptr)
334 global_host_idx_t mat_colind(Kokkos::ViewAllocateWithoutInitializing("mat_colind"), nzval.size());
335 global_host_val_t mat_nzvals(Kokkos::ViewAllocateWithoutInitializing("mat_nzvals"), colind.size());
336
337 auto host_colind = Kokkos::create_mirror_view(colind);
338 auto host_nzval = Kokkos::create_mirror_view(nzval);
339
340 // load crs (on host)
341 for( row_it = node_elements.begin(); row_it != row_end; ++row_it ){
342 size_t rowNNZ = get_mat->getGlobalRowNNZ(*row_it);
343 size_t nnzRet = OrdinalTraits<size_t>::zero();
344 //using range_type = Kokkos::pair<int, int>;
345 //auto colind_view = Kokkos::subview(mat_colind, range_type(rowInd, rowInd+rowNNZ));
346 //auto nzval_view = Kokkos::subview(mat_nzvals, range_type(rowInd, rowInd+rowNNZ));
347 global_host_idx_t colind_view (&(mat_colind(rowInd)), rowNNZ);
348 global_host_val_t nzvals_view (&(mat_nzvals(rowInd)), rowNNZ);
349
350 global_ordinal_t row_id = *row_it;
351 get_mat->getGlobalRowCopy_kokkos_view(row_id, colind_view, nzvals_view, nnzRet);
352
353 TEUCHOS_TEST_FOR_EXCEPTION( rowNNZ != nnzRet,
354 std::runtime_error,
355 "Number of values returned different from "
356 "number of values reported");
357 host_rowptr(rowptr_ind++) = rowInd;
358 rowInd += rowNNZ;
359 }
360 host_rowptr(rowptr_ind) = nnz = rowInd;
361
362 // fix index-base
363 if (rmap->getIndexBase() != 0) {
364 for (size_t k = 0; k < mat_colind.extent(0); k++) {
365 mat_colind(k) -= rmap->getIndexBase();
366 }
367 }
368
369 // copy to device (note: everything in the vectors are copied, though they may not be used)
370 deep_copy_or_assign_view(nzval, mat_nzvals);
371 deep_copy_or_assign_view(colind, mat_colind);
372 deep_copy_or_assign_view(rowptr, host_rowptr);
373
374 // sort
375 if( ordering == SORTED_INDICES ) {
376 using execution_space = typename KV_GS::execution_space;
377 KokkosSparse::sort_crs_matrix <execution_space, KV_GS, KV_GO, KV_S>
378 (rowptr, colind, nzval);
379 }
380 #endif
381 }
382
383 template < class Matrix >
384 template<typename KV_S, typename KV_GO, typename KV_GS>
385 void
386 MatrixAdapter<Matrix>::help_getCcs_kokkos_view(KV_S & nzval,
387 KV_GO & rowind,
388 KV_GS & colptr,
389 typename MatrixAdapter<Matrix>::global_size_t& nnz,
390 const Teuchos::Ptr<const map_t> colmap,
391 EDistribution distribution,
392 EStorage_Ordering ordering,
393 no_special_impl nsi) const
394 {
395
396 //Added void to remove parameter not used warning
397 ((void)nsi);
398 do_getCcs_kokkos_view(nzval, rowind, colptr,
399 nnz, colmap, distribution, ordering,
400 typename adapter_t::major_access());
401 }
402
403 template < class Matrix >
404 template<typename KV_S, typename KV_GO, typename KV_GS>
405 void
406 MatrixAdapter<Matrix>::do_getCcs_kokkos_view(KV_S & nzval,
407 KV_GO & rowind,
408 KV_GS & colptr,
409 typename MatrixAdapter<Matrix>::global_size_t& nnz,
410 const Teuchos::Ptr<const map_t> colmap,
411 EDistribution distribution,
412 EStorage_Ordering ordering,
413 row_access ra) const
414 {
415 using Teuchos::ArrayView;
416 // get the crs and transpose
417
418 ((void) ra);
419
420 KV_S nzval_tmp(Kokkos::ViewAllocateWithoutInitializing("nzval_tmp"), nzval.size());
421 KV_GO colind(Kokkos::ViewAllocateWithoutInitializing("colind"), rowind.size());
422 KV_GS rowptr(Kokkos::ViewAllocateWithoutInitializing("rowptr"), this->getGlobalNumRows() + 1);
423
424 this->getCrs_kokkos_view(nzval_tmp, colind, rowptr, nnz, colmap, ordering, distribution);
425
426 if(nnz > 0) {
427 // This is currently just used by Cholmod in which case the views will be
428 // host, even if Cholmod is using GPU. Will need to upgrade this section
429 // to properly handle device when we have a solver that needs it.
430 ArrayView<typename KV_S::value_type> av_nzval_tmp(nzval_tmp.data(), nzval_tmp.size());
431 ArrayView<typename KV_GO::value_type> av_colind(colind.data(), colind.size());
432 ArrayView<typename KV_GS::value_type> av_rowptr(rowptr.data(), rowptr.size());
433 ArrayView<typename KV_S::value_type> av_nzval(nzval.data(), nzval.size());
434 ArrayView<typename KV_GO::value_type> av_rowind(rowind.data(), rowind.size());
435 ArrayView<typename KV_GS::value_type> av_colptr(colptr.data(), colptr.size());
436 Util::transpose(av_nzval_tmp, av_colind, av_rowptr, av_nzval, av_rowind, av_colptr);
437 }
438 }
439
440 // These will link to concrete implementations
441 template < class Matrix >
442 template<typename KV_GO, typename KV_S>
443 void
445 KV_GO & indices,
446 KV_S & vals,
447 size_t& nnz) const
448 {
449 static_cast<const adapter_t*>(this)->getGlobalRowCopy_kokkos_view_impl(row, indices, vals, nnz);
450 }
451
452 template < class Matrix >
453 size_t
455 {
456 return static_cast<const adapter_t*>(this)->getMaxRowNNZ_impl();
457 }
458
459 template < class Matrix >
460 size_t
461 MatrixAdapter<Matrix>::getMaxColNNZ() const
462 {
463 return static_cast<const adapter_t*>(this)->getMaxColNNZ_impl();
464 }
465
466 template < class Matrix >
467 size_t
468 MatrixAdapter<Matrix>::getGlobalRowNNZ(global_ordinal_t row) const
469 {
470 return static_cast<const adapter_t*>(this)->getGlobalRowNNZ_impl(row);
471 }
472
473 template < class Matrix >
474 size_t
475 MatrixAdapter<Matrix>::getLocalRowNNZ(local_ordinal_t row) const
476 {
477 return static_cast<const adapter_t*>(this)->getLocalRowNNZ_impl(row);
478 }
479
480 template < class Matrix >
481 size_t
482 MatrixAdapter<Matrix>::getGlobalColNNZ(global_ordinal_t col) const
483 {
484 return static_cast<const adapter_t*>(this)->getGlobalColNNZ_impl(col);
485 }
486
487 template < class Matrix >
488 size_t
489 MatrixAdapter<Matrix>::getLocalColNNZ(local_ordinal_t col) const
490 {
491 return static_cast<const adapter_t*>(this)->getLocalColNNZ_impl(col);
492 }
493
494 template < class Matrix >
495 bool
496 MatrixAdapter<Matrix>::isLocallyIndexed() const
497 {
498 return static_cast<const adapter_t*>(this)->isLocallyIndexed_impl();
499 }
500
501 template < class Matrix >
502 bool
503 MatrixAdapter<Matrix>::isGloballyIndexed() const
504 {
505 return static_cast<const adapter_t*>(this)->isGloballyIndexed_impl();
506 }
507
508
509 template < class Matrix >
510 Teuchos::RCP<const MatrixAdapter<Matrix> >
511 MatrixAdapter<Matrix>::get(const Teuchos::Ptr<const map_t> map, EDistribution distribution) const
512 {
513 return static_cast<const adapter_t*>(this)->get_impl(map, distribution);
514 }
515
516
517 template < class Matrix >
518 Teuchos::RCP<const MatrixAdapter<Matrix> >
519 MatrixAdapter<Matrix>::reindex(Teuchos::RCP<const map_t> &contigRowMap, Teuchos::RCP<const map_t> &contigColMap, const EPhase current_phase) const
520 {
521 return static_cast<const adapter_t*>(this)->reindex_impl(contigRowMap, contigColMap, current_phase);
522 }
523
524 template < class Matrix >
525 template<typename KV_S, typename KV_GO, typename KV_GS, typename host_ordinal_type_array, typename host_scalar_type_array>
526 typename MatrixAdapter<Matrix>::local_ordinal_t
527 MatrixAdapter<Matrix>::gather(KV_S& nzvals, KV_GO& indices, KV_GS& pointers,
528 host_ordinal_type_array &perm_g2l,
529 host_ordinal_type_array &recvCountRows, host_ordinal_type_array &recvDisplRows,
530 host_ordinal_type_array &recvCounts, host_ordinal_type_array &recvDispls,
531 host_ordinal_type_array &transpose_map, host_scalar_type_array &nzvals_t,
532 bool column_major, EPhase current_phase) const
533 {
534 return static_cast<const adapter_t*>(this)->gather_impl(nzvals, indices, pointers, perm_g2l, recvCountRows, recvDisplRows, recvCounts, recvDispls,
535 transpose_map, nzvals_t, column_major, current_phase);
536 }
537
538 template <class Matrix>
539 Teuchos::RCP<MatrixAdapter<Matrix> >
540 createMatrixAdapter(Teuchos::RCP<Matrix> m){
541 using Teuchos::rcp;
542 using Teuchos::rcp_const_cast;
543
544 if(m.is_null()) return Teuchos::null;
545 return( rcp(new ConcreteMatrixAdapter<Matrix>(m)) );
546 }
547
548 template <class Matrix>
549 Teuchos::RCP<const MatrixAdapter<Matrix> >
550 createConstMatrixAdapter(Teuchos::RCP<const Matrix> m){
551 using Teuchos::rcp;
552 using Teuchos::rcp_const_cast;
553
554 if(m.is_null()) return Teuchos::null;
555 return( rcp(new ConcreteMatrixAdapter<Matrix>(rcp_const_cast<Matrix,const Matrix>(m))).getConst() );
556 }
557
558} // end namespace Amesos2
559
560#endif // AMESOS2_MATRIXADAPTER_DEF_HPP
EDistribution
Definition Amesos2_TypeDecl.hpp:89
EStorage_Ordering
Definition Amesos2_TypeDecl.hpp:107
A Matrix adapter interface for Amesos2.
Definition Amesos2_MatrixAdapter_decl.hpp:42
std::string description() const
Returns a short description of this Solver.
Definition Amesos2_MatrixAdapter_def.hpp:167
EPhase
Used to indicate a phase in the direct solution.
Definition Amesos2_TypeDecl.hpp:31
Indicates that the concrete class can use the generic getC{c|r}s methods implemented in MatrixAdapter...
Definition Amesos2_TypeDecl.hpp:57
Indicates that the object of an adapter provides row access to its data.
Definition Amesos2_TypeDecl.hpp:66