Tpetra parallel linear algebra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Tpetra_Details_makeColMap_def.hpp
Go to the documentation of this file.
1 #ifndef TPETRA_DETAILS_MAKECOLMAP_DEF_HPP
2 #define TPETRA_DETAILS_MAKECOLMAP_DEF_HPP
3 
4 // @HEADER
5 // ***********************************************************************
6 //
7 // Tpetra: Templated Linear Algebra Services Package
8 // Copyright (2008) Sandia Corporation
9 //
10 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
11 // the U.S. Government retains certain rights in this software.
12 //
13 // Redistribution and use in source and binary forms, with or without
14 // modification, are permitted provided that the following conditions are
15 // met:
16 //
17 // 1. Redistributions of source code must retain the above copyright
18 // notice, this list of conditions and the following disclaimer.
19 //
20 // 2. Redistributions in binary form must reproduce the above copyright
21 // notice, this list of conditions and the following disclaimer in the
22 // documentation and/or other materials provided with the distribution.
23 //
24 // 3. Neither the name of the Corporation nor the names of the
25 // contributors may be used to endorse or promote products derived from
26 // this software without specific prior written permission.
27 //
28 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
29 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
32 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
36 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 //
40 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
41 //
42 // ************************************************************************
43 // @HEADER
44 
55 
56 #include "Tpetra_RowGraph.hpp"
57 #include "Tpetra_Util.hpp"
58 #include "Teuchos_Array.hpp"
59 #include "Kokkos_Bitset.hpp"
60 #include <set>
61 #include <vector>
62 
63 namespace Tpetra {
64 namespace Details {
65 
66 template <class LO, class GO, class NT>
67 int
68 makeColMapImpl(Teuchos::RCP<const Tpetra::Map<LO, GO, NT>>& colMap,
69  Teuchos::Array<int>& remotePIDs,
70  const Teuchos::RCP<const Tpetra::Map<LO, GO, NT>>& domMap,
71  size_t numLocalColGIDs,
72  size_t numRemoteColGIDs,
73  std::set<GO>& RemoteGIDSet,
74  std::vector<GO>& RemoteGIDUnorderedVector,
75  std::vector<bool>& GIDisLocal,
76  const bool sortEachProcsGids,
77  std::ostream* errStrm)
78 {
79  using Teuchos::Array;
80  using Teuchos::ArrayView;
81  using Teuchos::rcp;
82  using std::endl;
83  int errCode = 0;
84  const char prefix[] = "Tpetra::Details::makeColMapImpl: ";
85  typedef ::Tpetra::Map<LO, GO, NT> map_type;
86  // Possible short-circuit for serial scenario:
87  //
88  // If all domain GIDs are present as column indices, then set
89  // ColMap=DomainMap. By construction, LocalGIDs is a subset of
90  // DomainGIDs.
91  //
92  // If we have
93  // * Number of remote GIDs is 0, so that ColGIDs == LocalGIDs,
94  // and
95  // * Number of local GIDs is number of domain GIDs
96  // then
97  // * LocalGIDs \subset DomainGIDs && size(LocalGIDs) ==
98  // size(DomainGIDs) => DomainGIDs == LocalGIDs == ColGIDs
99  // on the calling process.
100  //
101  // We will concern ourselves only with the special case of a
102  // serial DomainMap, obviating the need for communication.
103  //
104  // If
105  // * DomainMap has a serial communicator
106  // then we can set the column Map as the domain Map
107  // return. Benefit: this graph won't need an Import object
108  // later.
109  //
110  // Note, for a serial domain map, there can be no RemoteGIDs,
111  // because there are no remote processes. Likely explanations
112  // for this are:
113  // * user submitted erroneous column indices
114  // * user submitted erroneous domain Map
115  if (domMap->getComm ()->getSize () == 1) {
116  if (numRemoteColGIDs != 0) {
117  errCode = -2;
118  if (errStrm != NULL) {
119  *errStrm << prefix << "The domain Map only has one process, but "
120  << numRemoteColGIDs << " column "
121  << (numRemoteColGIDs != 1 ? "indices are" : "index is")
122  << " not in the domain Map. Either these indices are "
123  "invalid or the domain Map is invalid. Remember that nonsquare "
124  "matrices, or matrices where the row and range Maps differ, "
125  "require calling the version of fillComplete that takes the "
126  "domain and range Maps as input." << endl;
127  }
128  }
129  if (numLocalColGIDs == domMap->getNodeNumElements ()) {
130  colMap = domMap; // shallow copy
131  return errCode;
132  }
133  }
134 
135  // Populate myColumns with a list of all column GIDs. Put
136  // locally owned (in the domain Map) GIDs at the front: they
137  // correspond to "same" and "permuted" entries between the
138  // column Map and the domain Map. Put remote GIDs at the back.
139  Array<GO> myColumns(numLocalColGIDs + numRemoteColGIDs);
140  // get pointers into myColumns for each part
141  ArrayView<GO> LocalColGIDs = myColumns (0, numLocalColGIDs);
142  ArrayView<GO> remoteColGIDs = myColumns (numLocalColGIDs, numRemoteColGIDs);
143 
144  // Copy the remote GIDs into myColumns
145  if (sortEachProcsGids) {
146  // The std::set puts GIDs in increasing order.
147  std::copy (RemoteGIDSet.begin(), RemoteGIDSet.end(),
148  remoteColGIDs.begin());
149  }
150  else {
151  // Respect the originally encountered order.
152  std::copy (RemoteGIDUnorderedVector.begin(),
153  RemoteGIDUnorderedVector.end(), remoteColGIDs.begin());
154  }
155 
156  // Make a list of process ranks corresponding to the remote GIDs.
157  // remotePIDs is an output argument of getRemoteIndexList below;
158  // its initial contents don't matter.
159  if (static_cast<size_t> (remotePIDs.size ()) != numRemoteColGIDs) {
160  remotePIDs.resize (numRemoteColGIDs);
161  }
162  // Look up the remote process' ranks in the domain Map.
163  {
164  const LookupStatus stat =
165  domMap->getRemoteIndexList (remoteColGIDs, remotePIDs ());
166 
167  // If any process returns IDNotPresent, then at least one of
168  // the remote indices was not present in the domain Map. This
169  // means that the Import object cannot be constructed, because
170  // of incongruity between the column Map and domain Map.
171  // This has two likely causes:
172  // - The user has made a mistake in the column indices
173  // - The user has made a mistake with respect to the domain Map
174  if (stat == IDNotPresent) {
175  if (errStrm != NULL) {
176  *errStrm << prefix << "Some column indices are not in the domain Map."
177  "Either these column indices are invalid or the domain Map is "
178  "invalid. Likely cause: For a nonsquare matrix, you must give the "
179  "domain and range Maps as input to fillComplete." << endl;
180  }
181  // Don't return yet, because not all processes may have
182  // encountered this error state. This function ends with an
183  // all-reduce, so we have to make sure that everybody gets to
184  // that point. The resulting Map may be wrong, but at least
185  // nothing should crash.
186  errCode = -3;
187  }
188  }
189 
190  // Sort incoming remote column indices by their owning process
191  // rank, so that all columns coming from a given remote process
192  // are contiguous. This means the Import's Distributor doesn't
193  // need to reorder data.
194  //
195  // NOTE (mfh 02 Sep 2014) This needs to be a stable sort, so that
196  // it respects either of the possible orderings of GIDs (sorted,
197  // or original order) specified above.
198  sort2 (remotePIDs.begin (), remotePIDs.end (), remoteColGIDs.begin ());
199 
200  // Copy the local GIDs into myColumns. Two cases:
201  // 1. If the number of Local column GIDs is the same as the number
202  // of Local domain GIDs, we can simply read the domain GIDs
203  // into the front part of ColIndices (see logic above from the
204  // serial short circuit case)
205  // 2. We step through the GIDs of the DomainMap, checking to see
206  // if each domain GID is a column GID. We want to do this to
207  // maintain a consistent ordering of GIDs between the columns
208  // and the domain.
209 
210  const size_t numDomainElts = domMap->getNodeNumElements ();
211  if (numLocalColGIDs == numDomainElts) {
212  // If the number of locally owned GIDs are the same as the
213  // number of local domain Map elements, then the local domain
214  // Map elements are the same as the locally owned GIDs.
215  if (domMap->isContiguous ()) {
216  // NOTE (mfh 03 Mar 2013, 02 Sep 2014) In the common case that
217  // the domain Map is contiguous, it's more efficient to avoid
218  // calling getNodeElementList(), since that permanently
219  // constructs and caches the GID list in the contiguous Map.
220  GO curColMapGid = domMap->getMinGlobalIndex ();
221  for (size_t k = 0; k < numLocalColGIDs; ++k, ++curColMapGid) {
222  LocalColGIDs[k] = curColMapGid;
223  }
224  }
225  else {
226  ArrayView<const GO> domainElts = domMap->getNodeElementList ();
227  std::copy (domainElts.begin(), domainElts.end(), LocalColGIDs.begin());
228  }
229  }
230  else {
231  // Count the number of locally owned GIDs, both to keep track of
232  // the current array index, and as a sanity check.
233  size_t numLocalCount = 0;
234  if (domMap->isContiguous ()) {
235  // NOTE (mfh 03 Mar 2013, 02 Sep 2014) In the common case that
236  // the domain Map is contiguous, it's more efficient to avoid
237  // calling getNodeElementList(), since that permanently
238  // constructs and caches the GID list in the contiguous Map.
239  GO curColMapGid = domMap->getMinGlobalIndex ();
240  for (size_t i = 0; i < numDomainElts; ++i, ++curColMapGid) {
241  if (GIDisLocal[i]) {
242  LocalColGIDs[numLocalCount++] = curColMapGid;
243  }
244  }
245  }
246  else {
247  ArrayView<const GO> domainElts = domMap->getNodeElementList ();
248  for (size_t i = 0; i < numDomainElts; ++i) {
249  if (GIDisLocal[i]) {
250  LocalColGIDs[numLocalCount++] = domainElts[i];
251  }
252  }
253  }
254  if (numLocalCount != numLocalColGIDs) {
255  if (errStrm != NULL) {
256  *errStrm << prefix << "numLocalCount = " << numLocalCount
257  << " != numLocalColGIDs = " << numLocalColGIDs
258  << ". This should never happen. "
259  "Please report this bug to the Tpetra developers." << endl;
260  }
261  // Don't return yet, because not all processes may have
262  // encountered this error state. This function ends with an
263  // all-reduce, so we have to make sure that everybody gets to
264  // that point.
265  errCode = -4;
266  }
267  }
268 
269  // FIXME (mfh 03 Apr 2013) Now would be a good time to use the
270  // information we collected above to construct the Import. In
271  // particular, building an Import requires:
272  //
273  // 1. numSameIDs (length of initial contiguous sequence of GIDs
274  // on this process that are the same in both Maps; this
275  // equals the number of domain Map elements on this process)
276  //
277  // 2. permuteToLIDs and permuteFromLIDs (both empty in this
278  // case, since there's no permutation going on; the column
279  // Map starts with the domain Map's GIDs, and immediately
280  // after them come the remote GIDs)
281  //
282  // 3. remoteGIDs (exactly those GIDs that we found out above
283  // were not in the domain Map) and remoteLIDs (which we could
284  // have gotten above by using the three-argument version of
285  // getRemoteIndexList() that computes local indices as well
286  // as process ranks, instead of the two-argument version that
287  // was used above)
288  //
289  // 4. remotePIDs (which we have from the getRemoteIndexList() call
290  // above) -- NOTE (mfh 14 Sep 2017) Fix for Trilinos GitHub
291  // Issue #628 (https://github.com/trilinos/Trilinos/issues/628)
292  // addresses this. remotePIDs is now an output argument of
293  // both this function and CrsGraph::makeColMap, and
294  // CrsGraph::makeImportExport now has an option to use that
295  // information, if available (that is, if makeColMap was
296  // actually called, which it would not be if the graph already
297  // had a column Map).
298  //
299  // 5. Apply the permutation from sorting remotePIDs to both
300  // remoteGIDs and remoteLIDs (by calling sort3 above instead of
301  // sort2), instead of just to remoteLIDs alone.
302  //
303  // 6. Everything after the sort3 call in Import::setupExport():
304  // a. Create the Distributor via createFromRecvs(), which
305  // computes exportGIDs and exportPIDs
306  // b. Compute exportLIDs from exportGIDs (by asking the
307  // source Map, in this case the domain Map, to convert
308  // global to local)
309  //
310  // Steps 1-5 come for free, since we must do that work anyway in
311  // order to compute the column Map. In particular, Step 3 is
312  // even more expensive than Step 6a, since it involves both
313  // creating and using a new Distributor object.
314  const global_size_t INV =
315  Tpetra::Details::OrdinalTraits<global_size_t>::invalid ();
316  // FIXME (mfh 05 Mar 2014) Doesn't the index base of a Map have to
317  // be the same as the Map's min GID? If the first column is empty
318  // (contains no entries), then the column Map's min GID won't
319  // necessarily be the same as the domain Map's index base.
320  const GO indexBase = domMap->getIndexBase ();
321  colMap = rcp (new map_type (INV, myColumns, indexBase, domMap->getComm ()));
322  return errCode;
323 }
324 
325 template <class LO, class GO, class NT>
326 int
327 makeColMap (Teuchos::RCP<const Tpetra::Map<LO, GO, NT> >& colMap,
328  Teuchos::Array<int>& remotePIDs,
329  const Teuchos::RCP<const Tpetra::Map<LO, GO, NT> >& domMap,
330  const RowGraph<LO, GO, NT>& graph,
331  const bool sortEachProcsGids,
332  std::ostream* errStrm)
333 {
334  using Teuchos::Array;
335  using Teuchos::ArrayView;
336  using Teuchos::rcp;
337  using std::endl;
338  typedef ::Tpetra::Map<LO, GO, NT> map_type;
339  const char prefix[] = "Tpetra::Details::makeColMap: ";
340  int errCode = 0;
341 
342  // If the input domain Map or its communicator is null on the
343  // calling process, then the calling process does not participate in
344  // the returned column Map. Thus, we can set the returned column
345  // Map to null on those processes, and return immediately. This is
346  // not an error condition, as long as when domMap and its
347  // communicator are NOT null, the graph's other Maps and
348  // communicator are not also null.
349  if (domMap.is_null () || domMap->getComm ().is_null ()) {
350  colMap = Teuchos::null;
351  return errCode;
352  }
353 
354  Array<GO> myColumns;
355  if (graph.isLocallyIndexed ()) {
356  colMap = graph.getColMap ();
357  // If the graph is locally indexed, it had better have a column Map.
358  // The extra check for ! graph.hasColMap() is conservative.
359  if (colMap.is_null () || ! graph.hasColMap ()) {
360  errCode = -1;
361  if (errStrm != NULL) {
362  *errStrm << prefix << "The graph is locally indexed on the calling "
363  "process, but has no column Map (either getColMap() returns null, "
364  "or hasColMap() returns false)." << endl;
365  }
366  // Under this error condition, this process will not fill
367  // myColumns. The resulting new column Map will be incorrect,
368  // but at least we won't hang, and this process will report the
369  // error.
370  }
371  else {
372  // The graph already has a column Map, and is locally indexed on
373  // the calling process. However, it may be globally indexed (or
374  // neither locally nor globally indexed) on other processes.
375  // Assume that we want to recreate the column Map.
376  if (colMap->isContiguous ()) {
377  // The number of indices on each process must fit in LO.
378  const LO numCurGids = static_cast<LO> (colMap->getNodeNumElements ());
379  myColumns.resize (numCurGids);
380  const GO myFirstGblInd = colMap->getMinGlobalIndex ();
381  for (LO k = 0; k < numCurGids; ++k) {
382  myColumns[k] = myFirstGblInd + static_cast<GO> (k);
383  }
384  }
385  else { // the column Map is NOT contiguous
386  ArrayView<const GO> curGids = graph.getColMap ()->getNodeElementList ();
387  // The number of indices on each process must fit in LO.
388  const LO numCurGids = static_cast<LO> (curGids.size ());
389  myColumns.resize (numCurGids);
390  for (LO k = 0; k < numCurGids; ++k) {
391  myColumns[k] = curGids[k];
392  }
393  } // whether the graph's current column Map is contiguous
394  } // does the graph currently have a column Map?
395  }
396  else if (graph.isGloballyIndexed ()) {
397  // Go through all the rows, finding the populated column indices.
398  //
399  // Our final list of indices for the column Map constructor will
400  // have the following properties (all of which are with respect to
401  // the calling process):
402  //
403  // 1. Indices in the domain Map go first.
404  // 2. Indices not in the domain Map follow, ordered first
405  // contiguously by their owning process rank (in the domain
406  // Map), then in increasing order within that.
407  // 3. No duplicate indices.
408  //
409  // This imitates the ordering used by Aztec(OO) and Epetra.
410  // Storing indices owned by the same process (in the domain Map)
411  // contiguously permits the use of contiguous send and receive
412  // buffers.
413  //
414  // We begin by partitioning the column indices into "local" GIDs
415  // (owned by the domain Map) and "remote" GIDs (not owned by the
416  // domain Map). We use the same order for local GIDs as the
417  // domain Map, so we track them in place in their array. We use
418  // an std::set (RemoteGIDSet) to keep track of remote GIDs, so
419  // that we don't have to merge duplicates later.
420  const LO LINV = Tpetra::Details::OrdinalTraits<LO>::invalid ();
421  size_t numLocalColGIDs = 0;
422  size_t numRemoteColGIDs = 0;
423 
424  // GIDisLocal[lid] is false if and only if local index lid in the
425  // domain Map is remote (not local).
426  std::vector<bool> GIDisLocal (domMap->getNodeNumElements (), false);
427  std::set<GO> RemoteGIDSet;
428  // This preserves the not-sorted Epetra order of GIDs.
429  // We only use this if sortEachProcsGids is false.
430  std::vector<GO> RemoteGIDUnorderedVector;
431 
432  if (! graph.getRowMap ().is_null ()) {
433  const Tpetra::Map<LO, GO, NT>& rowMap = * (graph.getRowMap ());
434  const LO lclNumRows = rowMap.getNodeNumElements ();
435  for (LO lclRow = 0; lclRow < lclNumRows; ++lclRow) {
436  const GO gblRow = rowMap.getGlobalElement (lclRow);
437  Teuchos::ArrayView<const GO> rowGids;
438  graph.getGlobalRowView (gblRow, rowGids);
439 
440  const LO numEnt = static_cast<LO> (rowGids.size ());
441  if (numEnt != 0) {
442  for (LO k = 0; k < numEnt; ++k) {
443  const GO gid = rowGids[k];
444  const LO lid = domMap->getLocalElement (gid);
445  if (lid != LINV) {
446  const bool alreadyFound = GIDisLocal[lid];
447  if (! alreadyFound) {
448  GIDisLocal[lid] = true;
449  ++numLocalColGIDs;
450  }
451  }
452  else {
453  const bool notAlreadyFound = RemoteGIDSet.insert (gid).second;
454  if (notAlreadyFound) { // gid did not exist in the set before
455  if (! sortEachProcsGids) {
456  // The user doesn't want to sort remote GIDs (for each
457  // remote process); they want us to keep remote GIDs
458  // in their original order. We do this by stuffing
459  // each remote GID into an array as we encounter it
460  // for the first time. The std::set helpfully tracks
461  // first encounters.
462  RemoteGIDUnorderedVector.push_back (gid);
463  }
464  ++numRemoteColGIDs;
465  }
466  }
467  } // for each entry k in row r
468  } // if row r contains > 0 entries
469  } // for each locally owned row r
470  } // if the graph has a nonnull row Map
471 
472  return makeColMapImpl<LO, GO, NT>(
473  colMap, remotePIDs,
474  domMap,
475  numLocalColGIDs, numRemoteColGIDs,
476  RemoteGIDSet, RemoteGIDUnorderedVector, GIDisLocal,
477  sortEachProcsGids, errStrm);
478 
479  } // if the graph is globally indexed
480  else {
481  // If we reach this point, the graph is neither locally nor
482  // globally indexed. Thus, the graph is empty on this process
483  // (per the usual legacy Petra convention), so myColumns will be
484  // left empty.
485  ; // do nothing
486  }
487 
488  const global_size_t INV =
489  Tpetra::Details::OrdinalTraits<global_size_t>::invalid ();
490  // FIXME (mfh 05 Mar 2014) Doesn't the index base of a Map have to
491  // be the same as the Map's min GID? If the first column is empty
492  // (contains no entries), then the column Map's min GID won't
493  // necessarily be the same as the domain Map's index base.
494  const GO indexBase = domMap->getIndexBase ();
495  colMap = rcp (new map_type (INV, myColumns, indexBase, domMap->getComm ()));
496  return errCode;
497 }
498 
499 template<typename GO, typename device_t>
500 struct GatherPresentEntries
501 {
502  using bitset_t = Kokkos::Bitset<device_t>;
503  using mem_space = typename device_t::memory_space;
504  using GOView = Kokkos::View<GO*, mem_space>;
505 
506  GatherPresentEntries(GO minGID_, const GOView& gids_, bitset_t& present_)
507  : minGID(minGID_), gids(gids_), present(present_)
508  {}
509 
510  KOKKOS_INLINE_FUNCTION void operator()(const GO i) const
511  {
512  present.set(gids(i) - minGID);
513  }
514 
515  GO minGID;
516  GOView gids;
517  bitset_t present;
518 };
519 
520 template<typename LO, typename GO, typename device_t, typename LocalMapType, bool doingRemotes>
521 struct ListGIDs
522 {
523  using const_bitset_t = Kokkos::ConstBitset<device_t>;
524  using mem_space = typename device_t::memory_space;
525  using GOView = Kokkos::View<GO*, mem_space>;
526  using SingleView = Kokkos::View<GO, mem_space>;
527 
528  ListGIDs(GO minGID_, GOView& gidList_, SingleView& numElems_, const_bitset_t& present_, const LocalMapType& localDomainMap_)
529  : minGID(minGID_), gidList(gidList_), numElems(numElems_), present(present_), localDomainMap(localDomainMap_)
530  {}
531 
532  KOKKOS_INLINE_FUNCTION void operator()(const GO i, GO& lcount, const bool finalPass) const
533  {
534  bool isRemote = localDomainMap.getLocalElement(i + minGID) == ::Tpetra::Details::OrdinalTraits<LO>::invalid();
535  if(present.test(i) && doingRemotes == isRemote)
536  {
537  if(finalPass)
538  {
539  //lcount is the index where this GID should be inserted in gidList.
540  gidList(lcount) = minGID + i;
541  }
542  lcount++;
543  }
544  if((i == static_cast<GO>(present.size() - 1)) && finalPass)
545  {
546  //Set the number of inserted indices in a single-element view
547  numElems() = lcount;
548  }
549  }
550 
551  GO minGID;
552  GOView gidList;
553  SingleView numElems;
554  const_bitset_t present;
555  const LocalMapType localDomainMap;
556 };
557 
558 template<typename GO, typename mem_space>
559 struct MinMaxReduceFunctor
560 {
561  using MinMaxValue = typename Kokkos::MinMax<GO>::value_type;
562  using GOView = Kokkos::View<GO*, mem_space>;
563 
564  MinMaxReduceFunctor(const GOView& gids_)
565  : gids(gids_)
566  {}
567 
568  KOKKOS_INLINE_FUNCTION void operator()(const GO i, MinMaxValue& lminmax) const
569  {
570  GO gid = gids(i);
571  if(gid < lminmax.min_val)
572  lminmax.min_val = gid;
573  if(gid > lminmax.max_val)
574  lminmax.max_val = gid;
575  };
576 
577  const GOView gids;
578 };
579 
580 template <class LO, class GO, class NT>
581 int
582 makeColMap (Teuchos::RCP<const Tpetra::Map<LO, GO, NT>>& colMap,
583  const Teuchos::RCP<const Tpetra::Map<LO, GO, NT>>& domMap,
584  Kokkos::View<GO*, typename NT::memory_space> gids,
585  std::ostream* errStrm)
586 {
587  using Teuchos::RCP;
588  using Teuchos::Array;
589  using Kokkos::RangePolicy;
590  using device_t = typename NT::device_type;
591  using exec_space = typename device_t::execution_space;
592  using memory_space = typename device_t::memory_space;
593  using bitset_t = Kokkos::Bitset<device_t>;
594  using const_bitset_t = Kokkos::ConstBitset<device_t>;
595  using GOView = Kokkos::View<GO*, memory_space>;
596  using SingleView = Kokkos::View<GO, memory_space>;
597  using map_type = Tpetra::Map<LO, GO, NT>;
598  using LocalMap = typename map_type::local_map_type;
599  GO nentries = gids.extent(0);
600  GO minGID = Teuchos::OrdinalTraits<GO>::max();
601  GO maxGID = 0;
602  using MinMaxValue = typename Kokkos::MinMax<GO>::value_type;
603  MinMaxValue minMaxGID = {minGID, maxGID};
604  Kokkos::parallel_reduce(RangePolicy<exec_space>(0, nentries),
605  MinMaxReduceFunctor<GO, memory_space>(gids),
606  Kokkos::MinMax<GO>(minMaxGID));
607  minGID = minMaxGID.min_val; maxGID = minMaxGID.max_val;
608  //Now, know the full range of input GIDs.
609  //Determine the set of GIDs in the column map using a dense bitset, which corresponds to the range [minGID, maxGID]
610  bitset_t presentGIDs(maxGID - minGID + 1);
611  Kokkos::parallel_for(RangePolicy<exec_space>(0, nentries), GatherPresentEntries<GO, device_t>(minGID, gids, presentGIDs));
612  const_bitset_t constPresentGIDs(presentGIDs);
613  //Get the set of local and remote GIDs on device
614  SingleView numLocals("Num local GIDs");
615  SingleView numRemotes("Num remote GIDs");
616  GOView localGIDView(Kokkos::ViewAllocateWithoutInitializing("Local GIDs"), constPresentGIDs.count());
617  GOView remoteGIDView(Kokkos::ViewAllocateWithoutInitializing("Remote GIDs"), constPresentGIDs.count());
618  LocalMap localDomMap = domMap->getLocalMap();
619  //This lists the locally owned GIDs in localGIDView
620  Kokkos::parallel_scan(RangePolicy<exec_space>(0, constPresentGIDs.size()),
621  ListGIDs<LO, GO, device_t, LocalMap, false>
622  (minGID, localGIDView, numLocals, constPresentGIDs, localDomMap));
623  //And this lists the remote GIDs in remoteGIDView
624  Kokkos::parallel_scan(RangePolicy<exec_space>(0, constPresentGIDs.size()),
625  ListGIDs<LO, GO, device_t, LocalMap, true>
626  (minGID, remoteGIDView, numRemotes, constPresentGIDs, localDomMap));
627  //Pull down the sizes
628  GO numLocalColGIDs = 0;
629  GO numRemoteColGIDs = 0;
630  Kokkos::deep_copy(numLocalColGIDs, numLocals);
631  Kokkos::deep_copy(numRemoteColGIDs, numRemotes);
632  //Pull down the remote lists
633  auto localsHost = Kokkos::create_mirror_view(localGIDView);
634  auto remotesHost = Kokkos::create_mirror_view(remoteGIDView);
635  Kokkos::deep_copy(localsHost, localGIDView);
636  Kokkos::deep_copy(remotesHost, remoteGIDView);
637  //Finally, populate the STL structures which hold the index lists
638  std::set<GO> RemoteGIDSet;
639  std::vector<GO> RemoteGIDUnorderedVector;
640  std::vector<bool> GIDisLocal (domMap->getNodeNumElements (), false);
641  for(GO i = 0; i < numLocalColGIDs; i++)
642  {
643  GO gid = localsHost(i);
644  //Already know that gid is locally owned, so this index will never be invalid().
645  //makeColMapImpl uses this and the domain map to get the the local GID list.
646  GIDisLocal[domMap->getLocalElement(gid)] = true;
647  }
648  RemoteGIDUnorderedVector.reserve(numRemoteColGIDs);
649  for(GO i = 0; i < numRemoteColGIDs; i++)
650  {
651  GO gid = remotesHost(i);
652  RemoteGIDSet.insert(gid);
653  RemoteGIDUnorderedVector.push_back(gid);
654  }
655  //remotePIDs will be discarded in this version of makeColMap
656  Array<int> remotePIDs;
657  //Find the min and max GID
658  return makeColMapImpl<LO, GO, NT>(
659  colMap,
660  remotePIDs,
661  domMap,
662  static_cast<size_t>(numLocalColGIDs),
663  static_cast<size_t>(numRemoteColGIDs),
664  RemoteGIDSet,
665  RemoteGIDUnorderedVector,
666  GIDisLocal,
667  true, //always sort remotes
668  errStrm);
669 }
670 
671 } // namespace Details
672 } // namespace Tpetra
673 
674 //
675 // Explicit instantiation macros
676 //
677 // Must be expanded from within the Tpetra namespace!
678 //
679 #define TPETRA_DETAILS_MAKECOLMAP_INSTANT(LO,GO,NT) \
680  namespace Details { \
681  template int \
682  makeColMap (Teuchos::RCP<const Tpetra::Map<LO, GO, NT> >&, \
683  Teuchos::Array<int>&, \
684  const Teuchos::RCP<const Tpetra::Map<LO, GO, NT> >&, \
685  const RowGraph<LO, GO, NT>&, \
686  const bool, \
687  std::ostream*); \
688  template int \
689  makeColMap (Teuchos::RCP<const Tpetra::Map<LO, GO, NT> >&, \
690  const Teuchos::RCP<const Tpetra::Map<LO, GO, NT> >&, \
691  Kokkos::View<GO*, typename NT::memory_space>, \
692  std::ostream*); \
693  }
694 
695 #endif // TPETRA_DETAILS_MAKECOLMAP_DEF_HPP
virtual bool hasColMap() const =0
Whether the graph has a well-defined column Map.
An abstract interface for graphs accessed by rows.
virtual void getGlobalRowView(const GlobalOrdinal gblRow, Teuchos::ArrayView< const GlobalOrdinal > &gblColInds) const
Get a const, non-persisting view of the given global row&#39;s global column indices, as a Teuchos::Array...
LookupStatus
Return status of Map remote index lookup (getRemoteIndexList()).
virtual Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > getColMap() const =0
The Map that describes this graph&#39;s distribution of columns over processes.
&quot;Local&quot; part of Map suitable for Kokkos kernels.
size_t global_size_t
Global size_t object.
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.
global_ordinal_type getGlobalElement(local_ordinal_type localIndex) const
The global index corresponding to the given local index.
virtual Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > getRowMap() const =0
The Map that describes this graph&#39;s distribution of rows over processes.
virtual bool isGloballyIndexed() const =0
If graph indices are in the global range, this function returns true. Otherwise, this function return...
void sort2(const IT1 &first1, const IT1 &last1, const IT2 &first2)
Sort the first array, and apply the resulting permutation to the second array.
size_t getNodeNumElements() const
The number of elements belonging to the calling process.
A parallel distribution of indices over processes.
int makeColMap(Teuchos::RCP< const Tpetra::Map< LO, GO, NT > > &colMap, Teuchos::Array< int > &remotePIDs, const Teuchos::RCP< const Tpetra::Map< LO, GO, NT > > &domMap, const RowGraph< LO, GO, NT > &graph, const bool sortEachProcsGids=true, std::ostream *errStrm=NULL)
Make the graph&#39;s column Map.
Stand-alone utility functions and macros.
virtual bool isLocallyIndexed() const =0
If graph indices are in the local range, this function returns true. Otherwise, this function returns...