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 Tpetra::Map<local_ordinal_t, global_ordinal_t, node_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 Tpetra::Map<local_ordinal_t,global_ordinal_t,node_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 Tpetra::Map<local_ordinal_t, global_ordinal_t, node_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 Tpetra::Map<local_ordinal_t,global_ordinal_t,node_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 
181  template < class Matrix >
182  template < class KV >
184  {
185  return static_cast<const adapter_t*>(this)->getSparseRowPtr_kokkos_view(view);
186  }
187 
188  template < class Matrix >
189  template < class KV >
191  {
192  return static_cast<const adapter_t*>(this)->getSparseColInd_kokkos_view(view);
193  }
194 
195  template < class Matrix >
196  template < class KV >
198  {
199  return static_cast<const adapter_t*>(this)->getSparseValues_kokkos_view(view);
200  }
201 
202  /******************************
203  * Private method definitions *
204  ******************************/
205  template < class Matrix >
206  template<typename KV_S, typename KV_GO, typename KV_GS>
207  void
209  KV_GO & colind,
210  KV_GS & rowptr,
211  typename MatrixAdapter<Matrix>::global_size_t& nnz,
212  const Teuchos::Ptr<const Tpetra::Map<local_ordinal_t,global_ordinal_t,node_t> > rowmap,
213  EDistribution distribution,
214  EStorage_Ordering ordering,
215  no_special_impl nsi) const
216  {
217 
218  //Added void to remove parameter not used warning
219  ((void)nsi);
220  do_getCrs_kokkos_view(nzval, colind, rowptr,
221  nnz, rowmap, distribution, ordering,
222  typename adapter_t::major_access());
223  }
224 
225  template < class Matrix >
226  template<typename KV_S, typename KV_GO, typename KV_GS>
227  void
228  MatrixAdapter<Matrix>::do_getCrs_kokkos_view(KV_S & nzval,
229  KV_GO & colind,
230  KV_GS & rowptr,
231  typename MatrixAdapter<Matrix>::global_size_t& nnz,
232  const Teuchos::Ptr<const Tpetra::Map<local_ordinal_t,global_ordinal_t,node_t> > rowmap,
233  EDistribution distribution,
234  EStorage_Ordering ordering,
235  row_access ra) const
236  {
237  // Kokkos adapter will be serial and won't have the rowmap.
238  // Tacho for example wouldn't ever call this in serial but Cholmod will
239  // call ccs and want to convert using this.
240  // If the kokkos adapter is extended to multiple ranks then this will
241  // need to change.
242  if(this->row_map_ == Teuchos::null) {
243  this->returnValues_kokkos_view(nzval);
244  this->returnRowPtr_kokkos_view(rowptr);
245  this->returnColInd_kokkos_view(colind);
246  nnz = nzval.size();
247  return;
248  }
249 
250  using Teuchos::rcp;
251  using Teuchos::RCP;
252  using Teuchos::ArrayView;
253  using Teuchos::OrdinalTraits;
254 
255  ((void) ra);
256 
257  RCP<const type> get_mat;
258  if( *rowmap == *this->row_map_ && distribution != CONTIGUOUS_AND_ROOTED ){
259  // No need to redistribute
260  get_mat = rcp(this,false); // non-owning
261  } else {
262  get_mat = get(rowmap, distribution);
263  }
264  // RCP<const type> get_mat = get(rowmap);
265 
266  // rmap may not necessarily check same as rowmap because rmap may
267  // have been constructued with Tpetra's "expert" constructor,
268  // which assumes that the map points are non-contiguous.
269  //
270  // TODO: There may be some more checking between the row map
271  // compatibility, but things are working fine now.
272 
273  RCP<const Tpetra::Map<local_ordinal_t,global_ordinal_t,node_t> > rmap = get_mat->getRowMap();
274  ArrayView<const global_ordinal_t> node_elements = rmap->getLocalElementList();
275  //if( node_elements.size() == 0 ) return; // no more contribution
276  typename ArrayView<const global_ordinal_t>::iterator row_it, row_end;
277  row_end = node_elements.end();
278 
279  size_t rowptr_ind = OrdinalTraits<size_t>::zero();
280  global_ordinal_t rowInd = OrdinalTraits<global_ordinal_t>::zero();
281 
282  // For rowptr we can just make a mirror and deep_copy at the end
283  typename KV_GS::HostMirror host_rowptr = Kokkos::create_mirror_view(rowptr);
284 
285  #if !defined(TESTING_AMESOS2_WITH_TPETRA_REMOVE_UVM)
286  // Note nzval, colind, and rowptr will not all be in the same memory space.
287  // Currently only Cholmod exercises this code which has all the arrays on host,
288  // so this will need extension and testing when we have a solver using device here.
289  Kokkos::View<scalar_t*, Kokkos::HostSpace>
290  mat_nzval(Kokkos::ViewAllocateWithoutInitializing("mat_nzval"), nzval.size());
291 
292  Kokkos::View<global_ordinal_t*, Kokkos::HostSpace>
293  mat_colind(Kokkos::ViewAllocateWithoutInitializing("mat_colind"), colind.size());
294 
295  ArrayView<scalar_t> nzval_arrayview(mat_nzval.data(), nzval.size());
296  ArrayView<global_ordinal_t> colind_arrayview(mat_colind.data(), colind.size());
297 
298  for( row_it = node_elements.begin(); row_it != row_end; ++row_it ){
299  host_rowptr(rowptr_ind++) = rowInd;
300  size_t rowNNZ = get_mat->getGlobalRowNNZ(*row_it);
301  size_t nnzRet = OrdinalTraits<size_t>::zero();
302  ArrayView<global_ordinal_t> colind_view = colind_arrayview.view(rowInd,rowNNZ);
303  ArrayView<scalar_t> nzval_view = nzval_arrayview.view(rowInd,rowNNZ);
304 
305  get_mat->getGlobalRowCopy(*row_it, colind_view, nzval_view, nnzRet);
306 
307  for (size_t rr = 0; rr < nnzRet ; rr++) {
308  colind_view[rr] -= rmap->getIndexBase();
309  }
310 
311  // It was suggested that instead of sorting each row's indices
312  // individually, that we instead do a double-transpose at the
313  // end, which would also lead to the indices being sorted.
314  if( ordering == SORTED_INDICES ) {
315  Tpetra::sort2(colind_view.begin(), colind_view.end(), nzval_view.begin());
316  }
317 
318  TEUCHOS_TEST_FOR_EXCEPTION( rowNNZ != nnzRet,
319  std::runtime_error,
320  "Number of values returned different from "
321  "number of values reported");
322  rowInd += rowNNZ;
323  }
324  host_rowptr(rowptr_ind) = nnz = rowInd;
325 
326  deep_copy_or_assign_view(nzval, mat_nzval);
327  deep_copy_or_assign_view(colind, mat_colind);
328  deep_copy_or_assign_view(rowptr, host_rowptr);
329  #else
330  // create temporary views to hold colind and nvals (TODO: allocate as much as needed, also for rowptr)
331  global_host_idx_t mat_colind(Kokkos::ViewAllocateWithoutInitializing("mat_colind"), nzval.size());
332  global_host_val_t mat_nzvals(Kokkos::ViewAllocateWithoutInitializing("mat_nzvals"), colind.size());
333 
334  auto host_colind = Kokkos::create_mirror_view(colind);
335  auto host_nzval = Kokkos::create_mirror_view(nzval);
336 
337  // load crs (on host)
338  for( row_it = node_elements.begin(); row_it != row_end; ++row_it ){
339  size_t rowNNZ = get_mat->getGlobalRowNNZ(*row_it);
340  size_t nnzRet = OrdinalTraits<size_t>::zero();
341  //using range_type = Kokkos::pair<int, int>;
342  //auto colind_view = Kokkos::subview(mat_colind, range_type(rowInd, rowInd+rowNNZ));
343  //auto nzval_view = Kokkos::subview(mat_nzvals, range_type(rowInd, rowInd+rowNNZ));
344  global_host_idx_t colind_view (&(mat_colind(rowInd)), rowNNZ);
345  global_host_val_t nzvals_view (&(mat_nzvals(rowInd)), rowNNZ);
346 
347  global_ordinal_t row_id = *row_it;
348  get_mat->getGlobalRowCopy_kokkos_view(row_id, colind_view, nzvals_view, nnzRet);
349 
350  TEUCHOS_TEST_FOR_EXCEPTION( rowNNZ != nnzRet,
351  std::runtime_error,
352  "Number of values returned different from "
353  "number of values reported");
354  host_rowptr(rowptr_ind++) = rowInd;
355  rowInd += rowNNZ;
356  }
357  host_rowptr(rowptr_ind) = nnz = rowInd;
358 
359  // fix index-base
360  if (rmap->getIndexBase() != 0) {
361  for (size_t k = 0; k < mat_colind.extent(0); k++) {
362  mat_colind(k) -= rmap->getIndexBase();
363  }
364  }
365 
366  // copy to device (note: everything in the vectors are copied, though they may not be used)
367  deep_copy_or_assign_view(nzval, mat_nzvals);
368  deep_copy_or_assign_view(colind, mat_colind);
369  deep_copy_or_assign_view(rowptr, host_rowptr);
370 
371  // sort
372  if( ordering == SORTED_INDICES ) {
373  using execution_space = typename KV_GS::execution_space;
374  KokkosSparse::sort_crs_matrix <execution_space, KV_GS, KV_GO, KV_S>
375  (rowptr, colind, nzval);
376  }
377  #endif
378  }
379 
380  template < class Matrix >
381  template<typename KV_S, typename KV_GO, typename KV_GS>
382  void
383  MatrixAdapter<Matrix>::help_getCcs_kokkos_view(KV_S & nzval,
384  KV_GO & rowind,
385  KV_GS & colptr,
386  typename MatrixAdapter<Matrix>::global_size_t& nnz,
387  const Teuchos::Ptr<const Tpetra::Map<local_ordinal_t,global_ordinal_t,node_t> > colmap,
388  EDistribution distribution,
389  EStorage_Ordering ordering,
390  no_special_impl nsi) const
391  {
392 
393  //Added void to remove parameter not used warning
394  ((void)nsi);
395  do_getCcs_kokkos_view(nzval, rowind, colptr,
396  nnz, colmap, distribution, ordering,
397  typename adapter_t::major_access());
398  }
399 
400  template < class Matrix >
401  template<typename KV_S, typename KV_GO, typename KV_GS>
402  void
403  MatrixAdapter<Matrix>::do_getCcs_kokkos_view(KV_S & nzval,
404  KV_GO & rowind,
405  KV_GS & colptr,
406  typename MatrixAdapter<Matrix>::global_size_t& nnz,
407  const Teuchos::Ptr<const Tpetra::Map<local_ordinal_t,global_ordinal_t,node_t> > colmap,
408  EDistribution distribution,
409  EStorage_Ordering ordering,
410  row_access ra) const
411  {
412  using Teuchos::ArrayView;
413  // get the crs and transpose
414 
415  ((void) ra);
416 
417  KV_S nzval_tmp(Kokkos::ViewAllocateWithoutInitializing("nzval_tmp"), nzval.size());
418  KV_GO colind(Kokkos::ViewAllocateWithoutInitializing("colind"), rowind.size());
419  KV_GS rowptr(Kokkos::ViewAllocateWithoutInitializing("rowptr"), this->getGlobalNumRows() + 1);
420 
421  this->getCrs_kokkos_view(nzval_tmp, colind, rowptr, nnz, colmap, ordering, distribution);
422 
423  if(nnz > 0) {
424  // This is currently just used by Cholmod in which case the views will be
425  // host, even if Cholmod is using GPU. Will need to upgrade this section
426  // to properly handle device when we have a solver that needs it.
427  ArrayView<typename KV_S::value_type> av_nzval_tmp(nzval_tmp.data(), nzval_tmp.size());
428  ArrayView<typename KV_GO::value_type> av_colind(colind.data(), colind.size());
429  ArrayView<typename KV_GS::value_type> av_rowptr(rowptr.data(), rowptr.size());
430  ArrayView<typename KV_S::value_type> av_nzval(nzval.data(), nzval.size());
431  ArrayView<typename KV_GO::value_type> av_rowind(rowind.data(), rowind.size());
432  ArrayView<typename KV_GS::value_type> av_colptr(colptr.data(), colptr.size());
433  Util::transpose(av_nzval_tmp, av_colind, av_rowptr, av_nzval, av_rowind, av_colptr);
434  }
435  }
436 
437  // These will link to concrete implementations
438  template < class Matrix >
439  template<typename KV_GO, typename KV_S>
440  void
442  KV_GO & indices,
443  KV_S & vals,
444  size_t& nnz) const
445  {
446  static_cast<const adapter_t*>(this)->getGlobalRowCopy_kokkos_view_impl(row, indices, vals, nnz);
447  }
448 
449  template < class Matrix >
450  size_t
452  {
453  return static_cast<const adapter_t*>(this)->getMaxRowNNZ_impl();
454  }
455 
456  template < class Matrix >
457  size_t
458  MatrixAdapter<Matrix>::getMaxColNNZ() const
459  {
460  return static_cast<const adapter_t*>(this)->getMaxColNNZ_impl();
461  }
462 
463  template < class Matrix >
464  size_t
465  MatrixAdapter<Matrix>::getGlobalRowNNZ(global_ordinal_t row) const
466  {
467  return static_cast<const adapter_t*>(this)->getGlobalRowNNZ_impl(row);
468  }
469 
470  template < class Matrix >
471  size_t
472  MatrixAdapter<Matrix>::getLocalRowNNZ(local_ordinal_t row) const
473  {
474  return static_cast<const adapter_t*>(this)->getLocalRowNNZ_impl(row);
475  }
476 
477  template < class Matrix >
478  size_t
479  MatrixAdapter<Matrix>::getGlobalColNNZ(global_ordinal_t col) const
480  {
481  return static_cast<const adapter_t*>(this)->getGlobalColNNZ_impl(col);
482  }
483 
484  template < class Matrix >
485  size_t
486  MatrixAdapter<Matrix>::getLocalColNNZ(local_ordinal_t col) const
487  {
488  return static_cast<const adapter_t*>(this)->getLocalColNNZ_impl(col);
489  }
490 
491  template < class Matrix >
492  bool
493  MatrixAdapter<Matrix>::isLocallyIndexed() const
494  {
495  return static_cast<const adapter_t*>(this)->isLocallyIndexed_impl();
496  }
497 
498  template < class Matrix >
499  bool
500  MatrixAdapter<Matrix>::isGloballyIndexed() const
501  {
502  return static_cast<const adapter_t*>(this)->isGloballyIndexed_impl();
503  }
504 
505 
506  template < class Matrix >
507  Teuchos::RCP<const MatrixAdapter<Matrix> >
508  MatrixAdapter<Matrix>::get(const Teuchos::Ptr<const Tpetra::Map<local_ordinal_t,global_ordinal_t,node_t> > map, EDistribution distribution) const
509  {
510  return static_cast<const adapter_t*>(this)->get_impl(map, distribution);
511  }
512 
513 
514  template <class Matrix>
515  Teuchos::RCP<MatrixAdapter<Matrix> >
516  createMatrixAdapter(Teuchos::RCP<Matrix> m){
517  using Teuchos::rcp;
518  using Teuchos::rcp_const_cast;
519 
520  if(m.is_null()) return Teuchos::null;
521  return( rcp(new ConcreteMatrixAdapter<Matrix>(m)) );
522  }
523 
524  template <class Matrix>
525  Teuchos::RCP<const MatrixAdapter<Matrix> >
526  createConstMatrixAdapter(Teuchos::RCP<const Matrix> m){
527  using Teuchos::rcp;
528  using Teuchos::rcp_const_cast;
529 
530  if(m.is_null()) return Teuchos::null;
531  return( rcp(new ConcreteMatrixAdapter<Matrix>(rcp_const_cast<Matrix,const Matrix>(m))).getConst() );
532  }
533 
534 } // end namespace Amesos2
535 
536 #endif // AMESOS2_MATRIXADAPTER_DEF_HPP
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