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 
25 namespace 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  }
183 
184  template < class Matrix >
185  template < class KV >
187  {
188  return static_cast<const adapter_t*>(this)->getSparseRowPtr_kokkos_view(view);
189  }
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 >
201  {
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());
226  }
227 
228  template < class Matrix >
229  template<typename KV_S, typename KV_GO, typename KV_GS>
230  void
231  MatrixAdapter<Matrix>::do_getCrs_kokkos_view(KV_S & nzval,
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,
237  EStorage_Ordering ordering,
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::HostMirror 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 &recvCounts, host_ordinal_type_array &recvDispls,
529  host_ordinal_type_array &transpose_map, host_scalar_type_array &nzvals_t,
530  bool column_major, EPhase current_phase) const
531  {
532  return static_cast<const adapter_t*>(this)->gather_impl(nzvals, indices, pointers, recvCounts, recvDispls, transpose_map, nzvals_t,
533  column_major, current_phase);
534  }
535 
536  template <class Matrix>
537  Teuchos::RCP<MatrixAdapter<Matrix> >
538  createMatrixAdapter(Teuchos::RCP<Matrix> m){
539  using Teuchos::rcp;
540  using Teuchos::rcp_const_cast;
541 
542  if(m.is_null()) return Teuchos::null;
543  return( rcp(new ConcreteMatrixAdapter<Matrix>(m)) );
544  }
545 
546  template <class Matrix>
547  Teuchos::RCP<const MatrixAdapter<Matrix> >
548  createConstMatrixAdapter(Teuchos::RCP<const Matrix> m){
549  using Teuchos::rcp;
550  using Teuchos::rcp_const_cast;
551 
552  if(m.is_null()) return Teuchos::null;
553  return( rcp(new ConcreteMatrixAdapter<Matrix>(rcp_const_cast<Matrix,const Matrix>(m))).getConst() );
554  }
555 
556 } // end namespace Amesos2
557 
558 #endif // AMESOS2_MATRIXADAPTER_DEF_HPP
EPhase
Used to indicate a phase in the direct solution.
Definition: Amesos2_TypeDecl.hpp:31
EStorage_Ordering
Definition: Amesos2_TypeDecl.hpp:107
void transpose(ArrayView< Scalar > vals, ArrayView< GlobalOrdinal > indices, ArrayView< GlobalSizeT > ptr, ArrayView< Scalar > trans_vals, ArrayView< GlobalOrdinal > trans_indices, ArrayView< GlobalSizeT > trans_ptr)
std::string description() const
Returns a short description of this Solver.
Definition: Amesos2_MatrixAdapter_def.hpp:167
A Matrix adapter interface for Amesos2.
Definition: Amesos2_MatrixAdapter_decl.hpp:42
Indicates that the concrete class can use the generic getC{c|r}s methods implemented in MatrixAdapter...
Definition: Amesos2_TypeDecl.hpp:57
EDistribution
Definition: Amesos2_TypeDecl.hpp:89