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