Tpetra parallel linear algebra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator 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"
53 #include "Tpetra_Details_gathervPrint.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 #ifdef TPETRA_ENABLE_DEPRECATED_CODE
1003  copyAndPermuteNew
1004 #else // TPETRA_ENABLE_DEPRECATED_CODE
1005  copyAndPermute
1006 #endif // TPETRA_ENABLE_DEPRECATED_CODE
1007  (const ::Tpetra::SrcDistObject& sourceObject,
1008  const size_t numSameIDs,
1009  const Kokkos::DualView<const LO*,
1010  buffer_device_type>& permuteToLIDs,
1011  const Kokkos::DualView<const LO*,
1012  buffer_device_type>& permuteFromLIDs)
1013  {
1014  using std::endl;
1016  const char prefix[] = "Tpetra::Details::CooMatrix::copyAndPermute: ";
1017 
1018  // There's no communication in this method, so it's safe just to
1019  // return on error.
1020 
1021  if (* (this->localError_)) {
1022  std::ostream& err = this->markLocalErrorAndGetStream ();
1023  err << prefix << "The target object of the Import or Export is "
1024  "already in an error state." << endl;
1025  return;
1026  }
1027 
1028  const this_type* src = dynamic_cast<const this_type*> (&sourceObject);
1029  if (src == nullptr) {
1030  std::ostream& err = this->markLocalErrorAndGetStream ();
1031  err << prefix << "Input argument 'sourceObject' is not a CooMatrix."
1032  << endl;
1033  return;
1034  }
1035 
1036  const size_t numPermuteIDs =
1037  static_cast<size_t> (permuteToLIDs.extent (0));
1038  if (numPermuteIDs != static_cast<size_t> (permuteFromLIDs.extent (0))) {
1039  std::ostream& err = this->markLocalErrorAndGetStream ();
1040  err << prefix << "permuteToLIDs.extent(0) = "
1041  << numPermuteIDs << " != permuteFromLIDs.extent(0) = "
1042  << permuteFromLIDs.extent (0) << "." << endl;
1043  return;
1044  }
1045  if (sizeof (int) <= sizeof (size_t) &&
1046  numPermuteIDs > static_cast<size_t> (std::numeric_limits<int>::max ())) {
1047  std::ostream& err = this->markLocalErrorAndGetStream ();
1048  err << prefix << "numPermuteIDs = " << numPermuteIDs
1049  << ", a size_t, overflows int." << endl;
1050  return;
1051  }
1052 
1053  // Even though this is an std::set, we can start with numSameIDs
1054  // just by iterating through the first entries of the set.
1055 
1056  if (sizeof (int) <= sizeof (size_t) &&
1057  numSameIDs > static_cast<size_t> (std::numeric_limits<int>::max ())) {
1058  std::ostream& err = this->markLocalErrorAndGetStream ();
1059  err << prefix << "numSameIDs = " << numSameIDs
1060  << ", a size_t, overflows int." << endl;
1061  return;
1062  }
1063 
1064  //
1065  // Copy in entries from any initial rows with the same global row indices.
1066  //
1067  const LO numSame = static_cast<int> (numSameIDs);
1068  // Count of local row indices encountered here with invalid global
1069  // row indices. If nonzero, something went wrong. If something
1070  // did go wrong, we'll defer responding until the end of this
1071  // method, so we can print as much useful info as possible.
1072  LO numInvalidSameRows = 0;
1073  for (LO lclRow = 0; lclRow < numSame; ++lclRow) {
1074  // All numSame initial rows have the same global index in both
1075  // source and target Maps, so we only need to convert to global
1076  // once.
1077  const GO gblRow = this->map_->getGlobalElement (lclRow);
1078  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1079  ++numInvalidSameRows;
1080  continue;
1081  }
1082  else {
1083  this->impl_.mergeIntoRow (gblRow, src->impl_, gblRow);
1084  }
1085  }
1086 
1087  //
1088  // Copy in entries from remaining rows that are permutations, that
1089  // is, that live in both the source and target Maps, but aren't
1090  // included in the "same" list (see above).
1091  //
1092  const LO numPermute = static_cast<int> (numPermuteIDs);
1093  // Count of local "from" row indices encountered here with invalid
1094  // global row indices. If nonzero, something went wrong. If
1095  // something did go wrong, we'll defer responding until the end of
1096  // this method, so we can print as much useful info as possible.
1097  LO numInvalidRowsFrom = 0;
1098  // Count of local "to" row indices encountered here with invalid
1099  // global row indices. If nonzero, something went wrong. If
1100  // something did go wrong, we'll defer responding until the end of
1101  // this method, so we can print as much useful info as possible.
1102  LO numInvalidRowsTo = 0;
1103 
1104  TEUCHOS_ASSERT( ! permuteFromLIDs.need_sync_host () );
1105  TEUCHOS_ASSERT( ! permuteToLIDs.need_sync_host () );
1106  auto permuteFromLIDs_h = permuteFromLIDs.view_host ();
1107  auto permuteToLIDs_h = permuteToLIDs.view_host ();
1108 
1109  for (LO k = 0; k < numPermute; ++k) {
1110  const LO lclRowFrom = permuteFromLIDs_h[k];
1111  const LO lclRowTo = permuteToLIDs_h[k];
1112  const GO gblRowFrom = src->map_->getGlobalElement (lclRowFrom);
1113  const GO gblRowTo = this->map_->getGlobalElement (lclRowTo);
1114 
1115  bool bothConversionsValid = true;
1116  if (gblRowFrom == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1117  ++numInvalidRowsFrom;
1118  bothConversionsValid = false;
1119  }
1120  if (gblRowTo == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1121  ++numInvalidRowsTo;
1122  bothConversionsValid = false;
1123  }
1124  if (bothConversionsValid) {
1125  this->impl_.mergeIntoRow (gblRowTo, src->impl_, gblRowFrom);
1126  }
1127  }
1128 
1129  // Print info if any errors occurred.
1130  if (numInvalidSameRows != 0 || numInvalidRowsFrom != 0 ||
1131  numInvalidRowsTo != 0) {
1132  // Collect and print all the invalid input row indices, for the
1133  // "same," "from," and "to" lists.
1134  std::vector<std::pair<LO, GO> > invalidSameRows;
1135  invalidSameRows.reserve (numInvalidSameRows);
1136  std::vector<std::pair<LO, GO> > invalidRowsFrom;
1137  invalidRowsFrom.reserve (numInvalidRowsFrom);
1138  std::vector<std::pair<LO, GO> > invalidRowsTo;
1139  invalidRowsTo.reserve (numInvalidRowsTo);
1140 
1141  for (LO lclRow = 0; lclRow < numSame; ++lclRow) {
1142  // All numSame initial rows have the same global index in both
1143  // source and target Maps, so we only need to convert to global
1144  // once.
1145  const GO gblRow = this->map_->getGlobalElement (lclRow);
1146  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1147  invalidSameRows.push_back ({lclRow, gblRow});
1148  }
1149  }
1150 
1151  for (LO k = 0; k < numPermute; ++k) {
1152  const LO lclRowFrom = permuteFromLIDs_h[k];
1153  const LO lclRowTo = permuteToLIDs_h[k];
1154  const GO gblRowFrom = src->map_->getGlobalElement (lclRowFrom);
1155  const GO gblRowTo = this->map_->getGlobalElement (lclRowTo);
1156 
1157  if (gblRowFrom == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1158  invalidRowsFrom.push_back ({lclRowFrom, gblRowFrom});
1159  }
1160  if (gblRowTo == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1161  invalidRowsTo.push_back ({lclRowTo, gblRowTo});
1162  }
1163  }
1164 
1165  std::ostringstream os;
1166  if (numInvalidSameRows != 0) {
1167  os << "Invalid permute \"same\" (local, global) index pairs: [";
1168  for (std::size_t k = 0; k < invalidSameRows.size (); ++k) {
1169  const auto& p = invalidSameRows[k];
1170  os << "(" << p.first << "," << p.second << ")";
1171  if (k + 1 < invalidSameRows.size ()) {
1172  os << ", ";
1173  }
1174  }
1175  os << "]" << std::endl;
1176  }
1177  if (numInvalidRowsFrom != 0) {
1178  os << "Invalid permute \"from\" (local, global) index pairs: [";
1179  for (std::size_t k = 0; k < invalidRowsFrom.size (); ++k) {
1180  const auto& p = invalidRowsFrom[k];
1181  os << "(" << p.first << "," << p.second << ")";
1182  if (k + 1 < invalidRowsFrom.size ()) {
1183  os << ", ";
1184  }
1185  }
1186  os << "]" << std::endl;
1187  }
1188  if (numInvalidRowsTo != 0) {
1189  os << "Invalid permute \"to\" (local, global) index pairs: [";
1190  for (std::size_t k = 0; k < invalidRowsTo.size (); ++k) {
1191  const auto& p = invalidRowsTo[k];
1192  os << "(" << p.first << "," << p.second << ")";
1193  if (k + 1 < invalidRowsTo.size ()) {
1194  os << ", ";
1195  }
1196  }
1197  os << "]" << std::endl;
1198  }
1199 
1200  std::ostream& err = this->markLocalErrorAndGetStream ();
1201  err << prefix << os.str ();
1202  return;
1203  }
1204  }
1205 
1206  virtual void
1207 #ifdef TPETRA_ENABLE_DEPRECATED_CODE
1208  packAndPrepareNew
1209 #else // TPETRA_ENABLE_DEPRECATED_CODE
1210  packAndPrepare
1211 #endif // TPETRA_ENABLE_DEPRECATED_CODE
1212  (const ::Tpetra::SrcDistObject& sourceObject,
1213  const Kokkos::DualView<const local_ordinal_type*,
1214  buffer_device_type>& exportLIDs,
1215  Kokkos::DualView<packet_type*,
1216  buffer_device_type>& exports,
1217  Kokkos::DualView<size_t*,
1218  buffer_device_type> numPacketsPerLID,
1219  size_t& constantNumPackets,
1220  ::Tpetra::Distributor& /* distor */)
1221  {
1222  using Teuchos::Comm;
1223  using Teuchos::RCP;
1224  using std::endl;
1225  using this_type = CooMatrix<SC, LO, GO, NT>;
1226  const char prefix[] = "Tpetra::Details::CooMatrix::packAndPrepare: ";
1227  const char suffix[] = " This should never happen. "
1228  "Please report this bug to the Tpetra developers.";
1229 
1230  // Tell the caller that different rows may have different numbers
1231  // of matrix entries.
1232  constantNumPackets = 0;
1233 
1234  const this_type* src = dynamic_cast<const this_type*> (&sourceObject);
1235  if (src == nullptr) {
1236  std::ostream& err = this->markLocalErrorAndGetStream ();
1237  err << prefix << "Input argument 'sourceObject' is not a CooMatrix."
1238  << endl;
1239  }
1240  else if (* (src->localError_)) {
1241  std::ostream& err = this->markLocalErrorAndGetStream ();
1242  err << prefix << "The source (input) object of the Import or Export "
1243  "is already in an error state on this process."
1244  << endl;
1245  }
1246  else if (* (this->localError_)) {
1247  std::ostream& err = this->markLocalErrorAndGetStream ();
1248  err << prefix << "The target (output, \"this\") object of the Import "
1249  "or Export is already in an error state on this process." << endl;
1250  }
1251  // Respond to detected error(s) by resizing 'exports' to zero (so
1252  // we won't be tempted to read it later), and filling
1253  // numPacketsPerLID with zeros.
1254  if (* (this->localError_)) {
1255  // Resize 'exports' to zero, so we won't be tempted to read it.
1256  Details::reallocDualViewIfNeeded (exports, 0, "CooMatrix exports");
1257  // Trick to get around const DualView& being const.
1258  {
1259  auto numPacketsPerLID_tmp = numPacketsPerLID;
1260  numPacketsPerLID_tmp.sync_host ();
1261  numPacketsPerLID_tmp.modify_host ();
1262  }
1263  // Fill numPacketsPerLID with zeros.
1264  Kokkos::deep_copy (numPacketsPerLID.h_view, static_cast<size_t> (0));
1265  return;
1266  }
1267 
1268  const size_t numExports = exportLIDs.extent (0);
1269  if (numExports == 0) {
1270  Details::reallocDualViewIfNeeded (exports, 0, exports.h_view.label ());
1271  return; // nothing to send
1272  }
1273  RCP<const Comm<int> > comm = src->getMap ().is_null () ?
1274  Teuchos::null : src->getMap ()->getComm ();
1275  if (comm.is_null () || comm->getSize () == 1) {
1276  if (numExports != static_cast<size_t> (0)) {
1277  std::ostream& err = this->markLocalErrorAndGetStream ();
1278  err << prefix << "The input communicator is either null or "
1279  "has only one process, but numExports = " << numExports << " != 0."
1280  << suffix << endl;
1281  return;
1282  }
1283  // Don't go into the rest of this method unless there are
1284  // actually processes other than the calling process. This is
1285  // because the pack and unpack functions only have nonstub
1286  // implementations if building with MPI.
1287  return;
1288  }
1289 
1290  numPacketsPerLID.sync_host ();
1291  numPacketsPerLID.modify_host ();
1292 
1293  TEUCHOS_ASSERT( ! exportLIDs.need_sync_host () );
1294  auto exportLIDs_h = exportLIDs.view_host ();
1295 
1296  int totalNumPackets = 0;
1297  size_t numInvalidRowInds = 0;
1298  std::ostringstream errStrm; // current loop iteration's error messages
1299  for (size_t k = 0; k < numExports; ++k) {
1300  const LO lclRow = exportLIDs_h[k];
1301  // We're packing the source object's data, so we need to use the
1302  // source object's Map to convert from local to global indices.
1303  const GO gblRow = src->map_->getGlobalElement (lclRow);
1304  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1305  // Mark the error later; just count for now.
1306  ++numInvalidRowInds;
1307  numPacketsPerLID.h_view[k] = 0;
1308  continue;
1309  }
1310 
1311  // Count the number of bytes needed to pack the current row of
1312  // the source object.
1313  int numPackets = 0;
1314  const int errCode =
1315  src->impl_.countPackRow (numPackets, gblRow, *comm, &errStrm);
1316  if (errCode != 0) {
1317  std::ostream& err = this->markLocalErrorAndGetStream ();
1318  err << prefix << errStrm.str () << endl;
1319  numPacketsPerLID.h_view[k] = 0;
1320  continue;
1321  }
1322 
1323  // Make sure that the total number of packets fits in int.
1324  // MPI requires this.
1325  const long long newTotalNumPackets =
1326  static_cast<long long> (totalNumPackets) +
1327  static_cast<long long> (numPackets);
1328  if (newTotalNumPackets >
1329  static_cast<long long> (std::numeric_limits<int>::max ())) {
1330  std::ostream& err = this->markLocalErrorAndGetStream ();
1331  err << prefix << "The new total number of packets "
1332  << newTotalNumPackets << " does not fit in int." << endl;
1333  // At this point, we definitely cannot continue. In order to
1334  // leave the output arguments in a rational state, we zero out
1335  // all remaining entries of numPacketsPerLID before returning.
1336  for (size_t k2 = k; k2 < numExports; ++k2) {
1337  numPacketsPerLID.h_view[k2] = 0;
1338  }
1339  return;
1340  }
1341  numPacketsPerLID.h_view[k] = static_cast<size_t> (numPackets);
1342  totalNumPackets = static_cast<int> (newTotalNumPackets);
1343  }
1344 
1345  // If we found invalid row indices in exportLIDs, go back,
1346  // collect, and report them.
1347  if (numInvalidRowInds != 0) {
1348  std::vector<std::pair<LO, GO> > invalidRowInds;
1349  for (size_t k = 0; k < numExports; ++k) {
1350  const LO lclRow = exportLIDs_h[k];
1351  // We're packing the source object's data, so we need to use
1352  // the source object's Map to convert from local to global
1353  // indices.
1354  const GO gblRow = src->map_->getGlobalElement (lclRow);
1355  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1356  invalidRowInds.push_back ({lclRow, gblRow});
1357  }
1358  }
1359  std::ostringstream os;
1360  os << prefix << "We found " << numInvalidRowInds << " invalid row ind"
1361  << (numInvalidRowInds != static_cast<size_t> (1) ? "ices" : "ex")
1362  << " out of " << numExports << " in exportLIDs. Here is the list "
1363  << " of invalid row indices: [";
1364  for (size_t k = 0; k < invalidRowInds.size (); ++k) {
1365  os << "(LID: " << invalidRowInds[k].first << ", GID: "
1366  << invalidRowInds[k].second << ")";
1367  if (k + 1 < invalidRowInds.size ()) {
1368  os << ", ";
1369  }
1370  }
1371  os << "].";
1372 
1373  std::ostream& err = this->markLocalErrorAndGetStream ();
1374  err << prefix << os.str () << std::endl;
1375  return;
1376  }
1377 
1378  {
1379  const bool reallocated =
1380  Details::reallocDualViewIfNeeded (exports, totalNumPackets,
1381  "CooMatrix exports");
1382  if (reallocated) {
1383  exports.sync_host (); // make sure alloc'd on host
1384  }
1385  }
1386  exports.modify_host ();
1387 
1388  // FIXME (mfh 17 Jan 2017) packTriples wants three arrays, not a
1389  // single array of structs. For now, we just copy.
1390  std::vector<GO> gblRowInds;
1391  std::vector<GO> gblColInds;
1392  std::vector<SC> vals;
1393 
1394  int outBufCurPos = 0;
1395  packet_type* outBuf = exports.h_view.data ();
1396  for (size_t k = 0; k < numExports; ++k) {
1397  const LO lclRow = exportLIDs.h_view[k];
1398  // We're packing the source object's data, so we need to use the
1399  // source object's Map to convert from local to global indices.
1400  const GO gblRow = src->map_->getGlobalElement (lclRow);
1401  // Pack the current row of the source object.
1402  src->impl_.packRow (outBuf, totalNumPackets, outBufCurPos, *comm,
1403  gblRowInds, gblColInds, vals, gblRow);
1404  }
1405  }
1406 
1407  virtual void
1408 #ifdef TPETRA_ENABLE_DEPRECATED_CODE
1409  unpackAndCombineNew
1410 #else // TPETRA_ENABLE_DEPRECATED_CODE
1411  unpackAndCombine
1412 #endif // TPETRA_ENABLE_DEPRECATED_CODE
1413  (const Kokkos::DualView<const local_ordinal_type*,
1414  buffer_device_type>& importLIDs,
1415  Kokkos::DualView<packet_type*,
1416  buffer_device_type> imports,
1417  Kokkos::DualView<size_t*,
1418  buffer_device_type> numPacketsPerLID,
1419  const size_t /* constantNumPackets */,
1420  ::Tpetra::Distributor& /* distor */,
1421  const ::Tpetra::CombineMode /* combineMode */)
1422  {
1423  using Teuchos::Comm;
1424  using Teuchos::RCP;
1425  using std::endl;
1426  const char prefix[] = "Tpetra::Details::CooMatrix::unpackAndCombine: ";
1427  const char suffix[] = " This should never happen. "
1428  "Please report this bug to the Tpetra developers.";
1429 
1430  TEUCHOS_ASSERT( ! importLIDs.need_sync_host () );
1431  auto importLIDs_h = importLIDs.view_host ();
1432 
1433  const std::size_t numImports = importLIDs.extent (0);
1434  if (numImports == 0) {
1435  return; // nothing to receive
1436  }
1437  else if (imports.extent (0) == 0) {
1438  std::ostream& err = this->markLocalErrorAndGetStream ();
1439  err << prefix << "importLIDs.extent(0) = " << numImports << " != 0, "
1440  << "but imports.extent(0) = 0. This doesn't make sense, because "
1441  << "for every incoming LID, CooMatrix packs at least the count of "
1442  << "triples associated with that LID, even if the count is zero. "
1443  << "importLIDs = [";
1444  for (std::size_t k = 0; k < numImports; ++k) {
1445  err << importLIDs_h[k];
1446  if (k + 1 < numImports) {
1447  err << ", ";
1448  }
1449  }
1450  err << "]. " << suffix << endl;
1451  return;
1452  }
1453 
1454  RCP<const Comm<int> > comm = this->getMap ().is_null () ?
1455  Teuchos::null : this->getMap ()->getComm ();
1456  if (comm.is_null () || comm->getSize () == 1) {
1457  if (numImports != static_cast<size_t> (0)) {
1458  std::ostream& err = this->markLocalErrorAndGetStream ();
1459  err << prefix << "The input communicator is either null or "
1460  "has only one process, but numImports = " << numImports << " != 0."
1461  << suffix << endl;
1462  return;
1463  }
1464  // Don't go into the rest of this method unless there are
1465  // actually processes other than the calling process. This is
1466  // because the pack and unpack functions only have nonstub
1467  // implementations if building with MPI.
1468  return;
1469  }
1470 
1471  // Make sure that the length of 'imports' fits in int.
1472  // This is ultimately an MPI restriction.
1473  if (static_cast<size_t> (imports.extent (0)) >
1474  static_cast<size_t> (std::numeric_limits<int>::max ())) {
1475  std::ostream& err = this->markLocalErrorAndGetStream ();
1476  err << prefix << "imports.extent(0) = "
1477  << imports.extent (0) << " does not fit in int." << endl;
1478  return;
1479  }
1480  const int inBufSize = static_cast<int> (imports.extent (0));
1481 
1482  if (imports.need_sync_host ()) {
1483  imports.sync_host ();
1484  }
1485  if (numPacketsPerLID.need_sync_host ()) {
1486  numPacketsPerLID.sync_host ();
1487  }
1488  auto imports_h = imports.view_host ();
1489  auto numPacketsPerLID_h = numPacketsPerLID.view_host ();
1490 
1491  // FIXME (mfh 17,24 Jan 2017) packTriples wants three arrays, not a
1492  // single array of structs. For now, we just copy.
1493  std::vector<GO> gblRowInds;
1494  std::vector<GO> gblColInds;
1495  std::vector<SC> vals;
1496 
1497  const packet_type* inBuf = imports_h.data ();
1498  int inBufCurPos = 0;
1499  size_t numInvalidRowInds = 0;
1500  int errCode = 0;
1501  std::ostringstream errStrm; // for unpack* error output.
1502  for (size_t k = 0; k < numImports; ++k) {
1503  const LO lclRow = importLIDs_h(k);
1504  const GO gblRow = this->map_->getGlobalElement (lclRow);
1505  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1506  ++numInvalidRowInds;
1507  continue;
1508  }
1509 
1510  // Remember where we were, so we don't overrun the buffer
1511  // length. inBufCurPos is an in/out argument of unpackTriples*.
1512  const int origInBufCurPos = inBufCurPos;
1513 
1514  int numEnt = 0; // output argument of unpackTriplesCount
1515  errCode = unpackTriplesCount (inBuf, inBufSize, inBufCurPos,
1516  numEnt, *comm, &errStrm);
1517  if (errCode != 0 || numEnt < 0 || inBufCurPos < origInBufCurPos) {
1518  std::ostream& err = this->markLocalErrorAndGetStream ();
1519 
1520  err << prefix << "In unpack loop, k=" << k << ": ";
1521  if (errCode != 0) {
1522  err << " unpackTriplesCount returned errCode = " << errCode
1523  << " != 0." << endl;
1524  }
1525  if (numEnt < 0) {
1526  err << " unpackTriplesCount returned errCode = 0, but numEnt = "
1527  << numEnt << " < 0." << endl;
1528  }
1529  if (inBufCurPos < origInBufCurPos) {
1530  err << " After unpackTriplesCount, inBufCurPos = " << inBufCurPos
1531  << " < origInBufCurPos = " << origInBufCurPos << "." << endl;
1532  }
1533  err << " unpackTriplesCount report: " << errStrm.str () << endl;
1534  err << suffix << endl;
1535 
1536  // We only continue in a debug build, because the above error
1537  // messages could consume too much memory and cause an
1538  // out-of-memory error, without actually printing. Printing
1539  // everything is useful in a debug build, but not so much in a
1540  // release build.
1541 #ifdef HAVE_TPETRA_DEBUG
1542  // Clear out the current error stream, so we don't accumulate
1543  // over loop iterations.
1544  errStrm.str ("");
1545  continue;
1546 #else
1547  return;
1548 #endif // HAVE_TPETRA_DEBUG
1549  }
1550 
1551  // FIXME (mfh 17,24 Jan 2017) packTriples wants three arrays,
1552  // not a single array of structs. For now, we just copy.
1553  gblRowInds.resize (numEnt);
1554  gblColInds.resize (numEnt);
1555  vals.resize (numEnt);
1556 
1557  errCode = unpackTriples (inBuf, inBufSize, inBufCurPos,
1558  gblRowInds.data (), gblColInds.data (),
1559  vals.data (), numEnt, *comm, &errStrm);
1560  if (errCode != 0) {
1561  std::ostream& err = this->markLocalErrorAndGetStream ();
1562  err << prefix << "unpackTriples returned errCode = "
1563  << errCode << " != 0. It reports: " << errStrm.str ()
1564  << endl;
1565  // We only continue in a debug build, because the above error
1566  // messages could consume too much memory and cause an
1567  // out-of-memory error, without actually printing. Printing
1568  // everything is useful in a debug build, but not so much in a
1569  // release build.
1570 #ifdef HAVE_TPETRA_DEBUG
1571  // Clear out the current error stream, so we don't accumulate
1572  // over loop iterations.
1573  errStrm.str ("");
1574  continue;
1575 #else
1576  return;
1577 #endif // HAVE_TPETRA_DEBUG
1578  }
1579  this->sumIntoGlobalValues (gblRowInds.data (), gblColInds.data (),
1580  vals.data (), numEnt);
1581  }
1582 
1583  // If we found invalid row indices in exportLIDs, go back,
1584  // collect, and report them.
1585  if (numInvalidRowInds != 0) {
1586  // Mark the error now, before we possibly run out of memory.
1587  // The latter could raise an exception (e.g., std::bad_alloc),
1588  // but at least we would get the error state right.
1589  std::ostream& err = this->markLocalErrorAndGetStream ();
1590 
1591  std::vector<std::pair<LO, GO> > invalidRowInds;
1592  for (size_t k = 0; k < numImports; ++k) {
1593  const LO lclRow = importLIDs_h(k);
1594  const GO gblRow = this->map_->getGlobalElement (lclRow);
1595  if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1596  invalidRowInds.push_back ({lclRow, gblRow});
1597  }
1598  }
1599 
1600  err << prefix << "We found " << numInvalidRowInds << " invalid row ind"
1601  << (numInvalidRowInds != static_cast<size_t> (1) ? "ices" : "ex")
1602  << " out of " << numImports << " in importLIDs. Here is the list "
1603  << " of invalid row indices: [";
1604  for (size_t k = 0; k < invalidRowInds.size (); ++k) {
1605  err << "(LID: " << invalidRowInds[k].first << ", GID: "
1606  << invalidRowInds[k].second << ")";
1607  if (k + 1 < invalidRowInds.size ()) {
1608  err << ", ";
1609  }
1610  }
1611  err << "].";
1612  return;
1613  }
1614  }
1615 };
1616 
1617 } // namespace Details
1618 } // namespace Tpetra
1619 
1620 #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.
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.