Zoltan2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ddirectoryTest.cpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Zoltan2: A package of combinatorial algorithms for scientific computing
4 //
5 // Copyright 2012 NTESS and the Zoltan2 contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 // Program that demonstrates how to emulate distributed directories that
11 // can sum-up entries using Tpetra classes (Map, Vector).
12 // Similar functionality (but, of course, without the sum) using a Zoltan_DD
13 // is also written.
14 // It would be fair to compare the runtimes of the Tpetra version with the
15 // Zoltan version, even without the sum operation. And if/when we implement
16 // the sum operation, we could add a test of it to this program.
17 
18 #include <Teuchos_RCP.hpp>
19 #include <Teuchos_ArrayView.hpp>
20 
21 #include <unordered_map>
22 
23 #include <Tpetra_Map.hpp>
24 #include <Tpetra_Vector.hpp>
25 
26 #include <Zoltan2_TPLTraits.hpp>
27 
28 #include <zoltan_dd_cpp.h>
29 #include <unordered_set>
30 
31 
32 static const size_t TOOMANY = 100;
33 
35 // Functions for debugging
36 template <typename T>
37 void printTpetraThing(T &thing, const std::string &msg,
38  std::ostream &ostr = std::cout)
39 {
40  ostr << msg << std::endl;
41  Teuchos::FancyOStream fancy(Teuchos::rcpFromRef(ostr));
42  thing.describe(fancy, Teuchos::VERB_EXTREME);
43 }
44 
45 template <typename vector_t>
46 void printVector(vector_t &vec, const std::string &msg,
47  std::ostream &ostr = std::cout)
48 {
49  if (vec.getGlobalLength() > TOOMANY) return;
50  printTpetraThing<vector_t>(vec, msg, ostr);
51 }
52 
53 template <typename map_t>
54 void printMap(map_t &map, const std::string &msg,
55  std::ostream &ostr = std::cout)
56 {
57  if (map.getGlobalNumElements() > TOOMANY) return;
58  printTpetraThing<map_t>(map, msg, ostr);
59 }
60 
62 // Class to generate input IDs based on input values
63 template <typename id_t>
64 class IDs {
65 public:
66 
67  IDs(size_t nIds_, float fracShared_, id_t idBase_, int idStride_,
68  Teuchos::RCP<const Teuchos::Comm<int> > &comm_) :
69  nIds(nIds_),
70  nShared(std::ceil(nIds * fracShared_)),
71  nUnique(nIds - nShared),
72  idBase(idBase_),
73  idStride(idStride_),
74  contiguous(idStride_ == 1),
75  ids(nIds, 0), comm(comm_)
76  {
77  int me = comm->getRank();
78  int np = comm->getSize();
79 
80  // Generate the uniquely owned IDs;
81  size_t cnt = 0;
82  for (size_t i = 0; i < nUnique; i++)
83  ids[cnt++] = idBase + ((me * nUnique + i) * idStride);
84 
85  // Generate the shared IDs; they are the highest-numbered IDs
86  size_t gnUnique = nUnique * np;
87  std::srand(me);
88 
89  for (size_t i = 0; i < nShared; i++) {
90  size_t r = rand() % nShared;
91  ids[cnt++] = idBase + ((gnUnique + r) * idStride);
92  }
93 
94  print();
95  }
96 
97  void print()
98  {
99  if (nIds > TOOMANY) return;
100 
101  int me = comm->getRank();
102 
103  std::cout << me << " nIds = " << nIds << "; nUnique = " << nUnique
104  << "; nShared = " << nShared << std::endl;
105 
106  std::cout << me << " Unique: ";
107  for (size_t i = 0; i < nUnique; i++) std::cout << ids[i] << " ";
108  std::cout << std::endl;
109 
110  std::cout << me << " Shared: ";
111  for (size_t i = 0; i < nShared; i++) std::cout << ids[nUnique+i] << " ";
112  std::cout << std::endl;
113  }
114 
115  bool TpetraDDTest();
116 
117  bool ZoltanDDTest();
118 
119 private:
120 
121  typedef int scalar_t; // use int since we are counting occurrences
122 
123 
124  size_t nIds; // Number of IDs per processor
125  size_t nShared; // Number of shared IDs per processor
126  size_t nUnique; // Number of unique IDs per processor
127 
128  id_t idBase; // Smallest possible ID
129  int idStride; // Offset between IDs; 1 provides contiguous
130  bool contiguous; // Flag indicating whether IDs are contiguous
131 
132  std::vector<id_t> ids; // Ids generated on this proc
133 
134  Teuchos::RCP<const Teuchos::Comm<int> > comm;
135 };
136 
137 
139 // Test of DDirectory-like functionality using Tpetra
140 // Basic steps:
141 // T1: Create an overlapped map M1 and a vector V1 that uses it;
142 // V1[i] = number of local occurrencts of ID i
143 // T2: Call createOneToOne to create a one-to-one map M2;
144 // create a vector M2 that uses it.
145 // T3: Create an Import object between the overlapped map and
146 // the one-to-one map.
147 // T4: Import with Tpetra::ADD from V1 to V2 to count the occurrences
148 // T5: Import with Tpetra::REPLACE from V2 to V1 to return the
149 // result to the original processors
150 
151 
152 template <typename id_t>
154 {
155  typedef typename Tpetra::Map<int, id_t> map_t;
156  typedef typename Teuchos::RCP<const map_t> rcpmap_t;
157 
158  typedef typename Tpetra::Vector<scalar_t, int, id_t> vector_t;
159  typedef typename Teuchos::ArrayRCP<scalar_t> vectordata_t;
160 
161  // Step T1
162 
163  // Find unique IDs numbers and indexBase on this processor for
164  // constructing overlapping Tpetra::Map for IDs.
165 
166  id_t minId = std::numeric_limits<id_t>::max();
167 
168  std::unordered_map<id_t, size_t> uniqueIds;
169  uniqueIds.reserve(nIds); // Worst case
170 
171  for (size_t i = 0; i < nIds; i++) {
172  id_t id = ids[i];
173  if (id < minId) minId = id;
174  if (uniqueIds.find(id) == uniqueIds.end())
175  uniqueIds[id] = 1;
176  else
177  uniqueIds[id]++;
178  }
179 
180  // Compute global indexBase
181 
182  id_t indexBase;
183  Teuchos::reduceAll(*comm, Teuchos::REDUCE_MIN, 1, &minId, &indexBase);
184 
185  // Create list of locally unique IDs to use in Tpetra::Map creation
186 
187  size_t nUniqueIds = uniqueIds.size();
188  Teuchos::Array<id_t> uniqueIdsList(nUniqueIds);
189 
190  size_t cnt = 0;
191  for (auto it = uniqueIds.begin(); it != uniqueIds.end(); it++)
192  uniqueIdsList[cnt++] = it->first;
193 
194  // Build Tpetra::Map for the given local ids
195 
196  size_t dummy = Teuchos::OrdinalTraits<Tpetra::global_size_t>::invalid();
197 
198  rcpmap_t idMap = Teuchos::rcp(new map_t(dummy, uniqueIdsList(), indexBase,
199  comm), true);
200 
201  // Create Vector using this map.
202  // This vector will store number of occurrences of each id across procs
203 
204  vector_t idVec(idMap, 0.);
205 
206  // Use the multivector for counting number of occurrences of each id
207 
208  {
209  vectordata_t idData = idVec.getDataNonConst();
210  for (auto it = uniqueIds.begin(); it != uniqueIds.end(); it++) {
211  id_t idx = idMap->getLocalElement(it->first);
212  idData[idx] = it->second;
213  }
214  }
215 
216  printMap(*idMap, "idMap ");
217 
218  printVector(idVec, "idVec before ");
219 
220  // Step T2
221 
222  // Create a one-to-one map and a vector that uses it
223 
224  rcpmap_t oto_idMap;
225  if (contiguous) {
226  // For contigous ids, can use Tpetra default Map
227  id_t gnUnique = idMap->getMaxAllGlobalIndex() - indexBase + 1;
228  oto_idMap = Teuchos::rcp(new map_t(gnUnique, indexBase, comm));
229  }
230  else {
231  // Since ids may not be contiguous, cannot use default Tpetra Map
232  oto_idMap = Tpetra::createOneToOne(idMap);
233  }
234 
235  vector_t oto_idVec(oto_idMap);
236 
237  // Step T3
238 
239  // Create an exporter between the two maps
240 
241  typedef Tpetra::Export<int, id_t> export_t;
242 
243  export_t idExporter(idMap, oto_idMap);
244 
245  printTpetraThing(idExporter, "exporter ");
246 
247  // Step T4
248 
249  // Compute the number of occurrences of shared ids
250  printMap(*oto_idMap, "oto_idMap ");
251 
252  printVector(oto_idVec, "oto_idVec BEFORE EXPORT");
253 
254  oto_idVec.doExport(idVec, idExporter, Tpetra::ADD);
255 
256  printVector(oto_idVec, "oto_idVec AFTER EXPORT");
257 
258  // Step T5
259 
260  // Send the result back to orig processors
261  // so that we can identify shared IDs in orig procs
262 
263  idVec.doImport(oto_idVec, idExporter, Tpetra::REPLACE);
264 
265  printVector(idVec, "idVec after ");
266 
267  // Check the result
268  size_t cntShared = 0;
269  {
270  auto idData = idVec.getDataNonConst();
271  for (size_t i = 0; i < idVec.getLocalLength(); i++)
272  if (idData[i] > 1) cntShared++;
273  }
274 
275  std::cout << comm->getRank() << " cntShared = " << cntShared
276  << "; nShared = " << nShared << std::endl;
277 
278  return (cntShared == nShared);
279 }
280 
282 // Perform nearly same operations using Zoltan DD.
283 // Note that Zoltan cannot current do Tpetra::ADD; it will do Tpetra::REPLACE.
284 // (That is why we are doing this study!)
285 // Thus, the computed results will not be correct, but except for the addition,
286 // the mechanics are all the same.
287 
288 template <typename id_t>
290 {
291 
292  if (nIds > size_t(std::numeric_limits<int>::max()))
293  throw std::runtime_error("Problem too large for Zoltan_DD");
294 
295  int inIds = int(nIds);
296 
297  // Build a Zoltan directory for the IDs; don't care about local IDs
298 
299 #ifdef HAVE_MPI
300  MPI_Comm mpicomm = Teuchos::getRawMpiComm(*comm);
301 #else
302  { // Use siMPI from Zoltan here; make sure it is initialized
303  int flag;
304  MPI_Initialized(&flag);
305  if (!flag) {
306  int narg = 0;
307  char **argv = NULL;
308  MPI_Init(&narg, &argv);
309  }
310  }
311  MPI_Comm mpicomm = MPI_COMM_WORLD;
312 #endif
313 
315 
316  Zoltan_DD zz(mpicomm, nIdEnt, 0, sizeof(scalar_t), inIds, 0);
317 
318  // TODO: Can think about appropriate hash functions here.
319  // To match Tpetra's default map, we could use something like DD_Hash_Fn1.
320 
321  // Allocate space for user data.
322  // In this case, the user data is occurrence counts that we would like to
323  // add up in the directory.
324  std::vector<scalar_t> user(nIds, 1.);
325 
326  // Depending on size of id_t, may need to copy IDs to array of ZOLTAN_ID_TYPE
327  // TODO: Add TPL_Traits that don't require ArrayView. Doh.
328 
329  ZOLTAN_ID_PTR zgids = NULL;
330  if (nIds) {
331  Teuchos::ArrayView<id_t> av(&(ids[0]), nIds);
333  }
334 
335  // To do the summation, we'd need this function to be UpdateAdd or
336  // Update with an operator argument like Tpetra::ADD.
337  // For now, we'll just do the update (which does a replace-like operation).
338  zz.Update(zgids, NULL, (char*)(nIds ? &(user[0]) : NULL), NULL, inIds);
339 
340  // Retrieve the result for all local IDs.
341  zz.Find(zgids, NULL, (char*)(nIds ? &(user[0]) : NULL), NULL, inIds, NULL);
342 
343  // The following step is needed only to test the results;
344  // for general use, if user[i] > 1 in the summation, id[i] is shared.
345  size_t cntShared = 0;
346  std::unordered_set<id_t> alreadyCounted;
347  for (size_t i = 0; i < nIds; i++) {
348  if (user[i] > 1) {
349  // Id is shared; have we already counted it locally?
350  if (alreadyCounted.find(ids[i]) == alreadyCounted.end()) {
351  alreadyCounted.insert(ids[i]);
352  cntShared++;
353  }
354  }
355  }
356 
357  if ((nIds * comm->getSize()) <= TOOMANY) zz.Print();
358 
359  return (cntShared == nShared);
360 }
361 
363 int main(int narg, char **arg)
364 {
365  Tpetra::ScopeGuard tscope(&narg, &arg);
366  Teuchos::RCP<const Teuchos::Comm<int> > comm = Tpetra::getDefaultComm();
367 
368  // Test with contiguous IDs of default Tpetra GO type
369  {
370  size_t nIds = 5; // Number of IDs per processor
371  float fracShared = 0.2; // Fraction of IDs that should be shared
372  size_t idBase = 0; // Smallest possible ID
373  int idStride = 1; // Offset between IDs; 1 gives contiguous numbering
374 
375  typedef Tpetra::Map<>::global_ordinal_type gno_t;
376  IDs<gno_t> myIds(nIds, fracShared, idBase, idStride, comm);
377 
378  myIds.TpetraDDTest();
379 
380  myIds.ZoltanDDTest();
381  }
382 
383  // Test with non-contiguous IDs of default Tpetra GO starting at 20
384  {
385  size_t nIds = 5; // Number of IDs per processor
386  float fracShared = 0.2; // Fraction of IDs that should be shared
387  size_t idBase = 20; // Smallest possible ID
388  int idStride = 3; // Offset between IDs; 1 gives contiguous numbering
389 
390  typedef Tpetra::Map<>::global_ordinal_type gno_t;
391  IDs<gno_t> myIds(nIds, fracShared, idBase, idStride, comm);
392 
393  myIds.TpetraDDTest();
394 
395  myIds.ZoltanDDTest();
396  }
397 
398 #ifdef HAVE_TPETRA_INT_INT
399  // Test with contiguous integer IDs
400  {
401  size_t nIds = 5; // Number of IDs per processor
402  float fracShared = 0.2; // Fraction of IDs that should be shared
403  size_t idBase = 0; // Smallest possible ID
404  int idStride = 1; // Offset between IDs; 1 gives contiguous numbering
405 
406  IDs<int> myIds(nIds, fracShared, idBase, idStride, comm);
407 
408  myIds.TpetraDDTest();
409 
410  myIds.ZoltanDDTest();
411  }
412 
413  // Test with non-contiguous integer IDs starting at 20
414  {
415  size_t nIds = 5; // Number of IDs per processor
416  float fracShared = 0.2; // Fraction of IDs that should be shared
417  size_t idBase = 20; // Smallest possible ID
418  int idStride = 3; // Offset between IDs; 1 gives contiguous numbering
419 
420  IDs<int> myIds(nIds, fracShared, idBase, idStride, comm);
421 
422  myIds.TpetraDDTest();
423 
424  myIds.ZoltanDDTest();
425  }
426 #endif
427 
428 #ifdef HAVE_TPETRA_INT_LONG_LONG
429  // Test with non-contiguous long long IDs starting at 200
430  {
431  size_t nIds = 5; // Number of IDs per processor
432  float fracShared = 0.4; // Fraction of IDs that should be shared
433  size_t idBase = 200; // Smallest possible ID
434  int idStride = 4; // Offset between IDs; 1 gives contiguous numbering
435 
436  IDs<long long> myIds(nIds, fracShared, idBase, idStride, comm);
437 
438  myIds.TpetraDDTest();
439 
440  myIds.ZoltanDDTest();
441  }
442 #endif
443 }
void print()
void printTpetraThing(T &thing, const std::string &msg, std::ostream &ostr=std::cout)
map_t::global_ordinal_type gno_t
Definition: mapRemotes.cpp:27
int main(int narg, char **arg)
Definition: coloring1.cpp:164
void printVector(vector_t &vec, const std::string &msg, std::ostream &ostr=std::cout)
void printMap(map_t &map, const std::string &msg, std::ostream &ostr=std::cout)
bool ZoltanDDTest()
static const size_t TOOMANY
IDs(size_t nIds_, float fracShared_, id_t idBase_, int idStride_, Teuchos::RCP< const Teuchos::Comm< int > > &comm_)
Tpetra::Map map_t
Definition: mapRemotes.cpp:25
Traits class to handle conversions between gno_t/lno_t and TPL data types (e.g., ParMETIS&#39;s idx_t...
bool TpetraDDTest()
static void ASSIGN_ARRAY(first_t **a, ArrayView< second_t > &b)