Tpetra parallel linear algebra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Tpetra_Details_CooMatrix.hpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Tpetra: Templated Linear Algebra Services Package
4 //
5 // Copyright 2008 NTESS and the Tpetra contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #ifndef TPETRA_DETAILS_COOMATRIX_HPP
11 #define TPETRA_DETAILS_COOMATRIX_HPP
12 
17 
18 #include "TpetraCore_config.h"
19 #include "Tpetra_Details_PackTriples.hpp"
20 #include "Tpetra_DistObject.hpp"
23 #include "Teuchos_TypeNameTraits.hpp"
24 
25 #include <initializer_list>
26 #include <map>
27 #include <memory>
28 #include <string>
29 #include <type_traits>
30 #include <vector>
31 
32 
33 namespace Tpetra {
34 namespace Details {
35 
36 // Implementation details of Tpetra::Details.
37 // So, users REALLY should not use anything in here.
38 namespace Impl {
39 
42 template<class IndexType>
43 struct CooGraphEntry {
44  IndexType row;
45  IndexType col;
46 };
47 
52 template<class IndexType>
54  bool
55  operator () (const CooGraphEntry<IndexType>& x,
56  const CooGraphEntry<IndexType>& y) const
57  {
58  return (x.row < y.row) || (x.row == y.row && x.col < y.col);
59  }
60 };
61 
64 template<class SC, class GO>
66 private:
74  typedef std::map<CooGraphEntry<GO>, SC,
75  CompareCooGraphEntries<GO> > entries_type;
76 
79  entries_type entries_;
80 
81 public:
83  typedef char packet_type;
84 
86  CooMatrixImpl () = default;
87 
94  void
95  sumIntoGlobalValue (const GO gblRowInd,
96  const GO gblColInd,
97  const SC& val)
98  {
99  // There's no sense in worrying about the insertion hint, since
100  // indices may be all over the place. Users make no promises of
101  // sorting or locality of input.
102  CooGraphEntry<GO> ij {gblRowInd, gblColInd};
103  auto result = this->entries_.insert ({ij, val});
104  if (! result.second) { // already in the map
105  result.first->second += val; // sum in the new value
106  }
107  }
108 
117  void
118  sumIntoGlobalValues (const GO gblRowInds[],
119  const GO gblColInds[],
120  const SC vals[],
121  const std::size_t numEnt)
122  {
123  for (std::size_t k = 0; k < numEnt; ++k) {
124  // There's no sense in worrying about the insertion hint, since
125  // indices may be all over the place. Users make no promises of
126  // sorting or locality of input.
127  CooGraphEntry<GO> ij {gblRowInds[k], gblColInds[k]};
128  const SC val = vals[k];
129  auto result = this->entries_.insert ({ij, val});
130  if (! result.second) { // already in the map
131  result.first->second += val; // sum in the new value
132  }
133  }
134  }
135 
137  std::size_t
139  {
140  return this->entries_.size ();
141  }
142 
146  void
147  forAllEntries (std::function<void (const GO, const GO, const SC&)> f) const
148  {
149  for (auto iter = this->entries_.begin ();
150  iter != this->entries_.end (); ++iter) {
151  f (iter->first.row, iter->first.col, iter->second);
152  }
153  }
154 
160  void
161  mergeIntoRow (const GO tgtGblRow,
162  const CooMatrixImpl<SC, GO>& src,
163  const GO srcGblRow)
164  {
165  // const char prefix[] =
166  // "Tpetra::Details::Impl::CooMatrixImpl::mergeIntoRow: ";
167 
168  entries_type& tgtEntries = this->entries_;
169  const entries_type& srcEntries = src.entries_;
170 
171  // Find all entries with the given global row index. The min GO
172  // value is guaranteed to be the least possible column index, so
173  // beg1 is a lower bound for all (row index, column index) pairs.
174  // Lower bound is inclusive; upper bound is exclusive.
175  auto srcBeg = srcEntries.lower_bound ({srcGblRow, std::numeric_limits<GO>::min ()});
176  auto srcEnd = srcEntries.upper_bound ({srcGblRow+1, std::numeric_limits<GO>::min ()});
177  auto tgtBeg = tgtEntries.lower_bound ({tgtGblRow, std::numeric_limits<GO>::min ()});
178  auto tgtEnd = tgtEntries.upper_bound ({tgtGblRow+1, std::numeric_limits<GO>::min ()});
179 
180  // Don't waste time iterating over the current row of *this, if
181  // the current row of src is empty.
182  if (srcBeg != srcEnd) {
183  for (auto tgtCur = tgtBeg; tgtCur != tgtEnd; ++tgtCur) {
184  auto srcCur = srcBeg;
185  while (srcCur != srcEnd && srcCur->first.col < tgtCur->first.col) {
186  ++srcCur;
187  }
188  // At this point, one of the following is true:
189  //
190  // 1. srcCur == srcEnd, thus nothing more to insert.
191  // 2. srcCur->first.col > tgtCur->first.col, thus the current
192  // row of src has no entry matching tgtCur->first, so we
193  // have to insert it. Insertion does not invalidate
194  // tgtEntries iterators, and neither srcEntries nor
195  // tgtEntries have duplicates, so this is safe.
196  // 3. srcCur->first.col == tgtCur->first.col, so add the two entries.
197  if (srcCur != srcEnd) {
198  if (srcCur->first.col == tgtCur->first.col) {
199  tgtCur->second += srcCur->second;
200  }
201  else {
202  // tgtCur is (optimally) right before where we want to put
203  // the new entry, since srcCur points to the first entry
204  // in srcEntries whose column index is greater than that
205  // of the entry to which tgtCur points.
206  (void) tgtEntries.insert (tgtCur, *srcCur);
207  }
208  } // if srcCur != srcEnd
209  } // for each entry in the current row (lclRow) of *this
210  } // if srcBeg != srcEnd
211  }
212 
222  int
223  countPackRow (int& numPackets,
224  const GO gblRow,
225  const ::Teuchos::Comm<int>& comm,
226  std::ostream* errStrm = NULL) const
227  {
228  using ::Tpetra::Details::countPackTriples;
229  using ::Tpetra::Details::countPackTriplesCount;
230  using std::endl;
231  const char prefix[] = "Tpetra::Details::Impl::CooMatrixImpl::countPackRow: ";
232 #ifdef HAVE_TPETRACORE_MPI
233  int errCode = MPI_SUCCESS;
234 #else
235  int errCode = 0;
236 #endif // HAVE_TPETRACORE_MPI
237 
238  // Count the number of entries in the given row.
239  const GO minGO = std::numeric_limits<GO>::min ();
240  auto beg = this->entries_.lower_bound ({gblRow, minGO});
241  auto end = this->entries_.upper_bound ({gblRow+1, minGO});
242  int numEnt = 0;
243  for (auto iter = beg; iter != end; ++iter) {
244  ++numEnt;
245  if (numEnt == std::numeric_limits<int>::max ()) {
246  if (errStrm != NULL) {
247  *errStrm << prefix << "In (global) row " << gblRow << ", the number "
248  "of entries thus far, numEnt = " << numEnt << ", has reached the "
249  "maximum int value. We need to store this count as int for MPI's "
250  "sake." << endl;
251  }
252  return -1;
253  }
254  }
255 
256  int numPacketsForCount = 0; // output argument of countPackTriplesCount
257  {
258  errCode =
259  countPackTriplesCount (comm, numPacketsForCount, errStrm);
260  if (errCode != 0) {
261  if (errStrm != NULL) {
262  *errStrm << prefix << "countPackTriplesCount "
263  "returned errCode = " << errCode << " != 0." << endl;
264  }
265  return errCode;
266  }
267  if (numPacketsForCount < 0) {
268  if (errStrm != NULL) {
269  *errStrm << prefix << "countPackTriplesCount returned "
270  "numPacketsForCount = " << numPacketsForCount << " < 0." << endl;
271  }
272  return -1;
273  }
274  }
275 
276  int numPacketsForTriples = 0; // output argument of countPackTriples
277  {
278  errCode = countPackTriples<SC, GO> (numEnt, comm, numPacketsForTriples);
279  TEUCHOS_TEST_FOR_EXCEPTION
280  (errCode != 0, std::runtime_error, prefix << "countPackTriples "
281  "returned errCode = " << errCode << " != 0.");
282  TEUCHOS_TEST_FOR_EXCEPTION
283  (numPacketsForTriples < 0, std::logic_error, prefix << "countPackTriples "
284  "returned numPacketsForTriples = " << numPacketsForTriples << " < 0.");
285  }
286 
287  numPackets = numPacketsForCount + numPacketsForTriples;
288  return errCode;
289  }
290 
305  void
306  packRow (packet_type outBuf[],
307  const int outBufSize,
308  int& outBufCurPos, // in/out argument
309  const ::Teuchos::Comm<int>& comm,
310  std::vector<GO>& gblRowInds,
311  std::vector<GO>& gblColInds,
312  std::vector<SC>& vals,
313  const GO gblRow) const
314  {
315  using ::Tpetra::Details::packTriples;
316  using ::Tpetra::Details::packTriplesCount;
317  const char prefix[] = "Tpetra::Details::Impl::CooMatrixImpl::packRow: ";
318 
319  const GO minGO = std::numeric_limits<GO>::min ();
320  auto beg = this->entries_.lower_bound ({gblRow, minGO});
321  auto end = this->entries_.upper_bound ({gblRow+1, minGO});
322 
323  // This doesn't actually deallocate. Only swapping with an empty
324  // std::vector does that.
325  gblRowInds.resize (0);
326  gblColInds.resize (0);
327  vals.resize (0);
328 
329  int numEnt = 0;
330  for (auto iter = beg; iter != end; ++iter) {
331  gblRowInds.push_back (iter->first.row);
332  gblColInds.push_back (iter->first.col);
333  vals.push_back (iter->second);
334  ++numEnt;
335  TEUCHOS_TEST_FOR_EXCEPTION
336  (numEnt == std::numeric_limits<int>::max (), std::runtime_error, prefix
337  << "In (global) row " << gblRow << ", the number of entries thus far, "
338  "numEnt = " << numEnt << ", has reached the maximum int value. "
339  "We need to store this count as int for MPI's sake.");
340  }
341 
342  {
343  const int errCode =
344  packTriplesCount (numEnt, outBuf, outBufSize, outBufCurPos, comm);
345  TEUCHOS_TEST_FOR_EXCEPTION
346  (errCode != 0, std::runtime_error, prefix
347  << "In (global) row " << gblRow << ", packTriplesCount returned "
348  "errCode = " << errCode << " != 0.");
349  }
350  {
351  const int errCode =
352  packTriples (gblRowInds.data (),
353  gblColInds.data (),
354  vals.data (),
355  numEnt,
356  outBuf,
357  outBufSize,
358  outBufCurPos, // in/out argument
359  comm);
360  TEUCHOS_TEST_FOR_EXCEPTION
361  (errCode != 0, std::runtime_error, prefix << "In (global) row "
362  << gblRow << ", packTriples returned errCode = " << errCode
363  << " != 0.");
364  }
365  }
366 
374  GO
375  getMyGlobalRowIndices (std::vector<GO>& rowInds) const
376  {
377  rowInds.clear ();
378 
379  GO lclMinRowInd = std::numeric_limits<GO>::max (); // compute local min
380  for (typename entries_type::const_iterator iter = this->entries_.begin ();
381  iter != this->entries_.end (); ++iter) {
382  const GO rowInd = iter->first.row;
383  if (rowInd < lclMinRowInd) {
384  lclMinRowInd = rowInd;
385  }
386  if (rowInds.empty () || rowInds.back () != rowInd) {
387  rowInds.push_back (rowInd); // don't insert duplicates
388  }
389  }
390  return lclMinRowInd;
391  }
392 
393  template<class OffsetType>
394  void
395  buildCrs (std::vector<OffsetType>& rowOffsets,
396  GO gblColInds[],
397  SC vals[]) const
398  {
399  static_assert (std::is_integral<OffsetType>::value,
400  "OffsetType must be a built-in integer type.");
401 
402  // clear() doesn't generally free storage; it just resets the
403  // length. Thus, we reuse any existing storage here.
404  rowOffsets.clear ();
405 
406  const std::size_t numEnt = this->getLclNumEntries ();
407  if (numEnt == 0) {
408  rowOffsets.push_back (0);
409  }
410  else {
411  typename entries_type::const_iterator iter = this->entries_.begin ();
412  GO prevGblRowInd = iter->first.row;
413 
414  OffsetType k = 0;
415  for ( ; iter != this->entries_.end (); ++iter, ++k) {
416  const GO gblRowInd = iter->first.row;
417  if (k == 0 || gblRowInd != prevGblRowInd) {
418  // The row offsets array always has at least one entry. The
419  // first entry is always zero, and the last entry is always
420  // the number of matrix entries.
421  rowOffsets.push_back (k);
422  prevGblRowInd = gblRowInd;
423  }
424  gblColInds[k] = iter->first.col;
425 
426  static_assert (std::is_same<typename std::decay<decltype (iter->second)>::type, SC>::value,
427  "The type of iter->second != SC.");
428  vals[k] = iter->second;
429  }
430  rowOffsets.push_back (static_cast<OffsetType> (numEnt));
431  }
432  }
433 
446  template<class OffsetType, class LO>
447  void
448  buildLocallyIndexedCrs (std::vector<OffsetType>& rowOffsets,
449  LO lclColInds[],
450  SC vals[],
451  std::function<LO (const GO)> gblToLcl) const
452  {
453  static_assert (std::is_integral<OffsetType>::value,
454  "OffsetType must be a built-in integer type.");
455  static_assert (std::is_integral<LO>::value,
456  "LO must be a built-in integer type.");
457 
458  // clear() doesn't generally free storage; it just resets the
459  // length. Thus, we reuse any existing storage here.
460  rowOffsets.clear ();
461 
462  const std::size_t numEnt = this->getLclNumEntries ();
463  if (numEnt == 0) {
464  rowOffsets.push_back (0);
465  }
466  else {
467  typename entries_type::const_iterator iter = this->entries_.begin ();
468  GO prevGblRowInd = iter->first.row;
469 
470  OffsetType k = 0;
471  for ( ; iter != this->entries_.end (); ++iter, ++k) {
472  const GO gblRowInd = iter->first.row;
473  if (k == 0 || gblRowInd != prevGblRowInd) {
474  // The row offsets array always has at least one entry. The
475  // first entry is always zero, and the last entry is always
476  // the number of matrix entries.
477  rowOffsets.push_back (k);
478  prevGblRowInd = gblRowInd;
479  }
480  lclColInds[k] = gblToLcl (iter->first.col);
481  vals[k] = iter->second;
482  }
483  rowOffsets.push_back (static_cast<OffsetType> (numEnt));
484  }
485  }
486 };
487 
488 } // namespace Impl
489 
538 template<class SC,
542 class CooMatrix : public ::Tpetra::DistObject<char, LO, GO, NT> {
543 public:
545  typedef char packet_type;
547  typedef SC scalar_type;
548  typedef LO local_ordinal_type;
549  typedef GO global_ordinal_type;
550  typedef NT node_type;
551  typedef typename NT::device_type device_type;
553  typedef ::Tpetra::Map<local_ordinal_type, global_ordinal_type, node_type> map_type;
554 
555 private:
557  typedef ::Tpetra::DistObject<packet_type, local_ordinal_type,
558  global_ordinal_type, node_type> base_type;
559 
562 
563 public:
570  base_type (::Teuchos::null),
571  localError_ (new bool (false)),
572  errs_ (new std::shared_ptr<std::ostringstream> ()) // ptr to a null ptr
573  {}
574 
582  CooMatrix (const ::Teuchos::RCP<const map_type>& map) :
583  base_type (map),
584  localError_ (new bool (false)),
585  errs_ (new std::shared_ptr<std::ostringstream> ()) // ptr to a null ptr
586  {}
587 
589  virtual ~CooMatrix () {}
590 
597  void
598  sumIntoGlobalValue (const GO gblRowInd,
599  const GO gblColInd,
600  const SC& val)
601  {
602  this->impl_.sumIntoGlobalValue (gblRowInd, gblColInd, val);
603  }
604 
613  void
614  sumIntoGlobalValues (const GO gblRowInds[],
615  const GO gblColInds[],
616  const SC vals[],
617  const std::size_t numEnt)
618  {
619  this->impl_.sumIntoGlobalValues (gblRowInds, gblColInds, vals, numEnt);
620  }
621 
623  void
624  sumIntoGlobalValues (std::initializer_list<GO> gblRowInds,
625  std::initializer_list<GO> gblColInds,
626  std::initializer_list<SC> vals,
627  const std::size_t numEnt)
628  {
629  this->impl_.sumIntoGlobalValues (gblRowInds.begin (), gblColInds.begin (),
630  vals.begin (), numEnt);
631  }
632 
634  std::size_t
636  {
637  return this->impl_.getLclNumEntries ();
638  }
639 
640  template<class OffsetType>
641  void
642  buildCrs (::Kokkos::View<OffsetType*, device_type>& rowOffsets,
643  ::Kokkos::View<GO*, device_type>& gblColInds,
644  ::Kokkos::View<typename ::Kokkos::ArithTraits<SC>::val_type*, device_type>& vals)
645  {
646  static_assert (std::is_integral<OffsetType>::value,
647  "OffsetType must be a built-in integer type.");
648  using ::Kokkos::create_mirror_view;
650  using ::Kokkos::View;
651  typedef typename ::Kokkos::ArithTraits<SC>::val_type ISC;
652 
653  const std::size_t numEnt = this->getLclNumEntries ();
654 
655  gblColInds = View<GO*, device_type> ("gblColInds", numEnt);
656  vals = View<ISC*, device_type> ("vals", numEnt);
657  auto gblColInds_h = create_mirror_view (gblColInds);
658  auto vals_h = create_mirror_view (vals);
659 
660  std::vector<std::size_t> rowOffsetsSV;
661  this->impl_.buildCrs (rowOffsetsSV,
662  gblColInds_h.data (),
663  vals_h.data ());
664  rowOffsets =
665  View<OffsetType*, device_type> ("rowOffsets", rowOffsetsSV.size ());
666  typename View<OffsetType*, device_type>::HostMirror
667  rowOffsets_h (rowOffsetsSV.data (), rowOffsetsSV.size ());
668  deep_copy (rowOffsets, rowOffsets_h);
669 
670  deep_copy (gblColInds, gblColInds_h);
671  deep_copy (vals, vals_h);
672  }
673 
684  void
685  fillComplete (const ::Teuchos::RCP<const ::Teuchos::Comm<int> >& comm)
686  {
687  if (comm.is_null ()) {
688  this->map_ = ::Teuchos::null;
689  }
690  else {
691  this->map_ = this->buildMap (comm);
692  }
693  }
694 
703  void
705  {
706  TEUCHOS_TEST_FOR_EXCEPTION
707  (this->getMap ().is_null (), std::runtime_error, "Tpetra::Details::"
708  "CooMatrix::fillComplete: This object does not yet have a Map. "
709  "You must call the version of fillComplete "
710  "that takes an input communicator.");
711  this->fillComplete (this->getMap ()->getComm ());
712  }
713 
728  bool localError () const {
729  return *localError_;
730  }
731 
749  std::string errorMessages () const {
750  return ((*errs_).get () == NULL) ? std::string ("") : (*errs_)->str ();
751  }
752 
753 private:
766  std::shared_ptr<bool> localError_;
767 
775  std::shared_ptr<std::shared_ptr<std::ostringstream> > errs_;
776 
778  std::ostream&
779  markLocalErrorAndGetStream ()
780  {
781  * (this->localError_) = true;
782  if ((*errs_).get () == NULL) {
783  *errs_ = std::shared_ptr<std::ostringstream> (new std::ostringstream ());
784  }
785  return **errs_;
786  }
787 
788 public:
791  virtual std::string description () const {
792  using Teuchos::TypeNameTraits;
793 
794  std::ostringstream os;
795  os << "\"Tpetra::Details::CooMatrix\": {"
796  << "SC: " << TypeNameTraits<SC>::name ()
797  << ", LO: " << TypeNameTraits<LO>::name ()
798  << ", GO: " << TypeNameTraits<GO>::name ()
799  << ", NT: " << TypeNameTraits<NT>::name ();
800  if (this->getObjectLabel () != "") {
801  os << ", Label: \"" << this->getObjectLabel () << "\"";
802  }
803  os << ", Has Map: " << (this->map_.is_null () ? "false" : "true")
804  << "}";
805  return os.str ();
806  }
807 
810  virtual void
811  describe (Teuchos::FancyOStream& out,
812  const Teuchos::EVerbosityLevel verbLevel =
813  Teuchos::Describable::verbLevel_default) const
814  {
815  using ::Tpetra::Details::gathervPrint;
816  using ::Teuchos::EVerbosityLevel;
817  using ::Teuchos::OSTab;
818  using ::Teuchos::TypeNameTraits;
819  using ::Teuchos::VERB_DEFAULT;
820  using ::Teuchos::VERB_LOW;
821  using ::Teuchos::VERB_MEDIUM;
822  using std::endl;
823 
824  const auto vl = (verbLevel == VERB_DEFAULT) ? VERB_LOW : verbLevel;
825 
826  auto comm = this->getMap ().is_null () ?
827  Teuchos::null : this->getMap ()->getComm ();
828  const int myRank = comm.is_null () ? 0 : comm->getRank ();
829  // const int numProcs = comm.is_null () ? 1 : comm->getSize ();
830 
831  if (vl != Teuchos::VERB_NONE) {
832  // Convention is to start describe() implementations with a tab.
833  OSTab tab0 (out);
834  if (myRank == 0) {
835  out << "\"Tpetra::Details::CooMatrix\":" << endl;
836  }
837  OSTab tab1 (out);
838  if (myRank == 0) {
839  out << "Template parameters:" << endl;
840  {
841  OSTab tab2 (out);
842  out << "SC: " << TypeNameTraits<SC>::name () << endl
843  << "LO: " << TypeNameTraits<LO>::name () << endl
844  << "GO: " << TypeNameTraits<GO>::name () << endl
845  << "NT: " << TypeNameTraits<NT>::name () << endl;
846  }
847  if (this->getObjectLabel () != "") {
848  out << "Label: \"" << this->getObjectLabel () << "\"" << endl;
849  }
850  out << "Has Map: " << (this->map_.is_null () ? "false" : "true") << endl;
851  } // if myRank == 0
852 
853  // Describe the Map, if it exists.
854  if (! this->map_.is_null ()) {
855  if (myRank == 0) {
856  out << "Map:" << endl;
857  }
858  OSTab tab2 (out);
859  this->map_->describe (out, vl);
860  }
861 
862  // At verbosity > VERB_LOW, each process prints something.
863  if (vl > VERB_LOW) {
864  std::ostringstream os;
865  os << "Process " << myRank << ":" << endl;
866  //OSTab tab3 (os);
867 
868  const std::size_t numEnt = this->impl_.getLclNumEntries ();
869  os << " Local number of entries: " << numEnt << endl;
870  if (vl > VERB_MEDIUM) {
871  os << " Local entries:" << endl;
872  //OSTab tab4 (os);
873  this->impl_.forAllEntries ([&os] (const GO row, const GO col, const SC& val) {
874  os << " {"
875  << "row: " << row
876  << ", col: " << col
877  << ", val: " << val
878  << "}" << endl;
879  });
880  }
881  gathervPrint (out, os.str (), *comm);
882  }
883  } // vl != Teuchos::VERB_NONE
884  }
885 
886 private:
895  Teuchos::RCP<const map_type>
896  buildMap (const ::Teuchos::RCP<const ::Teuchos::Comm<int> >& comm)
897  {
898  using ::Teuchos::outArg;
899  using ::Teuchos::rcp;
900  using ::Teuchos::REDUCE_MIN;
901  using ::Teuchos::reduceAll;
902  typedef ::Tpetra::global_size_t GST;
903  //const char prefix[] = "Tpetra::Details::CooMatrix::buildMap: ";
904 
905  // Processes where comm is null don't participate in the Map.
906  if (comm.is_null ()) {
907  return ::Teuchos::null;
908  }
909 
910  // mfh 17 Jan 2017: We just happen to use row indices, because
911  // that's what Tpetra::CrsMatrix currently uses. That's probably
912  // not the best thing to use, but it's not bad for commonly
913  // encountered matrices. A better more general approach might be
914  // to hash (row index, column index) pairs to a global index. One
915  // could make that unique by doing a parallel scan at map
916  // construction time.
917 
918  std::vector<GO> rowInds;
919  const GO lclMinGblRowInd = this->impl_.getMyGlobalRowIndices (rowInds);
920 
921  // Compute the globally min row index for the "index base."
922  GO gblMinGblRowInd = 0; // output argument
923  reduceAll<int, GO> (*comm, REDUCE_MIN, lclMinGblRowInd,
924  outArg (gblMinGblRowInd));
925  const GO indexBase = gblMinGblRowInd;
926  const GST INV = Tpetra::Details::OrdinalTraits<GST>::invalid ();
927  return rcp (new map_type (INV, rowInds.data (), rowInds.size (),
928  indexBase, comm));
929  }
930 
931 protected:
935  virtual size_t constantNumberOfPackets () const {
936  return static_cast<size_t> (0);
937  }
938 
942  virtual bool
943  checkSizes (const ::Tpetra::SrcDistObject& source)
944  {
945  using std::endl;
946  typedef CooMatrix<SC, LO, GO, NT> this_COO_type;
947  const char prefix[] = "Tpetra::Details::CooMatrix::checkSizes: ";
948 
949  const this_COO_type* src = dynamic_cast<const this_COO_type* > (&source);
950 
951  if (src == NULL) {
952  std::ostream& err = markLocalErrorAndGetStream ();
953  err << prefix << "The source object of the Import or Export "
954  "must be a CooMatrix with the same template parameters as the "
955  "target object." << endl;
956  }
957  else if (this->map_.is_null ()) {
958  std::ostream& err = markLocalErrorAndGetStream ();
959  err << prefix << "The target object of the Import or Export "
960  "must be a CooMatrix with a nonnull Map." << endl;
961  }
962  return ! (* (this->localError_));
963  }
964 
966  using buffer_device_type =
967  typename ::Tpetra::DistObject<char, LO, GO, NT>::buffer_device_type;
968 
969  virtual void
970  copyAndPermute
971  (const ::Tpetra::SrcDistObject& sourceObject,
972  const size_t numSameIDs,
973  const Kokkos::DualView<const LO*,
974  buffer_device_type>& permuteToLIDs,
975  const Kokkos::DualView<const LO*,
976  buffer_device_type>& permuteFromLIDs,
977  const CombineMode /* CM */)
978  {
979  using std::endl;
980  using this_COO_type = CooMatrix<SC, LO, GO, NT>;
981  const char prefix[] = "Tpetra::Details::CooMatrix::copyAndPermute: ";
982 
983  // There's no communication in this method, so it's safe just to
984  // return on error.
985 
986  if (* (this->localError_)) {
987  std::ostream& err = this->markLocalErrorAndGetStream ();
988  err << prefix << "The target object of the Import or Export is "
989  "already in an error state." << endl;
990  return;
991  }
992 
993  const this_COO_type* src = dynamic_cast<const this_COO_type*> (&sourceObject);
994  if (src == nullptr) {
995  std::ostream& err = this->markLocalErrorAndGetStream ();
996  err << prefix << "Input argument 'sourceObject' is not a CooMatrix."
997  << endl;
998  return;
999  }
1000 
1001  const size_t numPermuteIDs =
1002  static_cast<size_t> (permuteToLIDs.extent (0));
1003  if (numPermuteIDs != static_cast<size_t> (permuteFromLIDs.extent (0))) {
1004  std::ostream& err = this->markLocalErrorAndGetStream ();
1005  err << prefix << "permuteToLIDs.extent(0) = "
1006  << numPermuteIDs << " != permuteFromLIDs.extent(0) = "
1007  << permuteFromLIDs.extent (0) << "." << endl;
1008  return;
1009  }
1010  if (sizeof (int) <= sizeof (size_t) &&
1011  numPermuteIDs > static_cast<size_t> (std::numeric_limits<int>::max ())) {
1012  std::ostream& err = this->markLocalErrorAndGetStream ();
1013  err << prefix << "numPermuteIDs = " << numPermuteIDs
1014  << ", a size_t, overflows int." << endl;
1015  return;
1016  }
1017 
1018  // Even though this is an std::set, we can start with numSameIDs
1019  // just by iterating through the first entries of the set.
1020 
1021  if (sizeof (int) <= sizeof (size_t) &&
1022  numSameIDs > static_cast<size_t> (std::numeric_limits<int>::max ())) {
1023  std::ostream& err = this->markLocalErrorAndGetStream ();
1024  err << prefix << "numSameIDs = " << numSameIDs
1025  << ", a size_t, overflows int." << endl;
1026  return;
1027  }
1028 
1029  //
1030  // Copy in entries from any initial rows with the same global row indices.
1031  //
1032  const LO numSame = static_cast<int> (numSameIDs);
1033  // Count of local row indices encountered here with invalid global
1034  // row indices. If nonzero, something went wrong. If something
1035  // did go wrong, we'll defer responding until the end of this
1036  // method, so we can print as much useful info as possible.
1037  LO numInvalidSameRows = 0;
1038  for (LO lclRow = 0; lclRow < numSame; ++lclRow) {
1039  // All numSame initial rows have the same global index in both
1040  // source and target Maps, so we only need to convert to global
1041  // once.
1042  const GO gblRow = this->map_->getGlobalElement (lclRow);
1043  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1044  ++numInvalidSameRows;
1045  continue;
1046  }
1047  else {
1048  this->impl_.mergeIntoRow (gblRow, src->impl_, gblRow);
1049  }
1050  }
1051 
1052  //
1053  // Copy in entries from remaining rows that are permutations, that
1054  // is, that live in both the source and target Maps, but aren't
1055  // included in the "same" list (see above).
1056  //
1057  const LO numPermute = static_cast<int> (numPermuteIDs);
1058  // Count of local "from" row indices encountered here with invalid
1059  // global row indices. If nonzero, something went wrong. If
1060  // something did go wrong, we'll defer responding until the end of
1061  // this method, so we can print as much useful info as possible.
1062  LO numInvalidRowsFrom = 0;
1063  // Count of local "to" row indices encountered here with invalid
1064  // global row indices. If nonzero, something went wrong. If
1065  // something did go wrong, we'll defer responding until the end of
1066  // this method, so we can print as much useful info as possible.
1067  LO numInvalidRowsTo = 0;
1068 
1069  TEUCHOS_ASSERT( ! permuteFromLIDs.need_sync_host () );
1070  TEUCHOS_ASSERT( ! permuteToLIDs.need_sync_host () );
1071  auto permuteFromLIDs_h = permuteFromLIDs.view_host ();
1072  auto permuteToLIDs_h = permuteToLIDs.view_host ();
1073 
1074  for (LO k = 0; k < numPermute; ++k) {
1075  const LO lclRowFrom = permuteFromLIDs_h[k];
1076  const LO lclRowTo = permuteToLIDs_h[k];
1077  const GO gblRowFrom = src->map_->getGlobalElement (lclRowFrom);
1078  const GO gblRowTo = this->map_->getGlobalElement (lclRowTo);
1079 
1080  bool bothConversionsValid = true;
1081  if (gblRowFrom == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1082  ++numInvalidRowsFrom;
1083  bothConversionsValid = false;
1084  }
1085  if (gblRowTo == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1086  ++numInvalidRowsTo;
1087  bothConversionsValid = false;
1088  }
1089  if (bothConversionsValid) {
1090  this->impl_.mergeIntoRow (gblRowTo, src->impl_, gblRowFrom);
1091  }
1092  }
1093 
1094  // Print info if any errors occurred.
1095  if (numInvalidSameRows != 0 || numInvalidRowsFrom != 0 ||
1096  numInvalidRowsTo != 0) {
1097  // Collect and print all the invalid input row indices, for the
1098  // "same," "from," and "to" lists.
1099  std::vector<std::pair<LO, GO> > invalidSameRows;
1100  invalidSameRows.reserve (numInvalidSameRows);
1101  std::vector<std::pair<LO, GO> > invalidRowsFrom;
1102  invalidRowsFrom.reserve (numInvalidRowsFrom);
1103  std::vector<std::pair<LO, GO> > invalidRowsTo;
1104  invalidRowsTo.reserve (numInvalidRowsTo);
1105 
1106  for (LO lclRow = 0; lclRow < numSame; ++lclRow) {
1107  // All numSame initial rows have the same global index in both
1108  // source and target Maps, so we only need to convert to global
1109  // once.
1110  const GO gblRow = this->map_->getGlobalElement (lclRow);
1111  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1112  invalidSameRows.push_back ({lclRow, gblRow});
1113  }
1114  }
1115 
1116  for (LO k = 0; k < numPermute; ++k) {
1117  const LO lclRowFrom = permuteFromLIDs_h[k];
1118  const LO lclRowTo = permuteToLIDs_h[k];
1119  const GO gblRowFrom = src->map_->getGlobalElement (lclRowFrom);
1120  const GO gblRowTo = this->map_->getGlobalElement (lclRowTo);
1121 
1122  if (gblRowFrom == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1123  invalidRowsFrom.push_back ({lclRowFrom, gblRowFrom});
1124  }
1125  if (gblRowTo == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1126  invalidRowsTo.push_back ({lclRowTo, gblRowTo});
1127  }
1128  }
1129 
1130  std::ostringstream os;
1131  if (numInvalidSameRows != 0) {
1132  os << "Invalid permute \"same\" (local, global) index pairs: [";
1133  for (std::size_t k = 0; k < invalidSameRows.size (); ++k) {
1134  const auto& p = invalidSameRows[k];
1135  os << "(" << p.first << "," << p.second << ")";
1136  if (k + 1 < invalidSameRows.size ()) {
1137  os << ", ";
1138  }
1139  }
1140  os << "]" << std::endl;
1141  }
1142  if (numInvalidRowsFrom != 0) {
1143  os << "Invalid permute \"from\" (local, global) index pairs: [";
1144  for (std::size_t k = 0; k < invalidRowsFrom.size (); ++k) {
1145  const auto& p = invalidRowsFrom[k];
1146  os << "(" << p.first << "," << p.second << ")";
1147  if (k + 1 < invalidRowsFrom.size ()) {
1148  os << ", ";
1149  }
1150  }
1151  os << "]" << std::endl;
1152  }
1153  if (numInvalidRowsTo != 0) {
1154  os << "Invalid permute \"to\" (local, global) index pairs: [";
1155  for (std::size_t k = 0; k < invalidRowsTo.size (); ++k) {
1156  const auto& p = invalidRowsTo[k];
1157  os << "(" << p.first << "," << p.second << ")";
1158  if (k + 1 < invalidRowsTo.size ()) {
1159  os << ", ";
1160  }
1161  }
1162  os << "]" << std::endl;
1163  }
1164 
1165  std::ostream& err = this->markLocalErrorAndGetStream ();
1166  err << prefix << os.str ();
1167  return;
1168  }
1169  }
1170 
1171  virtual void
1172  packAndPrepare
1173  (const ::Tpetra::SrcDistObject& sourceObject,
1174  const Kokkos::DualView<const local_ordinal_type*,
1175  buffer_device_type>& exportLIDs,
1176  Kokkos::DualView<packet_type*,
1177  buffer_device_type>& exports,
1178  Kokkos::DualView<size_t*,
1179  buffer_device_type> numPacketsPerLID,
1180  size_t& constantNumPackets)
1181  {
1182  using Teuchos::Comm;
1183  using Teuchos::RCP;
1184  using std::endl;
1185  using this_COO_type = CooMatrix<SC, LO, GO, NT>;
1186  const char prefix[] = "Tpetra::Details::CooMatrix::packAndPrepare: ";
1187  const char suffix[] = " This should never happen. "
1188  "Please report this bug to the Tpetra developers.";
1189 
1190  // Tell the caller that different rows may have different numbers
1191  // of matrix entries.
1192  constantNumPackets = 0;
1193 
1194  const this_COO_type* src = dynamic_cast<const this_COO_type*> (&sourceObject);
1195  if (src == nullptr) {
1196  std::ostream& err = this->markLocalErrorAndGetStream ();
1197  err << prefix << "Input argument 'sourceObject' is not a CooMatrix."
1198  << endl;
1199  }
1200  else if (* (src->localError_)) {
1201  std::ostream& err = this->markLocalErrorAndGetStream ();
1202  err << prefix << "The source (input) object of the Import or Export "
1203  "is already in an error state on this process."
1204  << endl;
1205  }
1206  else if (* (this->localError_)) {
1207  std::ostream& err = this->markLocalErrorAndGetStream ();
1208  err << prefix << "The target (output, \"this\") object of the Import "
1209  "or Export is already in an error state on this process." << endl;
1210  }
1211  // Respond to detected error(s) by resizing 'exports' to zero (so
1212  // we won't be tempted to read it later), and filling
1213  // numPacketsPerLID with zeros.
1214  if (* (this->localError_)) {
1215  // Resize 'exports' to zero, so we won't be tempted to read it.
1216  Details::reallocDualViewIfNeeded (exports, 0, "CooMatrix exports");
1217  // Trick to get around const DualView& being const.
1218  {
1219  auto numPacketsPerLID_tmp = numPacketsPerLID;
1220  numPacketsPerLID_tmp.sync_host ();
1221  numPacketsPerLID_tmp.modify_host ();
1222  }
1223  // Fill numPacketsPerLID with zeros.
1224  Kokkos::deep_copy (numPacketsPerLID.h_view, static_cast<size_t> (0));
1225  return;
1226  }
1227 
1228  const size_t numExports = exportLIDs.extent (0);
1229  if (numExports == 0) {
1230  Details::reallocDualViewIfNeeded (exports, 0, exports.h_view.label ());
1231  return; // nothing to send
1232  }
1233  RCP<const Comm<int> > comm = src->getMap ().is_null () ?
1234  Teuchos::null : src->getMap ()->getComm ();
1235  if (comm.is_null () || comm->getSize () == 1) {
1236  if (numExports != static_cast<size_t> (0)) {
1237  std::ostream& err = this->markLocalErrorAndGetStream ();
1238  err << prefix << "The input communicator is either null or "
1239  "has only one process, but numExports = " << numExports << " != 0."
1240  << suffix << endl;
1241  return;
1242  }
1243  // Don't go into the rest of this method unless there are
1244  // actually processes other than the calling process. This is
1245  // because the pack and unpack functions only have nonstub
1246  // implementations if building with MPI.
1247  return;
1248  }
1249 
1250  numPacketsPerLID.sync_host ();
1251  numPacketsPerLID.modify_host ();
1252 
1253  TEUCHOS_ASSERT( ! exportLIDs.need_sync_host () );
1254  auto exportLIDs_h = exportLIDs.view_host ();
1255 
1256  int totalNumPackets = 0;
1257  size_t numInvalidRowInds = 0;
1258  std::ostringstream errStrm; // current loop iteration's error messages
1259  for (size_t k = 0; k < numExports; ++k) {
1260  const LO lclRow = exportLIDs_h[k];
1261  // We're packing the source object's data, so we need to use the
1262  // source object's Map to convert from local to global indices.
1263  const GO gblRow = src->map_->getGlobalElement (lclRow);
1264  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1265  // Mark the error later; just count for now.
1266  ++numInvalidRowInds;
1267  numPacketsPerLID.h_view[k] = 0;
1268  continue;
1269  }
1270 
1271  // Count the number of bytes needed to pack the current row of
1272  // the source object.
1273  int numPackets = 0;
1274  const int errCode =
1275  src->impl_.countPackRow (numPackets, gblRow, *comm, &errStrm);
1276  if (errCode != 0) {
1277  std::ostream& err = this->markLocalErrorAndGetStream ();
1278  err << prefix << errStrm.str () << endl;
1279  numPacketsPerLID.h_view[k] = 0;
1280  continue;
1281  }
1282 
1283  // Make sure that the total number of packets fits in int.
1284  // MPI requires this.
1285  const long long newTotalNumPackets =
1286  static_cast<long long> (totalNumPackets) +
1287  static_cast<long long> (numPackets);
1288  if (newTotalNumPackets >
1289  static_cast<long long> (std::numeric_limits<int>::max ())) {
1290  std::ostream& err = this->markLocalErrorAndGetStream ();
1291  err << prefix << "The new total number of packets "
1292  << newTotalNumPackets << " does not fit in int." << endl;
1293  // At this point, we definitely cannot continue. In order to
1294  // leave the output arguments in a rational state, we zero out
1295  // all remaining entries of numPacketsPerLID before returning.
1296  for (size_t k2 = k; k2 < numExports; ++k2) {
1297  numPacketsPerLID.h_view[k2] = 0;
1298  }
1299  return;
1300  }
1301  numPacketsPerLID.h_view[k] = static_cast<size_t> (numPackets);
1302  totalNumPackets = static_cast<int> (newTotalNumPackets);
1303  }
1304 
1305  // If we found invalid row indices in exportLIDs, go back,
1306  // collect, and report them.
1307  if (numInvalidRowInds != 0) {
1308  std::vector<std::pair<LO, GO> > invalidRowInds;
1309  for (size_t k = 0; k < numExports; ++k) {
1310  const LO lclRow = exportLIDs_h[k];
1311  // We're packing the source object's data, so we need to use
1312  // the source object's Map to convert from local to global
1313  // indices.
1314  const GO gblRow = src->map_->getGlobalElement (lclRow);
1315  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1316  invalidRowInds.push_back ({lclRow, gblRow});
1317  }
1318  }
1319  std::ostringstream os;
1320  os << prefix << "We found " << numInvalidRowInds << " invalid row ind"
1321  << (numInvalidRowInds != static_cast<size_t> (1) ? "ices" : "ex")
1322  << " out of " << numExports << " in exportLIDs. Here is the list "
1323  << " of invalid row indices: [";
1324  for (size_t k = 0; k < invalidRowInds.size (); ++k) {
1325  os << "(LID: " << invalidRowInds[k].first << ", GID: "
1326  << invalidRowInds[k].second << ")";
1327  if (k + 1 < invalidRowInds.size ()) {
1328  os << ", ";
1329  }
1330  }
1331  os << "].";
1332 
1333  std::ostream& err = this->markLocalErrorAndGetStream ();
1334  err << prefix << os.str () << std::endl;
1335  return;
1336  }
1337 
1338  {
1339  const bool reallocated =
1340  Details::reallocDualViewIfNeeded (exports, totalNumPackets,
1341  "CooMatrix exports");
1342  if (reallocated) {
1343  exports.sync_host (); // make sure alloc'd on host
1344  }
1345  }
1346  exports.modify_host ();
1347 
1348  // FIXME (mfh 17 Jan 2017) packTriples wants three arrays, not a
1349  // single array of structs. For now, we just copy.
1350  std::vector<GO> gblRowInds;
1351  std::vector<GO> gblColInds;
1352  std::vector<SC> vals;
1353 
1354  int outBufCurPos = 0;
1355  packet_type* outBuf = exports.h_view.data ();
1356  for (size_t k = 0; k < numExports; ++k) {
1357  const LO lclRow = exportLIDs.h_view[k];
1358  // We're packing the source object's data, so we need to use the
1359  // source object's Map to convert from local to global indices.
1360  const GO gblRow = src->map_->getGlobalElement (lclRow);
1361  // Pack the current row of the source object.
1362  src->impl_.packRow (outBuf, totalNumPackets, outBufCurPos, *comm,
1363  gblRowInds, gblColInds, vals, gblRow);
1364  }
1365  }
1366 
1367  virtual void
1368  unpackAndCombine
1369  (const Kokkos::DualView<const local_ordinal_type*,
1370  buffer_device_type>& importLIDs,
1371  Kokkos::DualView<packet_type*,
1372  buffer_device_type> imports,
1373  Kokkos::DualView<size_t*,
1374  buffer_device_type> numPacketsPerLID,
1375  const size_t /* constantNumPackets */,
1376  const ::Tpetra::CombineMode /* combineMode */)
1377  {
1378  using Teuchos::Comm;
1379  using Teuchos::RCP;
1380  using std::endl;
1381  const char prefix[] = "Tpetra::Details::CooMatrix::unpackAndCombine: ";
1382  const char suffix[] = " This should never happen. "
1383  "Please report this bug to the Tpetra developers.";
1384 
1385  TEUCHOS_ASSERT( ! importLIDs.need_sync_host () );
1386  auto importLIDs_h = importLIDs.view_host ();
1387 
1388  const std::size_t numImports = importLIDs.extent (0);
1389  if (numImports == 0) {
1390  return; // nothing to receive
1391  }
1392  else if (imports.extent (0) == 0) {
1393  std::ostream& err = this->markLocalErrorAndGetStream ();
1394  err << prefix << "importLIDs.extent(0) = " << numImports << " != 0, "
1395  << "but imports.extent(0) = 0. This doesn't make sense, because "
1396  << "for every incoming LID, CooMatrix packs at least the count of "
1397  << "triples associated with that LID, even if the count is zero. "
1398  << "importLIDs = [";
1399  for (std::size_t k = 0; k < numImports; ++k) {
1400  err << importLIDs_h[k];
1401  if (k + 1 < numImports) {
1402  err << ", ";
1403  }
1404  }
1405  err << "]. " << suffix << endl;
1406  return;
1407  }
1408 
1409  RCP<const Comm<int> > comm = this->getMap ().is_null () ?
1410  Teuchos::null : this->getMap ()->getComm ();
1411  if (comm.is_null () || comm->getSize () == 1) {
1412  if (numImports != static_cast<size_t> (0)) {
1413  std::ostream& err = this->markLocalErrorAndGetStream ();
1414  err << prefix << "The input communicator is either null or "
1415  "has only one process, but numImports = " << numImports << " != 0."
1416  << suffix << endl;
1417  return;
1418  }
1419  // Don't go into the rest of this method unless there are
1420  // actually processes other than the calling process. This is
1421  // because the pack and unpack functions only have nonstub
1422  // implementations if building with MPI.
1423  return;
1424  }
1425 
1426  // Make sure that the length of 'imports' fits in int.
1427  // This is ultimately an MPI restriction.
1428  if (static_cast<size_t> (imports.extent (0)) >
1429  static_cast<size_t> (std::numeric_limits<int>::max ())) {
1430  std::ostream& err = this->markLocalErrorAndGetStream ();
1431  err << prefix << "imports.extent(0) = "
1432  << imports.extent (0) << " does not fit in int." << endl;
1433  return;
1434  }
1435  const int inBufSize = static_cast<int> (imports.extent (0));
1436 
1437  if (imports.need_sync_host ()) {
1438  imports.sync_host ();
1439  }
1440  if (numPacketsPerLID.need_sync_host ()) {
1441  numPacketsPerLID.sync_host ();
1442  }
1443  auto imports_h = imports.view_host ();
1444  auto numPacketsPerLID_h = numPacketsPerLID.view_host ();
1445 
1446  // FIXME (mfh 17,24 Jan 2017) packTriples wants three arrays, not a
1447  // single array of structs. For now, we just copy.
1448  std::vector<GO> gblRowInds;
1449  std::vector<GO> gblColInds;
1450  std::vector<SC> vals;
1451 
1452  const packet_type* inBuf = imports_h.data ();
1453  int inBufCurPos = 0;
1454  size_t numInvalidRowInds = 0;
1455  int errCode = 0;
1456  std::ostringstream errStrm; // for unpack* error output.
1457  for (size_t k = 0; k < numImports; ++k) {
1458  const LO lclRow = importLIDs_h(k);
1459  const GO gblRow = this->map_->getGlobalElement (lclRow);
1460  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1461  ++numInvalidRowInds;
1462  continue;
1463  }
1464 
1465  // Remember where we were, so we don't overrun the buffer
1466  // length. inBufCurPos is an in/out argument of unpackTriples*.
1467  const int origInBufCurPos = inBufCurPos;
1468 
1469  int numEnt = 0; // output argument of unpackTriplesCount
1470  errCode = unpackTriplesCount (inBuf, inBufSize, inBufCurPos,
1471  numEnt, *comm, &errStrm);
1472  if (errCode != 0 || numEnt < 0 || inBufCurPos < origInBufCurPos) {
1473  std::ostream& err = this->markLocalErrorAndGetStream ();
1474 
1475  err << prefix << "In unpack loop, k=" << k << ": ";
1476  if (errCode != 0) {
1477  err << " unpackTriplesCount returned errCode = " << errCode
1478  << " != 0." << endl;
1479  }
1480  if (numEnt < 0) {
1481  err << " unpackTriplesCount returned errCode = 0, but numEnt = "
1482  << numEnt << " < 0." << endl;
1483  }
1484  if (inBufCurPos < origInBufCurPos) {
1485  err << " After unpackTriplesCount, inBufCurPos = " << inBufCurPos
1486  << " < origInBufCurPos = " << origInBufCurPos << "." << endl;
1487  }
1488  err << " unpackTriplesCount report: " << errStrm.str () << endl;
1489  err << suffix << endl;
1490 
1491  // We only continue in a debug build, because the above error
1492  // messages could consume too much memory and cause an
1493  // out-of-memory error, without actually printing. Printing
1494  // everything is useful in a debug build, but not so much in a
1495  // release build.
1496 #ifdef HAVE_TPETRA_DEBUG
1497  // Clear out the current error stream, so we don't accumulate
1498  // over loop iterations.
1499  errStrm.str ("");
1500  continue;
1501 #else
1502  return;
1503 #endif // HAVE_TPETRA_DEBUG
1504  }
1505 
1506  // FIXME (mfh 17,24 Jan 2017) packTriples wants three arrays,
1507  // not a single array of structs. For now, we just copy.
1508  gblRowInds.resize (numEnt);
1509  gblColInds.resize (numEnt);
1510  vals.resize (numEnt);
1511 
1512  errCode = unpackTriples (inBuf, inBufSize, inBufCurPos,
1513  gblRowInds.data (), gblColInds.data (),
1514  vals.data (), numEnt, *comm, &errStrm);
1515  if (errCode != 0) {
1516  std::ostream& err = this->markLocalErrorAndGetStream ();
1517  err << prefix << "unpackTriples returned errCode = "
1518  << errCode << " != 0. It reports: " << errStrm.str ()
1519  << endl;
1520  // We only continue in a debug build, because the above error
1521  // messages could consume too much memory and cause an
1522  // out-of-memory error, without actually printing. Printing
1523  // everything is useful in a debug build, but not so much in a
1524  // release build.
1525 #ifdef HAVE_TPETRA_DEBUG
1526  // Clear out the current error stream, so we don't accumulate
1527  // over loop iterations.
1528  errStrm.str ("");
1529  continue;
1530 #else
1531  return;
1532 #endif // HAVE_TPETRA_DEBUG
1533  }
1534  this->sumIntoGlobalValues (gblRowInds.data (), gblColInds.data (),
1535  vals.data (), numEnt);
1536  }
1537 
1538  // If we found invalid row indices in exportLIDs, go back,
1539  // collect, and report them.
1540  if (numInvalidRowInds != 0) {
1541  // Mark the error now, before we possibly run out of memory.
1542  // The latter could raise an exception (e.g., std::bad_alloc),
1543  // but at least we would get the error state right.
1544  std::ostream& err = this->markLocalErrorAndGetStream ();
1545 
1546  std::vector<std::pair<LO, GO> > invalidRowInds;
1547  for (size_t k = 0; k < numImports; ++k) {
1548  const LO lclRow = importLIDs_h(k);
1549  const GO gblRow = this->map_->getGlobalElement (lclRow);
1550  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1551  invalidRowInds.push_back ({lclRow, gblRow});
1552  }
1553  }
1554 
1555  err << prefix << "We found " << numInvalidRowInds << " invalid row ind"
1556  << (numInvalidRowInds != static_cast<size_t> (1) ? "ices" : "ex")
1557  << " out of " << numImports << " in importLIDs. Here is the list "
1558  << " of invalid row indices: [";
1559  for (size_t k = 0; k < invalidRowInds.size (); ++k) {
1560  err << "(LID: " << invalidRowInds[k].first << ", GID: "
1561  << invalidRowInds[k].second << ")";
1562  if (k + 1 < invalidRowInds.size ()) {
1563  err << ", ";
1564  }
1565  }
1566  err << "].";
1567  return;
1568  }
1569  }
1570 };
1571 
1572 } // namespace Details
1573 } // namespace Tpetra
1574 
1575 #endif // TPETRA_DETAILS_COOMATRIX_HPP
char packet_type
Type for packing and unpacking data.
int packTriplesCount(const int, char[], const int, int &, const ::Teuchos::Comm< int > &, std::ostream *errStrm)
Pack the count (number) of matrix triples.
void sumIntoGlobalValues(std::initializer_list< GO > gblRowInds, std::initializer_list< GO > gblColInds, std::initializer_list< SC > vals, const std::size_t numEnt)
Initializer-list overload of the above method (which see).
Node node_type
The Node type. If you don&#39;t know what this is, don&#39;t use it.
CooMatrixImpl()=default
Default constructor.
std::string errorMessages() const
The current stream of error messages.
Declaration of a function that prints strings from each process.
virtual void describe(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel=Teuchos::Describable::verbLevel_default) const
Print a descriptiion of this object to the given output stream; overrides Teuchos::Describable method...
void packRow(packet_type outBuf[], const int outBufSize, int &outBufCurPos, const ::Teuchos::Comm< int > &comm, std::vector< GO > &gblRowInds, std::vector< GO > &gblColInds, std::vector< SC > &vals, const GO gblRow) const
Pack the given row of the matrix.
Function comparing two CooGraphEntry structs, lexicographically, first by row index, then by column index.
void sumIntoGlobalValue(const GO gblRowInd, const GO gblColInd, const SC &val)
Insert one entry locally into the sparse matrix, if it does not exist there yet. If it does exist...
Sparse matrix used only for file input / output.
void buildLocallyIndexedCrs(std::vector< OffsetType > &rowOffsets, LO lclColInds[], SC vals[], std::function< LO(const GO)> gblToLcl) const
Build a locally indexed version of CRS storage.
void fillComplete()
Special version of fillComplete that assumes that the matrix already has a Map, and reuses its commun...
GlobalOrdinal global_ordinal_type
The type of global indices.
virtual ~CooMatrix()
Destructor (virtual for memory safety of derived classes).
void gathervPrint(std::ostream &out, const std::string &s, const Teuchos::Comm< int > &comm)
On Process 0 in the given communicator, print strings from each process in that communicator, in rank order.
void fillComplete(const ::Teuchos::RCP< const ::Teuchos::Comm< int > > &comm)
Tell the matrix that you are done inserting entries locally, and that the matrix should build its Map...
void deep_copy(MultiVector< DS, DL, DG, DN > &dst, const MultiVector< SS, SL, SG, SN > &src)
Copy the contents of the MultiVector src into dst.
std::size_t getLclNumEntries() const
Number of entries in the sparse matrix on the calling process.
void sumIntoGlobalValue(const GO gblRowInd, const GO gblColInd, const SC &val)
Insert one entry locally into the sparse matrix, if it does not exist there yet. If it does exist...
Type of each (row index, column index) pair in the Tpetra::Details::CooMatrix (see below)...
bool localError() const
Whether this object had an error on the calling process.
int unpackTriplesCount(const char[], const int, int &, int &, const ::Teuchos::Comm< int > &, std::ostream *errStrm)
Unpack just the count of triples from the given input buffer.
SC scalar_type
Type of each entry (value) in the sparse matrix.
virtual size_t constantNumberOfPackets() const
By returning 0, tell DistObject that this class may not necessarily have a constant number of &quot;packet...
void sumIntoGlobalValues(const GO gblRowInds[], const GO gblColInds[], const SC vals[], const std::size_t numEnt)
Insert multiple entries locally into the sparse matrix.
CooMatrix(const ::Teuchos::RCP< const map_type > &map)
Constructor that takes a Map.
virtual bool checkSizes(const ::Tpetra::SrcDistObject &source)
Compare the source and target (this) objects for compatibility.
void sumIntoGlobalValues(const GO gblRowInds[], const GO gblColInds[], const SC vals[], const std::size_t numEnt)
Insert multiple entries locally into the sparse matrix.
CombineMode
Rule for combining data in an Import or Export.
Teuchos::RCP< const map_type > map_
The Map over which this object is distributed.
bool reallocDualViewIfNeeded(Kokkos::DualView< ValueType *, DeviceType > &dv, const size_t newSize, const char newLabel[], const size_t tooBigFactor=2, const bool needFenceBeforeRealloc=true)
Reallocate the DualView in/out argument, if needed.
int countPackTriplesCount(const ::Teuchos::Comm< int > &, int &size, std::ostream *errStrm)
Compute the buffer size required by packTriples for packing the number of matrix entries (&quot;triples&quot;)...
::Tpetra::Map< local_ordinal_type, global_ordinal_type, node_type > map_type
Type of the Map specialization to give to the constructor.
Declaration and definition of Tpetra::Details::reallocDualViewIfNeeded, an implementation detail of T...
void forAllEntries(std::function< void(const GO, const GO, const SC &)> f) const
Execute the given function for all entries of the sparse matrix, sequentially (no thread parallelism)...
LocalOrdinal local_ordinal_type
The type of local indices.
int countPackRow(int &numPackets, const GO gblRow, const ::Teuchos::Comm< int > &comm, std::ostream *errStrm=NULL) const
Count the number of packets (bytes, in this case) needed to pack the given row of the matrix...
std::size_t getLclNumEntries() const
Number of entries in the sparse matrix on the calling process.
GO getMyGlobalRowIndices(std::vector< GO > &rowInds) const
Get the global row indices on this process, sorted and made unique, and return the minimum global row...
typename::Tpetra::DistObject< char, LO, GO, NT >::buffer_device_type buffer_device_type
Kokkos::Device specialization for DistObject communication buffers.
void mergeIntoRow(const GO tgtGblRow, const CooMatrixImpl< SC, GO > &src, const GO srcGblRow)
Into global row tgtGblRow of *this, merge global row srcGblRow of src.
char packet_type
This class transfers data as bytes (MPI_BYTE).
virtual std::string description() const
One-line descriptiion of this object; overrides Teuchos::Describable method.
virtual Teuchos::RCP< const map_type > getMap() const
The Map describing the parallel distribution of this object.
int unpackTriples(const char[], const int, int &, OrdinalType[], OrdinalType[], ScalarType[], const int, const ::Teuchos::Comm< int > &, std::ostream *errStrm=NULL)
Unpack matrix entries (&quot;triples&quot; (i, j, A(i,j))) from the given input buffer.
Base class for distributed Tpetra objects that support data redistribution.
Implementation detail of Tpetra::Details::CooMatrix (which see below).
int packTriples(const OrdinalType[], const OrdinalType[], const ScalarType[], const int, char[], const int, int &, const ::Teuchos::Comm< int > &, std::ostream *errStrm=NULL)
Pack matrix entries (&quot;triples&quot; (i, j, A(i,j))) into the given output buffer.