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 //
4 // Tpetra: Templated Linear Algebra Services Package
5 // Copyright (2008) Sandia Corporation
6 //
7 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
8 // the U.S. Government retains certain rights in this software.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
38 //
39 // ************************************************************************
40 // @HEADER
41 
42 #ifndef TPETRA_DETAILS_COOMATRIX_HPP
43 #define TPETRA_DETAILS_COOMATRIX_HPP
44 
49 
50 #include "TpetraCore_config.h"
51 #include "Tpetra_Details_PackTriples.hpp"
52 #include "Tpetra_DistObject.hpp"
55 #include "Teuchos_TypeNameTraits.hpp"
56 
57 #include <initializer_list>
58 #include <map>
59 #include <memory>
60 #include <string>
61 #include <type_traits>
62 #include <vector>
63 
64 
65 namespace Tpetra {
66 namespace Details {
67 
68 // Implementation details of Tpetra::Details.
69 // So, users REALLY should not use anything in here.
70 namespace Impl {
71 
74 template<class IndexType>
75 struct CooGraphEntry {
76  IndexType row;
77  IndexType col;
78 };
79 
84 template<class IndexType>
86  bool
87  operator () (const CooGraphEntry<IndexType>& x,
88  const CooGraphEntry<IndexType>& y) const
89  {
90  return (x.row < y.row) || (x.row == y.row && x.col < y.col);
91  }
92 };
93 
96 template<class SC, class GO>
98 private:
106  typedef std::map<CooGraphEntry<GO>, SC,
107  CompareCooGraphEntries<GO> > entries_type;
108 
111  entries_type entries_;
112 
113 public:
115  typedef char packet_type;
116 
118  CooMatrixImpl () = default;
119 
126  void
127  sumIntoGlobalValue (const GO gblRowInd,
128  const GO gblColInd,
129  const SC& val)
130  {
131  // There's no sense in worrying about the insertion hint, since
132  // indices may be all over the place. Users make no promises of
133  // sorting or locality of input.
134  CooGraphEntry<GO> ij {gblRowInd, gblColInd};
135  auto result = this->entries_.insert ({ij, val});
136  if (! result.second) { // already in the map
137  result.first->second += val; // sum in the new value
138  }
139  }
140 
149  void
150  sumIntoGlobalValues (const GO gblRowInds[],
151  const GO gblColInds[],
152  const SC vals[],
153  const std::size_t numEnt)
154  {
155  for (std::size_t k = 0; k < numEnt; ++k) {
156  // There's no sense in worrying about the insertion hint, since
157  // indices may be all over the place. Users make no promises of
158  // sorting or locality of input.
159  CooGraphEntry<GO> ij {gblRowInds[k], gblColInds[k]};
160  const SC val = vals[k];
161  auto result = this->entries_.insert ({ij, val});
162  if (! result.second) { // already in the map
163  result.first->second += val; // sum in the new value
164  }
165  }
166  }
167 
169  std::size_t
171  {
172  return this->entries_.size ();
173  }
174 
178  void
179  forAllEntries (std::function<void (const GO, const GO, const SC&)> f) const
180  {
181  for (auto iter = this->entries_.begin ();
182  iter != this->entries_.end (); ++iter) {
183  f (iter->first.row, iter->first.col, iter->second);
184  }
185  }
186 
192  void
193  mergeIntoRow (const GO tgtGblRow,
194  const CooMatrixImpl<SC, GO>& src,
195  const GO srcGblRow)
196  {
197  // const char prefix[] =
198  // "Tpetra::Details::Impl::CooMatrixImpl::mergeIntoRow: ";
199 
200  entries_type& tgtEntries = this->entries_;
201  const entries_type& srcEntries = src.entries_;
202 
203  // Find all entries with the given global row index. The min GO
204  // value is guaranteed to be the least possible column index, so
205  // beg1 is a lower bound for all (row index, column index) pairs.
206  // Lower bound is inclusive; upper bound is exclusive.
207  auto srcBeg = srcEntries.lower_bound ({srcGblRow, std::numeric_limits<GO>::min ()});
208  auto srcEnd = srcEntries.upper_bound ({srcGblRow+1, std::numeric_limits<GO>::min ()});
209  auto tgtBeg = tgtEntries.lower_bound ({tgtGblRow, std::numeric_limits<GO>::min ()});
210  auto tgtEnd = tgtEntries.upper_bound ({tgtGblRow+1, std::numeric_limits<GO>::min ()});
211 
212  // Don't waste time iterating over the current row of *this, if
213  // the current row of src is empty.
214  if (srcBeg != srcEnd) {
215  for (auto tgtCur = tgtBeg; tgtCur != tgtEnd; ++tgtCur) {
216  auto srcCur = srcBeg;
217  while (srcCur != srcEnd && srcCur->first.col < tgtCur->first.col) {
218  ++srcCur;
219  }
220  // At this point, one of the following is true:
221  //
222  // 1. srcCur == srcEnd, thus nothing more to insert.
223  // 2. srcCur->first.col > tgtCur->first.col, thus the current
224  // row of src has no entry matching tgtCur->first, so we
225  // have to insert it. Insertion does not invalidate
226  // tgtEntries iterators, and neither srcEntries nor
227  // tgtEntries have duplicates, so this is safe.
228  // 3. srcCur->first.col == tgtCur->first.col, so add the two entries.
229  if (srcCur != srcEnd) {
230  if (srcCur->first.col == tgtCur->first.col) {
231  tgtCur->second += srcCur->second;
232  }
233  else {
234  // tgtCur is (optimally) right before where we want to put
235  // the new entry, since srcCur points to the first entry
236  // in srcEntries whose column index is greater than that
237  // of the entry to which tgtCur points.
238  (void) tgtEntries.insert (tgtCur, *srcCur);
239  }
240  } // if srcCur != srcEnd
241  } // for each entry in the current row (lclRow) of *this
242  } // if srcBeg != srcEnd
243  }
244 
254  int
255  countPackRow (int& numPackets,
256  const GO gblRow,
257  const ::Teuchos::Comm<int>& comm,
258  std::ostream* errStrm = NULL) const
259  {
260  using ::Tpetra::Details::countPackTriples;
261  using ::Tpetra::Details::countPackTriplesCount;
262  using std::endl;
263  const char prefix[] = "Tpetra::Details::Impl::CooMatrixImpl::countPackRow: ";
264 #ifdef HAVE_TPETRACORE_MPI
265  int errCode = MPI_SUCCESS;
266 #else
267  int errCode = 0;
268 #endif // HAVE_TPETRACORE_MPI
269 
270  // Count the number of entries in the given row.
271  const GO minGO = std::numeric_limits<GO>::min ();
272  auto beg = this->entries_.lower_bound ({gblRow, minGO});
273  auto end = this->entries_.upper_bound ({gblRow+1, minGO});
274  int numEnt = 0;
275  for (auto iter = beg; iter != end; ++iter) {
276  ++numEnt;
277  if (numEnt == std::numeric_limits<int>::max ()) {
278  if (errStrm != NULL) {
279  *errStrm << prefix << "In (global) row " << gblRow << ", the number "
280  "of entries thus far, numEnt = " << numEnt << ", has reached the "
281  "maximum int value. We need to store this count as int for MPI's "
282  "sake." << endl;
283  }
284  return -1;
285  }
286  }
287 
288  int numPacketsForCount = 0; // output argument of countPackTriplesCount
289  {
290  errCode =
291  countPackTriplesCount (comm, numPacketsForCount, errStrm);
292  if (errCode != 0) {
293  if (errStrm != NULL) {
294  *errStrm << prefix << "countPackTriplesCount "
295  "returned errCode = " << errCode << " != 0." << endl;
296  }
297  return errCode;
298  }
299  if (numPacketsForCount < 0) {
300  if (errStrm != NULL) {
301  *errStrm << prefix << "countPackTriplesCount returned "
302  "numPacketsForCount = " << numPacketsForCount << " < 0." << endl;
303  }
304  return -1;
305  }
306  }
307 
308  int numPacketsForTriples = 0; // output argument of countPackTriples
309  {
310  errCode = countPackTriples<SC, GO> (numEnt, comm, numPacketsForTriples);
311  TEUCHOS_TEST_FOR_EXCEPTION
312  (errCode != 0, std::runtime_error, prefix << "countPackTriples "
313  "returned errCode = " << errCode << " != 0.");
314  TEUCHOS_TEST_FOR_EXCEPTION
315  (numPacketsForTriples < 0, std::logic_error, prefix << "countPackTriples "
316  "returned numPacketsForTriples = " << numPacketsForTriples << " < 0.");
317  }
318 
319  numPackets = numPacketsForCount + numPacketsForTriples;
320  return errCode;
321  }
322 
337  void
338  packRow (packet_type outBuf[],
339  const int outBufSize,
340  int& outBufCurPos, // in/out argument
341  const ::Teuchos::Comm<int>& comm,
342  std::vector<GO>& gblRowInds,
343  std::vector<GO>& gblColInds,
344  std::vector<SC>& vals,
345  const GO gblRow) const
346  {
347  using ::Tpetra::Details::packTriples;
348  using ::Tpetra::Details::packTriplesCount;
349  const char prefix[] = "Tpetra::Details::Impl::CooMatrixImpl::packRow: ";
350 
351  const GO minGO = std::numeric_limits<GO>::min ();
352  auto beg = this->entries_.lower_bound ({gblRow, minGO});
353  auto end = this->entries_.upper_bound ({gblRow+1, minGO});
354 
355  // This doesn't actually deallocate. Only swapping with an empty
356  // std::vector does that.
357  gblRowInds.resize (0);
358  gblColInds.resize (0);
359  vals.resize (0);
360 
361  int numEnt = 0;
362  for (auto iter = beg; iter != end; ++iter) {
363  gblRowInds.push_back (iter->first.row);
364  gblColInds.push_back (iter->first.col);
365  vals.push_back (iter->second);
366  ++numEnt;
367  TEUCHOS_TEST_FOR_EXCEPTION
368  (numEnt == std::numeric_limits<int>::max (), std::runtime_error, prefix
369  << "In (global) row " << gblRow << ", the number of entries thus far, "
370  "numEnt = " << numEnt << ", has reached the maximum int value. "
371  "We need to store this count as int for MPI's sake.");
372  }
373 
374  {
375  const int errCode =
376  packTriplesCount (numEnt, outBuf, outBufSize, outBufCurPos, comm);
377  TEUCHOS_TEST_FOR_EXCEPTION
378  (errCode != 0, std::runtime_error, prefix
379  << "In (global) row " << gblRow << ", packTriplesCount returned "
380  "errCode = " << errCode << " != 0.");
381  }
382  {
383  const int errCode =
384  packTriples (gblRowInds.data (),
385  gblColInds.data (),
386  vals.data (),
387  numEnt,
388  outBuf,
389  outBufSize,
390  outBufCurPos, // in/out argument
391  comm);
392  TEUCHOS_TEST_FOR_EXCEPTION
393  (errCode != 0, std::runtime_error, prefix << "In (global) row "
394  << gblRow << ", packTriples returned errCode = " << errCode
395  << " != 0.");
396  }
397  }
398 
406  GO
407  getMyGlobalRowIndices (std::vector<GO>& rowInds) const
408  {
409  rowInds.clear ();
410 
411  GO lclMinRowInd = std::numeric_limits<GO>::max (); // compute local min
412  for (typename entries_type::const_iterator iter = this->entries_.begin ();
413  iter != this->entries_.end (); ++iter) {
414  const GO rowInd = iter->first.row;
415  if (rowInd < lclMinRowInd) {
416  lclMinRowInd = rowInd;
417  }
418  if (rowInds.empty () || rowInds.back () != rowInd) {
419  rowInds.push_back (rowInd); // don't insert duplicates
420  }
421  }
422  return lclMinRowInd;
423  }
424 
425  template<class OffsetType>
426  void
427  buildCrs (std::vector<OffsetType>& rowOffsets,
428  GO gblColInds[],
429  SC vals[]) const
430  {
431  static_assert (std::is_integral<OffsetType>::value,
432  "OffsetType must be a built-in integer type.");
433 
434  // clear() doesn't generally free storage; it just resets the
435  // length. Thus, we reuse any existing storage here.
436  rowOffsets.clear ();
437 
438  const std::size_t numEnt = this->getLclNumEntries ();
439  if (numEnt == 0) {
440  rowOffsets.push_back (0);
441  }
442  else {
443  typename entries_type::const_iterator iter = this->entries_.begin ();
444  GO prevGblRowInd = iter->first.row;
445 
446  OffsetType k = 0;
447  for ( ; iter != this->entries_.end (); ++iter, ++k) {
448  const GO gblRowInd = iter->first.row;
449  if (k == 0 || gblRowInd != prevGblRowInd) {
450  // The row offsets array always has at least one entry. The
451  // first entry is always zero, and the last entry is always
452  // the number of matrix entries.
453  rowOffsets.push_back (k);
454  prevGblRowInd = gblRowInd;
455  }
456  gblColInds[k] = iter->first.col;
457 
458  static_assert (std::is_same<typename std::decay<decltype (iter->second)>::type, SC>::value,
459  "The type of iter->second != SC.");
460  vals[k] = iter->second;
461  }
462  rowOffsets.push_back (static_cast<OffsetType> (numEnt));
463  }
464  }
465 
478  template<class OffsetType, class LO>
479  void
480  buildLocallyIndexedCrs (std::vector<OffsetType>& rowOffsets,
481  LO lclColInds[],
482  SC vals[],
483  std::function<LO (const GO)> gblToLcl) const
484  {
485  static_assert (std::is_integral<OffsetType>::value,
486  "OffsetType must be a built-in integer type.");
487  static_assert (std::is_integral<LO>::value,
488  "LO must be a built-in integer type.");
489 
490  // clear() doesn't generally free storage; it just resets the
491  // length. Thus, we reuse any existing storage here.
492  rowOffsets.clear ();
493 
494  const std::size_t numEnt = this->getLclNumEntries ();
495  if (numEnt == 0) {
496  rowOffsets.push_back (0);
497  }
498  else {
499  typename entries_type::const_iterator iter = this->entries_.begin ();
500  GO prevGblRowInd = iter->first.row;
501 
502  OffsetType k = 0;
503  for ( ; iter != this->entries_.end (); ++iter, ++k) {
504  const GO gblRowInd = iter->first.row;
505  if (k == 0 || gblRowInd != prevGblRowInd) {
506  // The row offsets array always has at least one entry. The
507  // first entry is always zero, and the last entry is always
508  // the number of matrix entries.
509  rowOffsets.push_back (k);
510  prevGblRowInd = gblRowInd;
511  }
512  lclColInds[k] = gblToLcl (iter->first.col);
513  vals[k] = iter->second;
514  }
515  rowOffsets.push_back (static_cast<OffsetType> (numEnt));
516  }
517  }
518 };
519 
520 } // namespace Impl
521 
570 template<class SC,
574 class CooMatrix : public ::Tpetra::DistObject<char, LO, GO, NT> {
575 public:
577  typedef char packet_type;
579  typedef SC scalar_type;
580  typedef LO local_ordinal_type;
581  typedef GO global_ordinal_type;
582  typedef NT node_type;
583  typedef typename NT::device_type device_type;
585  typedef ::Tpetra::Map<local_ordinal_type, global_ordinal_type, node_type> map_type;
586 
587 private:
589  typedef ::Tpetra::DistObject<packet_type, local_ordinal_type,
590  global_ordinal_type, node_type> base_type;
591 
594 
595 public:
602  base_type (::Teuchos::null),
603  localError_ (new bool (false)),
604  errs_ (new std::shared_ptr<std::ostringstream> ()) // ptr to a null ptr
605  {}
606 
614  CooMatrix (const ::Teuchos::RCP<const map_type>& map) :
615  base_type (map),
616  localError_ (new bool (false)),
617  errs_ (new std::shared_ptr<std::ostringstream> ()) // ptr to a null ptr
618  {}
619 
621  virtual ~CooMatrix () {}
622 
629  void
630  sumIntoGlobalValue (const GO gblRowInd,
631  const GO gblColInd,
632  const SC& val)
633  {
634  this->impl_.sumIntoGlobalValue (gblRowInd, gblColInd, val);
635  }
636 
645  void
646  sumIntoGlobalValues (const GO gblRowInds[],
647  const GO gblColInds[],
648  const SC vals[],
649  const std::size_t numEnt)
650  {
651  this->impl_.sumIntoGlobalValues (gblRowInds, gblColInds, vals, numEnt);
652  }
653 
655  void
656  sumIntoGlobalValues (std::initializer_list<GO> gblRowInds,
657  std::initializer_list<GO> gblColInds,
658  std::initializer_list<SC> vals,
659  const std::size_t numEnt)
660  {
661  this->impl_.sumIntoGlobalValues (gblRowInds.begin (), gblColInds.begin (),
662  vals.begin (), numEnt);
663  }
664 
666  std::size_t
668  {
669  return this->impl_.getLclNumEntries ();
670  }
671 
672  template<class OffsetType>
673  void
674  buildCrs (::Kokkos::View<OffsetType*, device_type>& rowOffsets,
675  ::Kokkos::View<GO*, device_type>& gblColInds,
676  ::Kokkos::View<typename ::Kokkos::Details::ArithTraits<SC>::val_type*, device_type>& vals)
677  {
678  static_assert (std::is_integral<OffsetType>::value,
679  "OffsetType must be a built-in integer type.");
680  using ::Kokkos::create_mirror_view;
682  using ::Kokkos::View;
683  typedef typename ::Kokkos::Details::ArithTraits<SC>::val_type ISC;
684 
685  const std::size_t numEnt = this->getLclNumEntries ();
686 
687  gblColInds = View<GO*, device_type> ("gblColInds", numEnt);
688  vals = View<ISC*, device_type> ("vals", numEnt);
689  auto gblColInds_h = create_mirror_view (gblColInds);
690  auto vals_h = create_mirror_view (vals);
691 
692  std::vector<std::size_t> rowOffsetsSV;
693  this->impl_.buildCrs (rowOffsetsSV,
694  gblColInds_h.data (),
695  vals_h.data ());
696  rowOffsets =
697  View<OffsetType*, device_type> ("rowOffsets", rowOffsetsSV.size ());
698  typename View<OffsetType*, device_type>::HostMirror
699  rowOffsets_h (rowOffsetsSV.data (), rowOffsetsSV.size ());
700  deep_copy (rowOffsets, rowOffsets_h);
701 
702  deep_copy (gblColInds, gblColInds_h);
703  deep_copy (vals, vals_h);
704  }
705 
716  void
717  fillComplete (const ::Teuchos::RCP<const ::Teuchos::Comm<int> >& comm)
718  {
719  if (comm.is_null ()) {
720  this->map_ = ::Teuchos::null;
721  }
722  else {
723  this->map_ = this->buildMap (comm);
724  }
725  }
726 
735  void
737  {
738  TEUCHOS_TEST_FOR_EXCEPTION
739  (this->getMap ().is_null (), std::runtime_error, "Tpetra::Details::"
740  "CooMatrix::fillComplete: This object does not yet have a Map. "
741  "You must call the version of fillComplete "
742  "that takes an input communicator.");
743  this->fillComplete (this->getMap ()->getComm ());
744  }
745 
760  bool localError () const {
761  return *localError_;
762  }
763 
781  std::string errorMessages () const {
782  return ((*errs_).get () == NULL) ? std::string ("") : (*errs_)->str ();
783  }
784 
785 private:
798  std::shared_ptr<bool> localError_;
799 
807  std::shared_ptr<std::shared_ptr<std::ostringstream> > errs_;
808 
810  std::ostream&
811  markLocalErrorAndGetStream ()
812  {
813  * (this->localError_) = true;
814  if ((*errs_).get () == NULL) {
815  *errs_ = std::shared_ptr<std::ostringstream> (new std::ostringstream ());
816  }
817  return **errs_;
818  }
819 
820 public:
823  virtual std::string description () const {
824  using Teuchos::TypeNameTraits;
825 
826  std::ostringstream os;
827  os << "\"Tpetra::Details::CooMatrix\": {"
828  << "SC: " << TypeNameTraits<SC>::name ()
829  << ", LO: " << TypeNameTraits<LO>::name ()
830  << ", GO: " << TypeNameTraits<GO>::name ()
831  << ", NT: " << TypeNameTraits<NT>::name ();
832  if (this->getObjectLabel () != "") {
833  os << ", Label: \"" << this->getObjectLabel () << "\"";
834  }
835  os << ", Has Map: " << (this->map_.is_null () ? "false" : "true")
836  << "}";
837  return os.str ();
838  }
839 
842  virtual void
843  describe (Teuchos::FancyOStream& out,
844  const Teuchos::EVerbosityLevel verbLevel =
845  Teuchos::Describable::verbLevel_default) const
846  {
847  using ::Tpetra::Details::gathervPrint;
848  using ::Teuchos::EVerbosityLevel;
849  using ::Teuchos::OSTab;
850  using ::Teuchos::TypeNameTraits;
851  using ::Teuchos::VERB_DEFAULT;
852  using ::Teuchos::VERB_LOW;
853  using ::Teuchos::VERB_MEDIUM;
854  using std::endl;
855 
856  const auto vl = (verbLevel == VERB_DEFAULT) ? VERB_LOW : verbLevel;
857 
858  auto comm = this->getMap ().is_null () ?
859  Teuchos::null : this->getMap ()->getComm ();
860  const int myRank = comm.is_null () ? 0 : comm->getRank ();
861  // const int numProcs = comm.is_null () ? 1 : comm->getSize ();
862 
863  if (vl != Teuchos::VERB_NONE) {
864  // Convention is to start describe() implementations with a tab.
865  OSTab tab0 (out);
866  if (myRank == 0) {
867  out << "\"Tpetra::Details::CooMatrix\":" << endl;
868  }
869  OSTab tab1 (out);
870  if (myRank == 0) {
871  out << "Template parameters:" << endl;
872  {
873  OSTab tab2 (out);
874  out << "SC: " << TypeNameTraits<SC>::name () << endl
875  << "LO: " << TypeNameTraits<LO>::name () << endl
876  << "GO: " << TypeNameTraits<GO>::name () << endl
877  << "NT: " << TypeNameTraits<NT>::name () << endl;
878  }
879  if (this->getObjectLabel () != "") {
880  out << "Label: \"" << this->getObjectLabel () << "\"" << endl;
881  }
882  out << "Has Map: " << (this->map_.is_null () ? "false" : "true") << endl;
883  } // if myRank == 0
884 
885  // Describe the Map, if it exists.
886  if (! this->map_.is_null ()) {
887  if (myRank == 0) {
888  out << "Map:" << endl;
889  }
890  OSTab tab2 (out);
891  this->map_->describe (out, vl);
892  }
893 
894  // At verbosity > VERB_LOW, each process prints something.
895  if (vl > VERB_LOW) {
896  std::ostringstream os;
897  os << "Process " << myRank << ":" << endl;
898  //OSTab tab3 (os);
899 
900  const std::size_t numEnt = this->impl_.getLclNumEntries ();
901  os << " Local number of entries: " << numEnt << endl;
902  if (vl > VERB_MEDIUM) {
903  os << " Local entries:" << endl;
904  //OSTab tab4 (os);
905  this->impl_.forAllEntries ([&os] (const GO row, const GO col, const SC& val) {
906  os << " {"
907  << "row: " << row
908  << ", col: " << col
909  << ", val: " << val
910  << "}" << endl;
911  });
912  }
913  gathervPrint (out, os.str (), *comm);
914  }
915  } // vl != Teuchos::VERB_NONE
916  }
917 
918 private:
927  Teuchos::RCP<const map_type>
928  buildMap (const ::Teuchos::RCP<const ::Teuchos::Comm<int> >& comm)
929  {
930  using ::Teuchos::outArg;
931  using ::Teuchos::rcp;
932  using ::Teuchos::REDUCE_MIN;
933  using ::Teuchos::reduceAll;
934  typedef ::Tpetra::global_size_t GST;
935  //const char prefix[] = "Tpetra::Details::CooMatrix::buildMap: ";
936 
937  // Processes where comm is null don't participate in the Map.
938  if (comm.is_null ()) {
939  return ::Teuchos::null;
940  }
941 
942  // mfh 17 Jan 2017: We just happen to use row indices, because
943  // that's what Tpetra::CrsMatrix currently uses. That's probably
944  // not the best thing to use, but it's not bad for commonly
945  // encountered matrices. A better more general approach might be
946  // to hash (row index, column index) pairs to a global index. One
947  // could make that unique by doing a parallel scan at map
948  // construction time.
949 
950  std::vector<GO> rowInds;
951  const GO lclMinGblRowInd = this->impl_.getMyGlobalRowIndices (rowInds);
952 
953  // Compute the globally min row index for the "index base."
954  GO gblMinGblRowInd = 0; // output argument
955  reduceAll<int, GO> (*comm, REDUCE_MIN, lclMinGblRowInd,
956  outArg (gblMinGblRowInd));
957  const GO indexBase = gblMinGblRowInd;
958  const GST INV = Tpetra::Details::OrdinalTraits<GST>::invalid ();
959  return rcp (new map_type (INV, rowInds.data (), rowInds.size (),
960  indexBase, comm));
961  }
962 
963 protected:
967  virtual size_t constantNumberOfPackets () const {
968  return static_cast<size_t> (0);
969  }
970 
974  virtual bool
975  checkSizes (const ::Tpetra::SrcDistObject& source)
976  {
977  using std::endl;
979  const char prefix[] = "Tpetra::Details::CooMatrix::checkSizes: ";
980 
981  const this_type* src = dynamic_cast<const this_type* > (&source);
982 
983  if (src == NULL) {
984  std::ostream& err = markLocalErrorAndGetStream ();
985  err << prefix << "The source object of the Import or Export "
986  "must be a CooMatrix with the same template parameters as the "
987  "target object." << endl;
988  }
989  else if (this->map_.is_null ()) {
990  std::ostream& err = markLocalErrorAndGetStream ();
991  err << prefix << "The target object of the Import or Export "
992  "must be a CooMatrix with a nonnull Map." << endl;
993  }
994  return ! (* (this->localError_));
995  }
996 
998  using buffer_device_type =
999  typename ::Tpetra::DistObject<char, LO, GO, NT>::buffer_device_type;
1000 
1001  virtual void
1002  copyAndPermute
1003  (const ::Tpetra::SrcDistObject& sourceObject,
1004  const size_t numSameIDs,
1005  const Kokkos::DualView<const LO*,
1006  buffer_device_type>& permuteToLIDs,
1007  const Kokkos::DualView<const LO*,
1008  buffer_device_type>& permuteFromLIDs)
1009  {
1010  using std::endl;
1012  const char prefix[] = "Tpetra::Details::CooMatrix::copyAndPermute: ";
1013 
1014  // There's no communication in this method, so it's safe just to
1015  // return on error.
1016 
1017  if (* (this->localError_)) {
1018  std::ostream& err = this->markLocalErrorAndGetStream ();
1019  err << prefix << "The target object of the Import or Export is "
1020  "already in an error state." << endl;
1021  return;
1022  }
1023 
1024  const this_type* src = dynamic_cast<const this_type*> (&sourceObject);
1025  if (src == nullptr) {
1026  std::ostream& err = this->markLocalErrorAndGetStream ();
1027  err << prefix << "Input argument 'sourceObject' is not a CooMatrix."
1028  << endl;
1029  return;
1030  }
1031 
1032  const size_t numPermuteIDs =
1033  static_cast<size_t> (permuteToLIDs.extent (0));
1034  if (numPermuteIDs != static_cast<size_t> (permuteFromLIDs.extent (0))) {
1035  std::ostream& err = this->markLocalErrorAndGetStream ();
1036  err << prefix << "permuteToLIDs.extent(0) = "
1037  << numPermuteIDs << " != permuteFromLIDs.extent(0) = "
1038  << permuteFromLIDs.extent (0) << "." << endl;
1039  return;
1040  }
1041  if (sizeof (int) <= sizeof (size_t) &&
1042  numPermuteIDs > static_cast<size_t> (std::numeric_limits<int>::max ())) {
1043  std::ostream& err = this->markLocalErrorAndGetStream ();
1044  err << prefix << "numPermuteIDs = " << numPermuteIDs
1045  << ", a size_t, overflows int." << endl;
1046  return;
1047  }
1048 
1049  // Even though this is an std::set, we can start with numSameIDs
1050  // just by iterating through the first entries of the set.
1051 
1052  if (sizeof (int) <= sizeof (size_t) &&
1053  numSameIDs > static_cast<size_t> (std::numeric_limits<int>::max ())) {
1054  std::ostream& err = this->markLocalErrorAndGetStream ();
1055  err << prefix << "numSameIDs = " << numSameIDs
1056  << ", a size_t, overflows int." << endl;
1057  return;
1058  }
1059 
1060  //
1061  // Copy in entries from any initial rows with the same global row indices.
1062  //
1063  const LO numSame = static_cast<int> (numSameIDs);
1064  // Count of local row indices encountered here with invalid global
1065  // row indices. If nonzero, something went wrong. If something
1066  // did go wrong, we'll defer responding until the end of this
1067  // method, so we can print as much useful info as possible.
1068  LO numInvalidSameRows = 0;
1069  for (LO lclRow = 0; lclRow < numSame; ++lclRow) {
1070  // All numSame initial rows have the same global index in both
1071  // source and target Maps, so we only need to convert to global
1072  // once.
1073  const GO gblRow = this->map_->getGlobalElement (lclRow);
1074  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1075  ++numInvalidSameRows;
1076  continue;
1077  }
1078  else {
1079  this->impl_.mergeIntoRow (gblRow, src->impl_, gblRow);
1080  }
1081  }
1082 
1083  //
1084  // Copy in entries from remaining rows that are permutations, that
1085  // is, that live in both the source and target Maps, but aren't
1086  // included in the "same" list (see above).
1087  //
1088  const LO numPermute = static_cast<int> (numPermuteIDs);
1089  // Count of local "from" row indices encountered here with invalid
1090  // global row indices. If nonzero, something went wrong. If
1091  // something did go wrong, we'll defer responding until the end of
1092  // this method, so we can print as much useful info as possible.
1093  LO numInvalidRowsFrom = 0;
1094  // Count of local "to" row indices encountered here with invalid
1095  // global row indices. If nonzero, something went wrong. If
1096  // something did go wrong, we'll defer responding until the end of
1097  // this method, so we can print as much useful info as possible.
1098  LO numInvalidRowsTo = 0;
1099 
1100  TEUCHOS_ASSERT( ! permuteFromLIDs.need_sync_host () );
1101  TEUCHOS_ASSERT( ! permuteToLIDs.need_sync_host () );
1102  auto permuteFromLIDs_h = permuteFromLIDs.view_host ();
1103  auto permuteToLIDs_h = permuteToLIDs.view_host ();
1104 
1105  for (LO k = 0; k < numPermute; ++k) {
1106  const LO lclRowFrom = permuteFromLIDs_h[k];
1107  const LO lclRowTo = permuteToLIDs_h[k];
1108  const GO gblRowFrom = src->map_->getGlobalElement (lclRowFrom);
1109  const GO gblRowTo = this->map_->getGlobalElement (lclRowTo);
1110 
1111  bool bothConversionsValid = true;
1112  if (gblRowFrom == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1113  ++numInvalidRowsFrom;
1114  bothConversionsValid = false;
1115  }
1116  if (gblRowTo == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1117  ++numInvalidRowsTo;
1118  bothConversionsValid = false;
1119  }
1120  if (bothConversionsValid) {
1121  this->impl_.mergeIntoRow (gblRowTo, src->impl_, gblRowFrom);
1122  }
1123  }
1124 
1125  // Print info if any errors occurred.
1126  if (numInvalidSameRows != 0 || numInvalidRowsFrom != 0 ||
1127  numInvalidRowsTo != 0) {
1128  // Collect and print all the invalid input row indices, for the
1129  // "same," "from," and "to" lists.
1130  std::vector<std::pair<LO, GO> > invalidSameRows;
1131  invalidSameRows.reserve (numInvalidSameRows);
1132  std::vector<std::pair<LO, GO> > invalidRowsFrom;
1133  invalidRowsFrom.reserve (numInvalidRowsFrom);
1134  std::vector<std::pair<LO, GO> > invalidRowsTo;
1135  invalidRowsTo.reserve (numInvalidRowsTo);
1136 
1137  for (LO lclRow = 0; lclRow < numSame; ++lclRow) {
1138  // All numSame initial rows have the same global index in both
1139  // source and target Maps, so we only need to convert to global
1140  // once.
1141  const GO gblRow = this->map_->getGlobalElement (lclRow);
1142  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1143  invalidSameRows.push_back ({lclRow, gblRow});
1144  }
1145  }
1146 
1147  for (LO k = 0; k < numPermute; ++k) {
1148  const LO lclRowFrom = permuteFromLIDs_h[k];
1149  const LO lclRowTo = permuteToLIDs_h[k];
1150  const GO gblRowFrom = src->map_->getGlobalElement (lclRowFrom);
1151  const GO gblRowTo = this->map_->getGlobalElement (lclRowTo);
1152 
1153  if (gblRowFrom == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1154  invalidRowsFrom.push_back ({lclRowFrom, gblRowFrom});
1155  }
1156  if (gblRowTo == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1157  invalidRowsTo.push_back ({lclRowTo, gblRowTo});
1158  }
1159  }
1160 
1161  std::ostringstream os;
1162  if (numInvalidSameRows != 0) {
1163  os << "Invalid permute \"same\" (local, global) index pairs: [";
1164  for (std::size_t k = 0; k < invalidSameRows.size (); ++k) {
1165  const auto& p = invalidSameRows[k];
1166  os << "(" << p.first << "," << p.second << ")";
1167  if (k + 1 < invalidSameRows.size ()) {
1168  os << ", ";
1169  }
1170  }
1171  os << "]" << std::endl;
1172  }
1173  if (numInvalidRowsFrom != 0) {
1174  os << "Invalid permute \"from\" (local, global) index pairs: [";
1175  for (std::size_t k = 0; k < invalidRowsFrom.size (); ++k) {
1176  const auto& p = invalidRowsFrom[k];
1177  os << "(" << p.first << "," << p.second << ")";
1178  if (k + 1 < invalidRowsFrom.size ()) {
1179  os << ", ";
1180  }
1181  }
1182  os << "]" << std::endl;
1183  }
1184  if (numInvalidRowsTo != 0) {
1185  os << "Invalid permute \"to\" (local, global) index pairs: [";
1186  for (std::size_t k = 0; k < invalidRowsTo.size (); ++k) {
1187  const auto& p = invalidRowsTo[k];
1188  os << "(" << p.first << "," << p.second << ")";
1189  if (k + 1 < invalidRowsTo.size ()) {
1190  os << ", ";
1191  }
1192  }
1193  os << "]" << std::endl;
1194  }
1195 
1196  std::ostream& err = this->markLocalErrorAndGetStream ();
1197  err << prefix << os.str ();
1198  return;
1199  }
1200  }
1201 
1202  virtual void
1203  packAndPrepare
1204  (const ::Tpetra::SrcDistObject& sourceObject,
1205  const Kokkos::DualView<const local_ordinal_type*,
1206  buffer_device_type>& exportLIDs,
1207  Kokkos::DualView<packet_type*,
1208  buffer_device_type>& exports,
1209  Kokkos::DualView<size_t*,
1210  buffer_device_type> numPacketsPerLID,
1211  size_t& constantNumPackets,
1212  ::Tpetra::Distributor& /* distor */)
1213  {
1214  using Teuchos::Comm;
1215  using Teuchos::RCP;
1216  using std::endl;
1217  using this_type = CooMatrix<SC, LO, GO, NT>;
1218  const char prefix[] = "Tpetra::Details::CooMatrix::packAndPrepare: ";
1219  const char suffix[] = " This should never happen. "
1220  "Please report this bug to the Tpetra developers.";
1221 
1222  // Tell the caller that different rows may have different numbers
1223  // of matrix entries.
1224  constantNumPackets = 0;
1225 
1226  const this_type* src = dynamic_cast<const this_type*> (&sourceObject);
1227  if (src == nullptr) {
1228  std::ostream& err = this->markLocalErrorAndGetStream ();
1229  err << prefix << "Input argument 'sourceObject' is not a CooMatrix."
1230  << endl;
1231  }
1232  else if (* (src->localError_)) {
1233  std::ostream& err = this->markLocalErrorAndGetStream ();
1234  err << prefix << "The source (input) object of the Import or Export "
1235  "is already in an error state on this process."
1236  << endl;
1237  }
1238  else if (* (this->localError_)) {
1239  std::ostream& err = this->markLocalErrorAndGetStream ();
1240  err << prefix << "The target (output, \"this\") object of the Import "
1241  "or Export is already in an error state on this process." << endl;
1242  }
1243  // Respond to detected error(s) by resizing 'exports' to zero (so
1244  // we won't be tempted to read it later), and filling
1245  // numPacketsPerLID with zeros.
1246  if (* (this->localError_)) {
1247  // Resize 'exports' to zero, so we won't be tempted to read it.
1248  Details::reallocDualViewIfNeeded (exports, 0, "CooMatrix exports");
1249  // Trick to get around const DualView& being const.
1250  {
1251  auto numPacketsPerLID_tmp = numPacketsPerLID;
1252  numPacketsPerLID_tmp.sync_host ();
1253  numPacketsPerLID_tmp.modify_host ();
1254  }
1255  // Fill numPacketsPerLID with zeros.
1256  Kokkos::deep_copy (numPacketsPerLID.h_view, static_cast<size_t> (0));
1257  return;
1258  }
1259 
1260  const size_t numExports = exportLIDs.extent (0);
1261  if (numExports == 0) {
1262  Details::reallocDualViewIfNeeded (exports, 0, exports.h_view.label ());
1263  return; // nothing to send
1264  }
1265  RCP<const Comm<int> > comm = src->getMap ().is_null () ?
1266  Teuchos::null : src->getMap ()->getComm ();
1267  if (comm.is_null () || comm->getSize () == 1) {
1268  if (numExports != static_cast<size_t> (0)) {
1269  std::ostream& err = this->markLocalErrorAndGetStream ();
1270  err << prefix << "The input communicator is either null or "
1271  "has only one process, but numExports = " << numExports << " != 0."
1272  << suffix << endl;
1273  return;
1274  }
1275  // Don't go into the rest of this method unless there are
1276  // actually processes other than the calling process. This is
1277  // because the pack and unpack functions only have nonstub
1278  // implementations if building with MPI.
1279  return;
1280  }
1281 
1282  numPacketsPerLID.sync_host ();
1283  numPacketsPerLID.modify_host ();
1284 
1285  TEUCHOS_ASSERT( ! exportLIDs.need_sync_host () );
1286  auto exportLIDs_h = exportLIDs.view_host ();
1287 
1288  int totalNumPackets = 0;
1289  size_t numInvalidRowInds = 0;
1290  std::ostringstream errStrm; // current loop iteration's error messages
1291  for (size_t k = 0; k < numExports; ++k) {
1292  const LO lclRow = exportLIDs_h[k];
1293  // We're packing the source object's data, so we need to use the
1294  // source object's Map to convert from local to global indices.
1295  const GO gblRow = src->map_->getGlobalElement (lclRow);
1296  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1297  // Mark the error later; just count for now.
1298  ++numInvalidRowInds;
1299  numPacketsPerLID.h_view[k] = 0;
1300  continue;
1301  }
1302 
1303  // Count the number of bytes needed to pack the current row of
1304  // the source object.
1305  int numPackets = 0;
1306  const int errCode =
1307  src->impl_.countPackRow (numPackets, gblRow, *comm, &errStrm);
1308  if (errCode != 0) {
1309  std::ostream& err = this->markLocalErrorAndGetStream ();
1310  err << prefix << errStrm.str () << endl;
1311  numPacketsPerLID.h_view[k] = 0;
1312  continue;
1313  }
1314 
1315  // Make sure that the total number of packets fits in int.
1316  // MPI requires this.
1317  const long long newTotalNumPackets =
1318  static_cast<long long> (totalNumPackets) +
1319  static_cast<long long> (numPackets);
1320  if (newTotalNumPackets >
1321  static_cast<long long> (std::numeric_limits<int>::max ())) {
1322  std::ostream& err = this->markLocalErrorAndGetStream ();
1323  err << prefix << "The new total number of packets "
1324  << newTotalNumPackets << " does not fit in int." << endl;
1325  // At this point, we definitely cannot continue. In order to
1326  // leave the output arguments in a rational state, we zero out
1327  // all remaining entries of numPacketsPerLID before returning.
1328  for (size_t k2 = k; k2 < numExports; ++k2) {
1329  numPacketsPerLID.h_view[k2] = 0;
1330  }
1331  return;
1332  }
1333  numPacketsPerLID.h_view[k] = static_cast<size_t> (numPackets);
1334  totalNumPackets = static_cast<int> (newTotalNumPackets);
1335  }
1336 
1337  // If we found invalid row indices in exportLIDs, go back,
1338  // collect, and report them.
1339  if (numInvalidRowInds != 0) {
1340  std::vector<std::pair<LO, GO> > invalidRowInds;
1341  for (size_t k = 0; k < numExports; ++k) {
1342  const LO lclRow = exportLIDs_h[k];
1343  // We're packing the source object's data, so we need to use
1344  // the source object's Map to convert from local to global
1345  // indices.
1346  const GO gblRow = src->map_->getGlobalElement (lclRow);
1347  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1348  invalidRowInds.push_back ({lclRow, gblRow});
1349  }
1350  }
1351  std::ostringstream os;
1352  os << prefix << "We found " << numInvalidRowInds << " invalid row ind"
1353  << (numInvalidRowInds != static_cast<size_t> (1) ? "ices" : "ex")
1354  << " out of " << numExports << " in exportLIDs. Here is the list "
1355  << " of invalid row indices: [";
1356  for (size_t k = 0; k < invalidRowInds.size (); ++k) {
1357  os << "(LID: " << invalidRowInds[k].first << ", GID: "
1358  << invalidRowInds[k].second << ")";
1359  if (k + 1 < invalidRowInds.size ()) {
1360  os << ", ";
1361  }
1362  }
1363  os << "].";
1364 
1365  std::ostream& err = this->markLocalErrorAndGetStream ();
1366  err << prefix << os.str () << std::endl;
1367  return;
1368  }
1369 
1370  {
1371  const bool reallocated =
1372  Details::reallocDualViewIfNeeded (exports, totalNumPackets,
1373  "CooMatrix exports");
1374  if (reallocated) {
1375  exports.sync_host (); // make sure alloc'd on host
1376  }
1377  }
1378  exports.modify_host ();
1379 
1380  // FIXME (mfh 17 Jan 2017) packTriples wants three arrays, not a
1381  // single array of structs. For now, we just copy.
1382  std::vector<GO> gblRowInds;
1383  std::vector<GO> gblColInds;
1384  std::vector<SC> vals;
1385 
1386  int outBufCurPos = 0;
1387  packet_type* outBuf = exports.h_view.data ();
1388  for (size_t k = 0; k < numExports; ++k) {
1389  const LO lclRow = exportLIDs.h_view[k];
1390  // We're packing the source object's data, so we need to use the
1391  // source object's Map to convert from local to global indices.
1392  const GO gblRow = src->map_->getGlobalElement (lclRow);
1393  // Pack the current row of the source object.
1394  src->impl_.packRow (outBuf, totalNumPackets, outBufCurPos, *comm,
1395  gblRowInds, gblColInds, vals, gblRow);
1396  }
1397  }
1398 
1399  virtual void
1400  unpackAndCombine
1401  (const Kokkos::DualView<const local_ordinal_type*,
1402  buffer_device_type>& importLIDs,
1403  Kokkos::DualView<packet_type*,
1404  buffer_device_type> imports,
1405  Kokkos::DualView<size_t*,
1406  buffer_device_type> numPacketsPerLID,
1407  const size_t /* constantNumPackets */,
1408  ::Tpetra::Distributor& /* distor */,
1409  const ::Tpetra::CombineMode /* combineMode */)
1410  {
1411  using Teuchos::Comm;
1412  using Teuchos::RCP;
1413  using std::endl;
1414  const char prefix[] = "Tpetra::Details::CooMatrix::unpackAndCombine: ";
1415  const char suffix[] = " This should never happen. "
1416  "Please report this bug to the Tpetra developers.";
1417 
1418  TEUCHOS_ASSERT( ! importLIDs.need_sync_host () );
1419  auto importLIDs_h = importLIDs.view_host ();
1420 
1421  const std::size_t numImports = importLIDs.extent (0);
1422  if (numImports == 0) {
1423  return; // nothing to receive
1424  }
1425  else if (imports.extent (0) == 0) {
1426  std::ostream& err = this->markLocalErrorAndGetStream ();
1427  err << prefix << "importLIDs.extent(0) = " << numImports << " != 0, "
1428  << "but imports.extent(0) = 0. This doesn't make sense, because "
1429  << "for every incoming LID, CooMatrix packs at least the count of "
1430  << "triples associated with that LID, even if the count is zero. "
1431  << "importLIDs = [";
1432  for (std::size_t k = 0; k < numImports; ++k) {
1433  err << importLIDs_h[k];
1434  if (k + 1 < numImports) {
1435  err << ", ";
1436  }
1437  }
1438  err << "]. " << suffix << endl;
1439  return;
1440  }
1441 
1442  RCP<const Comm<int> > comm = this->getMap ().is_null () ?
1443  Teuchos::null : this->getMap ()->getComm ();
1444  if (comm.is_null () || comm->getSize () == 1) {
1445  if (numImports != static_cast<size_t> (0)) {
1446  std::ostream& err = this->markLocalErrorAndGetStream ();
1447  err << prefix << "The input communicator is either null or "
1448  "has only one process, but numImports = " << numImports << " != 0."
1449  << suffix << endl;
1450  return;
1451  }
1452  // Don't go into the rest of this method unless there are
1453  // actually processes other than the calling process. This is
1454  // because the pack and unpack functions only have nonstub
1455  // implementations if building with MPI.
1456  return;
1457  }
1458 
1459  // Make sure that the length of 'imports' fits in int.
1460  // This is ultimately an MPI restriction.
1461  if (static_cast<size_t> (imports.extent (0)) >
1462  static_cast<size_t> (std::numeric_limits<int>::max ())) {
1463  std::ostream& err = this->markLocalErrorAndGetStream ();
1464  err << prefix << "imports.extent(0) = "
1465  << imports.extent (0) << " does not fit in int." << endl;
1466  return;
1467  }
1468  const int inBufSize = static_cast<int> (imports.extent (0));
1469 
1470  if (imports.need_sync_host ()) {
1471  imports.sync_host ();
1472  }
1473  if (numPacketsPerLID.need_sync_host ()) {
1474  numPacketsPerLID.sync_host ();
1475  }
1476  auto imports_h = imports.view_host ();
1477  auto numPacketsPerLID_h = numPacketsPerLID.view_host ();
1478 
1479  // FIXME (mfh 17,24 Jan 2017) packTriples wants three arrays, not a
1480  // single array of structs. For now, we just copy.
1481  std::vector<GO> gblRowInds;
1482  std::vector<GO> gblColInds;
1483  std::vector<SC> vals;
1484 
1485  const packet_type* inBuf = imports_h.data ();
1486  int inBufCurPos = 0;
1487  size_t numInvalidRowInds = 0;
1488  int errCode = 0;
1489  std::ostringstream errStrm; // for unpack* error output.
1490  for (size_t k = 0; k < numImports; ++k) {
1491  const LO lclRow = importLIDs_h(k);
1492  const GO gblRow = this->map_->getGlobalElement (lclRow);
1493  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1494  ++numInvalidRowInds;
1495  continue;
1496  }
1497 
1498  // Remember where we were, so we don't overrun the buffer
1499  // length. inBufCurPos is an in/out argument of unpackTriples*.
1500  const int origInBufCurPos = inBufCurPos;
1501 
1502  int numEnt = 0; // output argument of unpackTriplesCount
1503  errCode = unpackTriplesCount (inBuf, inBufSize, inBufCurPos,
1504  numEnt, *comm, &errStrm);
1505  if (errCode != 0 || numEnt < 0 || inBufCurPos < origInBufCurPos) {
1506  std::ostream& err = this->markLocalErrorAndGetStream ();
1507 
1508  err << prefix << "In unpack loop, k=" << k << ": ";
1509  if (errCode != 0) {
1510  err << " unpackTriplesCount returned errCode = " << errCode
1511  << " != 0." << endl;
1512  }
1513  if (numEnt < 0) {
1514  err << " unpackTriplesCount returned errCode = 0, but numEnt = "
1515  << numEnt << " < 0." << endl;
1516  }
1517  if (inBufCurPos < origInBufCurPos) {
1518  err << " After unpackTriplesCount, inBufCurPos = " << inBufCurPos
1519  << " < origInBufCurPos = " << origInBufCurPos << "." << endl;
1520  }
1521  err << " unpackTriplesCount report: " << errStrm.str () << endl;
1522  err << suffix << endl;
1523 
1524  // We only continue in a debug build, because the above error
1525  // messages could consume too much memory and cause an
1526  // out-of-memory error, without actually printing. Printing
1527  // everything is useful in a debug build, but not so much in a
1528  // release build.
1529 #ifdef HAVE_TPETRA_DEBUG
1530  // Clear out the current error stream, so we don't accumulate
1531  // over loop iterations.
1532  errStrm.str ("");
1533  continue;
1534 #else
1535  return;
1536 #endif // HAVE_TPETRA_DEBUG
1537  }
1538 
1539  // FIXME (mfh 17,24 Jan 2017) packTriples wants three arrays,
1540  // not a single array of structs. For now, we just copy.
1541  gblRowInds.resize (numEnt);
1542  gblColInds.resize (numEnt);
1543  vals.resize (numEnt);
1544 
1545  errCode = unpackTriples (inBuf, inBufSize, inBufCurPos,
1546  gblRowInds.data (), gblColInds.data (),
1547  vals.data (), numEnt, *comm, &errStrm);
1548  if (errCode != 0) {
1549  std::ostream& err = this->markLocalErrorAndGetStream ();
1550  err << prefix << "unpackTriples returned errCode = "
1551  << errCode << " != 0. It reports: " << errStrm.str ()
1552  << endl;
1553  // We only continue in a debug build, because the above error
1554  // messages could consume too much memory and cause an
1555  // out-of-memory error, without actually printing. Printing
1556  // everything is useful in a debug build, but not so much in a
1557  // release build.
1558 #ifdef HAVE_TPETRA_DEBUG
1559  // Clear out the current error stream, so we don't accumulate
1560  // over loop iterations.
1561  errStrm.str ("");
1562  continue;
1563 #else
1564  return;
1565 #endif // HAVE_TPETRA_DEBUG
1566  }
1567  this->sumIntoGlobalValues (gblRowInds.data (), gblColInds.data (),
1568  vals.data (), numEnt);
1569  }
1570 
1571  // If we found invalid row indices in exportLIDs, go back,
1572  // collect, and report them.
1573  if (numInvalidRowInds != 0) {
1574  // Mark the error now, before we possibly run out of memory.
1575  // The latter could raise an exception (e.g., std::bad_alloc),
1576  // but at least we would get the error state right.
1577  std::ostream& err = this->markLocalErrorAndGetStream ();
1578 
1579  std::vector<std::pair<LO, GO> > invalidRowInds;
1580  for (size_t k = 0; k < numImports; ++k) {
1581  const LO lclRow = importLIDs_h(k);
1582  const GO gblRow = this->map_->getGlobalElement (lclRow);
1583  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1584  invalidRowInds.push_back ({lclRow, gblRow});
1585  }
1586  }
1587 
1588  err << prefix << "We found " << numInvalidRowInds << " invalid row ind"
1589  << (numInvalidRowInds != static_cast<size_t> (1) ? "ices" : "ex")
1590  << " out of " << numImports << " in importLIDs. Here is the list "
1591  << " of invalid row indices: [";
1592  for (size_t k = 0; k < invalidRowInds.size (); ++k) {
1593  err << "(LID: " << invalidRowInds[k].first << ", GID: "
1594  << invalidRowInds[k].second << ")";
1595  if (k + 1 < invalidRowInds.size ()) {
1596  err << ", ";
1597  }
1598  }
1599  err << "].";
1600  return;
1601  }
1602  }
1603 };
1604 
1605 } // namespace Details
1606 } // namespace Tpetra
1607 
1608 #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.
Sets up and executes a communication plan for a Tpetra DistObject.
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.