Tpetra parallel linear algebra  Version of the Day
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 <set>
59 #include <vector>
60 
61 namespace Tpetra {
62 namespace Details {
63 
64 template <class LO, class GO, class NT>
65 int
66 makeColMap (Teuchos::RCP<const Tpetra::Map<LO, GO, NT> >& colMap,
67  const Teuchos::RCP<const Tpetra::Map<LO, GO, NT> >& domMap,
68  const RowGraph<LO, GO, NT>& graph,
69  const bool sortEachProcsGids,
70  std::ostream* errStrm)
71 {
72  using Teuchos::Array;
73  using Teuchos::ArrayView;
74  using Teuchos::rcp;
75  using std::endl;
76  typedef ::Tpetra::Map<LO, GO, NT> map_type;
77  const char prefix[] = "Tpetra::Details::makeColMap: ";
78  int errCode = 0;
79 
80  // If the input domain Map or its communicator is null on the
81  // calling process, then the calling process does not participate in
82  // the returned column Map. Thus, we can set the returned column
83  // Map to null on those processes, and return immediately. This is
84  // not an error condition, as long as when domMap and its
85  // communicator are NOT null, the graph's other Maps and
86  // communicator are not also null.
87  if (domMap.is_null () || domMap->getComm ().is_null ()) {
88  colMap = Teuchos::null;
89  return errCode;
90  }
91 
92  // After the calling process is done going through all of the rows
93  // it owns, myColumns will contain the list of indices owned by this
94  // process in the column Map.
95  Array<GO> myColumns;
96 
97  if (graph.isLocallyIndexed ()) {
98  colMap = graph.getColMap ();
99  // If the graph is locally indexed, it had better have a column Map.
100  // The extra check for ! graph.hasColMap() is conservative.
101  if (colMap.is_null () || ! graph.hasColMap ()) {
102  errCode = -1;
103  if (errStrm != NULL) {
104  *errStrm << prefix << "The graph is locally indexed on the calling "
105  "process, but has no column Map (either getColMap() returns null, "
106  "or hasColMap() returns false)." << endl;
107  }
108  // Under this error condition, this process will not fill
109  // myColumns. The resulting new column Map will be incorrect,
110  // but at least we won't hang, and this process will report the
111  // error.
112  }
113  else {
114  // The graph already has a column Map, and is locally indexed on
115  // the calling process. However, it may be globally indexed (or
116  // neither locally nor globally indexed) on other processes.
117  // Assume that we want to recreate the column Map.
118  if (colMap->isContiguous ()) {
119  // The number of indices on each process must fit in LO.
120  const LO numCurGids = static_cast<LO> (colMap->getNodeNumElements ());
121  myColumns.resize (numCurGids);
122  const GO myFirstGblInd = colMap->getMinGlobalIndex ();
123  for (LO k = 0; k < numCurGids; ++k) {
124  myColumns[k] = myFirstGblInd + static_cast<GO> (k);
125  }
126  }
127  else { // the column Map is NOT contiguous
128  ArrayView<const GO> curGids = graph.getColMap ()->getNodeElementList ();
129  // The number of indices on each process must fit in LO.
130  const LO numCurGids = static_cast<LO> (curGids.size ());
131  myColumns.resize (numCurGids);
132  for (LO k = 0; k < numCurGids; ++k) {
133  myColumns[k] = curGids[k];
134  }
135  } // whether the graph's current column Map is contiguous
136  } // does the graph currently have a column Map?
137  }
138  else if (graph.isGloballyIndexed ()) {
139  // Go through all the rows, finding the populated column indices.
140  //
141  // Our final list of indices for the column Map constructor will
142  // have the following properties (all of which are with respect to
143  // the calling process):
144  //
145  // 1. Indices in the domain Map go first.
146  // 2. Indices not in the domain Map follow, ordered first
147  // contiguously by their owning process rank (in the domain
148  // Map), then in increasing order within that.
149  // 3. No duplicate indices.
150  //
151  // This imitates the ordering used by Aztec(OO) and Epetra.
152  // Storing indices owned by the same process (in the domain Map)
153  // contiguously permits the use of contiguous send and receive
154  // buffers.
155  //
156  // We begin by partitioning the column indices into "local" GIDs
157  // (owned by the domain Map) and "remote" GIDs (not owned by the
158  // domain Map). We use the same order for local GIDs as the
159  // domain Map, so we track them in place in their array. We use
160  // an std::set (RemoteGIDSet) to keep track of remote GIDs, so
161  // that we don't have to merge duplicates later.
162  const LO LINV = Tpetra::Details::OrdinalTraits<LO>::invalid ();
163  size_t numLocalColGIDs = 0;
164  size_t numRemoteColGIDs = 0;
165 
166  // GIDisLocal[lid] is false if and only if local index lid in the
167  // domain Map is remote (not local).
168  std::vector<bool> GIDisLocal (domMap->getNodeNumElements (), false);
169  std::set<GO> RemoteGIDSet;
170  // This preserves the not-sorted Epetra order of GIDs.
171  // We only use this if sortEachProcsGids is false.
172  std::vector<GO> RemoteGIDUnorderedVector;
173 
174  if (! graph.getRowMap ().is_null ()) {
175  const Tpetra::Map<LO, GO, NT>& rowMap = * (graph.getRowMap ());
176  const LO lclNumRows = rowMap.getNodeNumElements ();
177  for (LO lclRow = 0; lclRow < lclNumRows; ++lclRow) {
178  const GO gblRow = rowMap.getGlobalElement (lclRow);
179  Teuchos::ArrayView<const GO> rowGids;
180  graph.getGlobalRowView (gblRow, rowGids);
181 
182  const LO numEnt = static_cast<LO> (rowGids.size ());
183  if (numEnt != 0) {
184  for (LO k = 0; k < numEnt; ++k) {
185  const GO gid = rowGids[k];
186  const LO lid = domMap->getLocalElement (gid);
187  if (lid != LINV) {
188  const bool alreadyFound = GIDisLocal[lid];
189  if (! alreadyFound) {
190  GIDisLocal[lid] = true;
191  ++numLocalColGIDs;
192  }
193  }
194  else {
195  const bool notAlreadyFound = RemoteGIDSet.insert (gid).second;
196  if (notAlreadyFound) { // gid did not exist in the set before
197  if (! sortEachProcsGids) {
198  // The user doesn't want to sort remote GIDs (for each
199  // remote process); they want us to keep remote GIDs
200  // in their original order. We do this by stuffing
201  // each remote GID into an array as we encounter it
202  // for the first time. The std::set helpfully tracks
203  // first encounters.
204  RemoteGIDUnorderedVector.push_back (gid);
205  }
206  ++numRemoteColGIDs;
207  }
208  }
209  } // for each entry k in row r
210  } // if row r contains > 0 entries
211  } // for each locally owned row r
212  } // if the graph has a nonnull row Map
213 
214  // Possible short-circuit for serial scenario:
215  //
216  // If all domain GIDs are present as column indices, then set
217  // ColMap=DomainMap. By construction, LocalGIDs is a subset of
218  // DomainGIDs.
219  //
220  // If we have
221  // * Number of remote GIDs is 0, so that ColGIDs == LocalGIDs,
222  // and
223  // * Number of local GIDs is number of domain GIDs
224  // then
225  // * LocalGIDs \subset DomainGIDs && size(LocalGIDs) ==
226  // size(DomainGIDs) => DomainGIDs == LocalGIDs == ColGIDs
227  // on the calling process.
228  //
229  // We will concern ourselves only with the special case of a
230  // serial DomainMap, obviating the need for communication.
231  //
232  // If
233  // * DomainMap has a serial communicator
234  // then we can set the column Map as the domain Map
235  // return. Benefit: this graph won't need an Import object
236  // later.
237  //
238  // Note, for a serial domain map, there can be no RemoteGIDs,
239  // because there are no remote processes. Likely explanations
240  // for this are:
241  // * user submitted erroneous column indices
242  // * user submitted erroneous domain Map
243  if (domMap->getComm ()->getSize () == 1) {
244  if (numRemoteColGIDs != 0) {
245  errCode = -2;
246  if (errStrm != NULL) {
247  *errStrm << prefix << "The domain Map only has one process, but "
248  << numRemoteColGIDs << " column "
249  << (numRemoteColGIDs != 1 ? "indices are" : "index is")
250  << " not in the domain Map. Either these indices are "
251  "invalid or the domain Map is invalid. Remember that nonsquare "
252  "matrices, or matrices where the row and range Maps differ, "
253  "require calling the version of fillComplete that takes the "
254  "domain and range Maps as input." << endl;
255  }
256  }
257  if (numLocalColGIDs == domMap->getNodeNumElements ()) {
258  colMap = domMap; // shallow copy
259  return errCode;
260  }
261  }
262 
263  // Populate myColumns with a list of all column GIDs. Put
264  // locally owned (in the domain Map) GIDs at the front: they
265  // correspond to "same" and "permuted" entries between the
266  // column Map and the domain Map. Put remote GIDs at the back.
267  myColumns.resize (numLocalColGIDs + numRemoteColGIDs);
268  // get pointers into myColumns for each part
269  ArrayView<GO> LocalColGIDs = myColumns (0, numLocalColGIDs);
270  ArrayView<GO> remoteColGIDs = myColumns (numLocalColGIDs, numRemoteColGIDs);
271 
272  // Copy the remote GIDs into myColumns
273  if (sortEachProcsGids) {
274  // The std::set puts GIDs in increasing order.
275  std::copy (RemoteGIDSet.begin(), RemoteGIDSet.end(),
276  remoteColGIDs.begin());
277  }
278  else {
279  // Respect the originally encountered order.
280  std::copy (RemoteGIDUnorderedVector.begin(),
281  RemoteGIDUnorderedVector.end(), remoteColGIDs.begin());
282  }
283 
284  // Make a list of process ranks corresponding to the remote GIDs.
285  Array<int> remotePIDs (numRemoteColGIDs);
286  // Look up the remote process' ranks in the domain Map.
287  {
288  const LookupStatus stat =
289  domMap->getRemoteIndexList (remoteColGIDs, remotePIDs ());
290 
291  // If any process returns IDNotPresent, then at least one of
292  // the remote indices was not present in the domain Map. This
293  // means that the Import object cannot be constructed, because
294  // of incongruity between the column Map and domain Map.
295  // This has two likely causes:
296  // - The user has made a mistake in the column indices
297  // - The user has made a mistake with respect to the domain Map
298  if (stat == IDNotPresent) {
299  if (errStrm != NULL) {
300  *errStrm << prefix << "Some column indices are not in the domain Map."
301  "Either these column indices are invalid or the domain Map is "
302  "invalid. Likely cause: For a nonsquare matrix, you must give the "
303  "domain and range Maps as input to fillComplete." << endl;
304  }
305  // Don't return yet, because not all processes may have
306  // encountered this error state. This function ends with an
307  // all-reduce, so we have to make sure that everybody gets to
308  // that point. The resulting Map may be wrong, but at least
309  // nothing should crash.
310  errCode = -3;
311  }
312  }
313 
314  // Sort incoming remote column indices by their owning process
315  // rank, so that all columns coming from a given remote process
316  // are contiguous. This means the Import's Distributor doesn't
317  // need to reorder data.
318  //
319  // NOTE (mfh 02 Sep 2014) This needs to be a stable sort, so that
320  // it respects either of the possible orderings of GIDs (sorted,
321  // or original order) specified above.
322  sort2 (remotePIDs.begin(), remotePIDs.end(), remoteColGIDs.begin());
323 
324  // Copy the local GIDs into myColumns. Two cases:
325  // 1. If the number of Local column GIDs is the same as the number
326  // of Local domain GIDs, we can simply read the domain GIDs
327  // into the front part of ColIndices (see logic above from the
328  // serial short circuit case)
329  // 2. We step through the GIDs of the DomainMap, checking to see
330  // if each domain GID is a column GID. We want to do this to
331  // maintain a consistent ordering of GIDs between the columns
332  // and the domain.
333 
334  const size_t numDomainElts = domMap->getNodeNumElements ();
335  if (numLocalColGIDs == numDomainElts) {
336  // If the number of locally owned GIDs are the same as the
337  // number of local domain Map elements, then the local domain
338  // Map elements are the same as the locally owned GIDs.
339  if (domMap->isContiguous ()) {
340  // NOTE (mfh 03 Mar 2013, 02 Sep 2014) In the common case that
341  // the domain Map is contiguous, it's more efficient to avoid
342  // calling getNodeElementList(), since that permanently
343  // constructs and caches the GID list in the contiguous Map.
344  GO curColMapGid = domMap->getMinGlobalIndex ();
345  for (size_t k = 0; k < numLocalColGIDs; ++k, ++curColMapGid) {
346  LocalColGIDs[k] = curColMapGid;
347  }
348  }
349  else {
350  ArrayView<const GO> domainElts = domMap->getNodeElementList ();
351  std::copy (domainElts.begin(), domainElts.end(), LocalColGIDs.begin());
352  }
353  }
354  else {
355  // Count the number of locally owned GIDs, both to keep track of
356  // the current array index, and as a sanity check.
357  size_t numLocalCount = 0;
358  if (domMap->isContiguous ()) {
359  // NOTE (mfh 03 Mar 2013, 02 Sep 2014) In the common case that
360  // the domain Map is contiguous, it's more efficient to avoid
361  // calling getNodeElementList(), since that permanently
362  // constructs and caches the GID list in the contiguous Map.
363  GO curColMapGid = domMap->getMinGlobalIndex ();
364  for (size_t i = 0; i < numDomainElts; ++i, ++curColMapGid) {
365  if (GIDisLocal[i]) {
366  LocalColGIDs[numLocalCount++] = curColMapGid;
367  }
368  }
369  }
370  else {
371  ArrayView<const GO> domainElts = domMap->getNodeElementList ();
372  for (size_t i = 0; i < numDomainElts; ++i) {
373  if (GIDisLocal[i]) {
374  LocalColGIDs[numLocalCount++] = domainElts[i];
375  }
376  }
377  }
378  if (numLocalCount != numLocalColGIDs) {
379  if (errStrm != NULL) {
380  *errStrm << prefix << "numLocalCount = " << numLocalCount
381  << " != numLocalColGIDs = " << numLocalColGIDs
382  << ". This should never happen. "
383  "Please report this bug to the Tpetra developers." << endl;
384  }
385  // Don't return yet, because not all processes may have
386  // encountered this error state. This function ends with an
387  // all-reduce, so we have to make sure that everybody gets to
388  // that point.
389  errCode = -4;
390  }
391  }
392 
393  // FIXME (mfh 03 Apr 2013) Now would be a good time to use the
394  // information we collected above to construct the Import. In
395  // particular, building an Import requires:
396  //
397  // 1. numSameIDs (length of initial contiguous sequence of GIDs
398  // on this process that are the same in both Maps; this
399  // equals the number of domain Map elements on this process)
400  //
401  // 2. permuteToLIDs and permuteFromLIDs (both empty in this
402  // case, since there's no permutation going on; the column
403  // Map starts with the domain Map's GIDs, and immediately
404  // after them come the remote GIDs)
405  //
406  // 3. remoteGIDs (exactly those GIDs that we found out above
407  // were not in the domain Map) and remoteLIDs (which we could
408  // have gotten above by using the three-argument version of
409  // getRemoteIndexList() that computes local indices as well
410  // as process ranks, instead of the two-argument version that
411  // was used above)
412  //
413  // 4. remotePIDs (which we have from the getRemoteIndexList()
414  // call above)
415  //
416  // 5. Sorting remotePIDs, and applying that permutation to
417  // remoteGIDs and remoteLIDs (by calling sort3 above instead
418  // of sort2)
419  //
420  // 6. Everything after the sort3 call in Import::setupExport():
421  // a. Create the Distributor via createFromRecvs(), which
422  // computes exportGIDs and exportPIDs
423  // b. Compute exportLIDs from exportGIDs (by asking the
424  // source Map, in this case the domain Map, to convert
425  // global to local)
426  //
427  // Steps 1-5 come for free, since we must do that work anyway in
428  // order to compute the column Map. In particular, Step 3 is
429  // even more expensive than Step 6a, since it involves both
430  // creating and using a new Distributor object.
431  } // if the graph is globally indexed
432  else {
433  // If we reach this point, the graph is neither locally nor
434  // globally indexed. Thus, the graph is empty on this process
435  // (per the usual legacy Petra convention), so myColumns will be
436  // left empty.
437  ; // do nothing
438  }
439 
440  const global_size_t INV =
441  Tpetra::Details::OrdinalTraits<global_size_t>::invalid ();
442  // FIXME (mfh 05 Mar 2014) Doesn't the index base of a Map have to
443  // be the same as the Map's min GID? If the first column is empty
444  // (contains no entries), then the column Map's min GID won't
445  // necessarily be the same as the domain Map's index base.
446  const GO indexBase = domMap->getIndexBase ();
447  colMap = rcp (new map_type (INV, myColumns, indexBase, domMap->getComm ()));
448  return errCode;
449 }
450 
451 } // namespace Details
452 } // namespace Tpetra
453 
454 //
455 // Explicit instantiation macros
456 //
457 // Must be expanded from within the Tpetra::Details namespace!
458 //
459 #define TPETRA_DETAILS_MAKECOLMAP_INSTANT(LO,GO,NT) template int makeColMap (Teuchos::RCP<const Tpetra::Map<LO, GO, NT> >&, const Teuchos::RCP<const Tpetra::Map<LO, GO, NT> >&, const RowGraph<LO, GO, NT>&, const bool, std::ostream*);
460 
461 #endif // TPETRA_DETAILS_MAKECOLMAP_DEF_HPP
virtual bool hasColMap() const =0
Indicates whether the graph has a well-defined column map.
Namespace Tpetra contains the class and methods constituting the Tpetra library.
An abstract interface for graphs accessed by rows.
LookupStatus
Return status of Map remote index lookup (getRemoteIndexList()).
int makeColMap(Teuchos::RCP< const Tpetra::Map< LO, GO, NT > > &colMap, 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.
virtual Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > getColMap() const =0
The Map that describes this graph&#39;s distribution of columns over processes.
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...
size_t getNodeNumElements() const
The number of elements belonging to the calling process.
Implementation details of Tpetra.
size_t global_size_t
Global size_t object.
virtual Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > getRowMap() const =0
The Map that describes this graph&#39;s distribution of rows over processes.
GlobalOrdinal getGlobalElement(LocalOrdinal localIndex) const
The global index corresponding to the given local index.
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.
A parallel distribution of indices over processes.
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...