Zoltan2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
directoryTest_KokkosSimple.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 
11 #include "Tpetra_Core.hpp"
12 
13 // This type will be used by some of these tests
14 class gid_struct {
15  public:
16  // Kokkos will need an empty constructor so either provide one or
17  // make a simple struct with no constructors will also work
19  gid_struct(int v0, int v1, int v2, int v3) {
20  val[0] = v0;
21  val[1] = v1;
22  val[2] = v2;
23  val[3] = v3;
24  }
25  int val[4]; // can be any length but this can't have mem alloc (like std::vector)
26 };
27 
28 // same as gid but for better coverage of testing, make it different
29 class lid_struct {
30  public:
31  // Kokkos will need an empty constructor so either provide one or
32  // make a simple struct with no constructors will also work
34  lid_struct(unsigned long v0, unsigned long v1) {
35  val[0] = v0;
36  val[1] = v1;
37  }
38  unsigned long val[2]; // can be any length but this can't have mem alloc (like std::vector)
39 };
40 
41 // a very simple test of replace
42 // after calling update and find we do a second round with new gids
43 // to make sure repeated update calls will work.
44 int test_simple_replace(Teuchos::RCP<const Teuchos::Comm<int> > comm) {
45 
46  int err = 0;
47 
48  // define a few constants/helpers
49  typedef int test_lid_t; // lid will be ignored in this case
50  typedef int test_gid_t;
51  typedef long user_t;
52 
53  // set up the directory type - this is the single user mode (not vector user)
55 
56  // create the directory
57  directory_t directory(comm, false, 0);
58 
59  // now create some gids to write
60  // to keep things interesting we'll have just proc 0 write 4 Ids and all the
61  // other procs right the same gid (3).
62  std::vector<test_gid_t> writeGIDs;
63 
64  if(comm->getRank() == 0) {
65  writeGIDs = { 1, 5, 7, 10 };
66  }
67  else {
68  writeGIDs = { 3 };
69  }
70 
71  // now create some user values associated with above gids
72  // for easy reference just make them gid x 10
73  std::vector<user_t> writeUser;
74  if(comm->getRank() == 0) {
75  writeUser = { 10, 50, 70, 100 };
76  }
77  else {
78  writeUser = { 30 };
79  }
80 
81  // call update on the directory
82  directory.update(writeGIDs.size(), &writeGIDs[0], NULL, &writeUser[0], NULL,
84 
85  // now pick some gids to find
86  std::vector<test_gid_t> findIds = { 3, 5 };
87 
88  // now create a user space to accept the values
89  // Setting this empty will turn off this option. The original directory uses
90  // ptrs and null which has some advantages so this API choice might need some
91  // better setup.
92  std::vector<user_t> findUser(findIds.size());
93 
94  // now call find which will fill findUser
95  // for special case of mpi proc 1 the find of gid 3 will give a throw
96  // because rank 1 never existed to write it. This test is currently setup
97  // so it will pass for any mpi count so for this special case we turn this
98  // into a test to verify the throw happened. If mpi count is not 1, then
99  // a throw will not happen (or if it does it's a real error).
100  // However maybe it's better to change this so the simple test example
101  // doesn't have this complication ... to do.
102  try {
103  directory.find(findIds.size(), &findIds[0], NULL, &findUser[0], NULL, NULL);
104 
105  // now check element 0 in the array - make sure it matches writeUser above
106  // all procs except 0 sent this one
107  if(findUser[0] != 30) {
108  std::cout << "We should have gotten 30 for this value." << std::endl;
109  ++err;
110  }
111 
112  // only proc 0 sent the second one - make sure all procs can read it
113  if(findUser[1] != 50) {
114  std::cout << "We should have gotten 50 for this value." << std::endl;
115  ++err;
116  }
117  }
118  catch(std::logic_error &e) {
119  if(comm->getSize() != 1) {
120  err = 1;
121  }
122  else {
123  std::cout << "Successfully detected throw since find failed." << std::endl;
124  }
125  }
126 
127  // Phase 2
128  // now let's test further by updating with some more values
129  // we'll try a mix of preexisting values and new values
130  // this adds some unit testing coverage for the case when update is called
131  // twice on the same directory, not implemented in the general unit tests.
132  std::vector<test_gid_t> writeGIDs2;
133 
134  if(comm->getRank() == 0) {
135  writeGIDs2 = { 5, 700, 1000 };
136  }
137  else {
138  writeGIDs2 = { 3, 200 };
139  }
140 
141  std::vector<user_t> writeUser2;
142  if(comm->getRank() == 0) {
143  writeUser2 = { 50, 7000, 10000 };
144  }
145  else {
146  writeUser2 = { 30, 2000 };
147  }
148 
149  // call update on the directory (this will be the second round)
150  directory.update(writeGIDs2.size(), &writeGIDs2[0], NULL, &writeUser2[0], NULL,
152 
153  // now pick some gids to find
154  std::vector<test_gid_t> findIds2 = { 1, 5, 1000 };
155 
156  // now create a user space to accept the values
157  std::vector<user_t> findUser2(findIds2.size());
158  directory.find(findIds2.size(), &findIds2[0], NULL, &findUser2[0], NULL, NULL);
159 
160  // validate the results
161  // index 0 (gid 1) was updated on the first round (not second)
162  // index 1 (gid 5) was updated on both rounds
163  // index 2 (gid 1000) was updated on the second round (not first)
164  if(findUser2[0] != 10) {
165  std::cout << "We should have gotten 10 for this value. " << std::endl;
166  ++err;
167  }
168  if(findUser2[1] != 50) {
169  std::cout << "We should have gotten 50 for this value." << std::endl;
170  ++err;
171  }
172 
173  // only proc 0 sent the second one - make sure all procs can read it
174  if(findUser2[2] != 10000) {
175  std::cout << "We should have gotten 10000 for this value." << std::endl;
176  ++err;
177  }
178 
179  return err;
180 }
181 
182 // an example of aggregate mode using vector type
183 int test_aggregate(Teuchos::RCP<const Teuchos::Comm<int> > comm) {
184 
185  int err = 0;
186 
187  // define a few constants/helpers
188  typedef int test_lid_t; // lid will be ignored in this case
189  typedef long test_gid_t;
190  typedef std::vector<long long> user_t; // now user is a vector of long long
191 
192  // set up the directory type - this is the vector user mode
193  // the way things are setup right now a std::vector user_t must use this
194  // class - maybe eventually this could all be handling by templating but
195  // I think this might be cleaner and more performant
197  directory_t;
198 
199  // create the directory
200  directory_t directory(comm, false, 0);
201 
202  // now create some gids to write based on the rank
203  std::vector<test_gid_t> writeGIDs;
204  switch(comm->getRank()) {
205  case 0:
206  writeGIDs = { 3, 5 }; // rank 0 writes only 3 and 5
207  break;
208  case 1:
209  writeGIDs = { 3, 7 }; // rank 1 writes only 3 and 7
210  break;
211  default:
212  writeGIDs = { 5 }; // all other ranks write only 5
213  break;
214  }
215 
216  // now create some user values associated with above gids
217  // in this case we can make the values different lengths
218  std::vector<user_t> writeUser;
219  switch(comm->getRank()) {
220  case 0:
221  writeUser = { {1,2}, {4,8,10} }; // rank 0 writes only 3 and 5
222  break;
223  case 1:
224  writeUser = { {2,10}, {6,8} }; // rank 1 writes only 3 and 7
225  break;
226  default:
227  writeUser = { {1,2,10} }; // all other ranks write only 5
228  break;
229  }
230 
231  // call update on the directory
232  directory.update(writeGIDs.size(), &writeGIDs[0], NULL, &writeUser[0], NULL,
234 
235  // now pick some gids to find - could make this rank specific
236  std::vector<test_gid_t> findIds = { 3, 5 };
237 
238  // now create a user space to accept the values
239  // Setting this empty will turn off this option. The original directory uses
240  // ptrs and null which has some advantages so this API choice might need some
241  // better setup. Note that findUser will be a set of empty std::vector
242  // in this case and each will be individually filled by the directory with
243  // the correct aggregated result
244  std::vector<user_t> findUser(findIds.size());
245 
246  // now call find which will fill findUser
247  directory.find(findIds.size(), &findIds[0], NULL, &findUser[0], NULL, NULL);
248 
249  // now check element 0 in findUser
250  // that was for gid = 3 so from the above we expect the following:
251  // rank 0 provided user value {1,2}
252  // rank 1 provided user value {2,10}
253  // all other ranks provided nothing for gid 3
254  // Therefore the aggregated result should be {1,2} + {2,10} = {1,2,10}
255  // For a little variation run as MPI 1 proc, then rank 1 will never write
256  // and the expected value is just {1,2}.
257  user_t expectedValue0 =
258  (comm->getSize() == 1) ? user_t({1,2}) : user_t({1,2,10});
259  if(findUser[0] != expectedValue0) {
260  std::cout << "findUser[0] did not match expected." << std::endl;
261  ++err;
262  }
263 
264  // now check element 1 in findUser
265  // that was for gid = 5 so from the above we expect the following:
266  // rank 0 provided user value {4,8,10}
267  // rank 1 provided nothing
268  // all other ranks provided {1,2,10}
269  // Therefore the aggregated result should be {4,8,10} + {1,2,10} = {1,2,4,8,10}
270  // Again for variation can run this with proc count < 2, in which case
271  // the expected resut will be just {4,8}.
272  user_t expectedValue1 =
273  (comm->getSize() <= 2) ? user_t({4,8,10}) : user_t({1,2,4,8,10});
274  if(findUser[1] != expectedValue1) {
275  std::cout << "findUser[1] did not match expected." << std::endl;
276  ++err;
277  }
278 
279  return err;
280 }
281 
282 // an example of using the directory with a gid defined as a struct
283 int test_multiple_gid(Teuchos::RCP<const Teuchos::Comm<int> > comm) {
284 
285  int err = 0;
286 
287  // define a few constants/helpers
288  typedef int test_lid_t; // lid will be ignored in this case
289  typedef gid_struct test_gid_t;
290  typedef int user_t;
291 
292  // set up the directory type - this is the single user mode (not vector user)
294  directory_t;
295 
296  // create the directory
297  directory_t directory(comm, false, 0);
298 
299  // set up some test gids
300  // in this case the entire set of values is the gid
301  std::vector<test_gid_t> writeGIDs;
302  test_gid_t gid1(1, 8, 7, 3);
303  test_gid_t gid2(1, 8, 7, 4); // this is a completely different gid from the prior
304 
305  // let's have rank 0 write gid1 and gid2 while other ranks write gid2 only
306  if(comm->getRank() == 0) {
307  writeGIDs = { gid1, gid2 };
308  }
309  else {
310  writeGIDs = { gid2 };
311  }
312 
313  // now create some user values associated with above gids
314  // since this will be an add test, just write 1 for all of them
315  // we expect gid1 to end up unchanged (only rank 0 writes it)
316  // but we expect gid2 to end up with value equal to comm->getSize()
317  // because all ranks will contribute 1 to the sum
318  std::vector<user_t> writeUser;
319  if(comm->getRank() == 0) {
320  writeUser = { 1, 1 }; // two values because rank 0 write both gid1 and gid2
321  }
322  else {
323  writeUser = { 1 }; // one value because other ranks only write gid2
324  }
325 
326  // call update on the directory using add mode
327  directory.update(writeGIDs.size(), &writeGIDs[0], NULL, &writeUser[0], NULL,
329 
330  // now check them on all ranks - this could be different for different ranks
331  std::vector<test_gid_t> findIds = { gid1, gid2 };
332 
333  // create a user space to accept the values
334  // Setting this empty will turn off this option. The original directory uses
335  // ptrs and null which has some advantages so this API choice might need some
336  // better setup.
337  std::vector<user_t> findUser(findIds.size());
338 
339  // now call find which will fill findUser
340  directory.find(findIds.size(), &findIds[0], NULL, &findUser[0], NULL, NULL);
341 
342  // now check element 0 in the array which should have value 1
343  if(findUser[0] != 1) {
344  std::cout << "We should have gotten 1 for gid1. Only rank 0 wrote to gid1"
345  " so the sum should have been 1." << std::endl;
346  ++err;
347  }
348 
349  // now check element 1 in the array should have value comm->getSize()
350  // that is because each rank contributed 1 and we are in Add mode
351  if(findUser[1] != comm->getSize()) {
352  std::cout << "We should have gotten the proc count " << comm->getSize()
353  << " since every rank wrote to gid2." << std::endl;
354  ++err;
355  }
356 
357  return err;
358 }
359 
360 // an example of using the directory with an lid defined as a struct
361 // this is similar to the prior test but here we will use both
362 // gid and lid as different structs and also do a find with user data
363 // set ignored just to get some more coverage of the possibilities.
364 int test_multiple_lid(Teuchos::RCP<const Teuchos::Comm<int> > comm) {
365 
366  int err = 0;
367 
368  // define a few constants/helpers
369  typedef gid_struct test_gid_t;
370  typedef lid_struct test_lid_t;
371  typedef int user_t;
372 
374  directory_t;
375 
376  // create the directory
377  directory_t directory(comm, true, 0);
378 
379  // set up some test gids
380  // in this case the entire set of values is the gid
381  std::vector<test_gid_t> writeGIDs;
382  test_gid_t gid1(1, 8, 7, 3);
383  test_gid_t gid2(1, 8, 7, 4); // this is a completely different gid from the prior
384 
385  // set up some test lids
386  // in this case the entire set of values is the lid
387  std::vector<test_lid_t> writeLIDs;
388  test_lid_t lid1(500, 2009);
389  test_lid_t lid2(500, 8000); // this is a completely different lid from the prior
390 
391  // let's have rank 0 write gid1 and gid2 while other ranks write gid2 only
392  if(comm->getRank() == 0) {
393  writeGIDs = { gid1, gid2 };
394  writeLIDs = { lid1, lid2 };
395  }
396  else {
397  writeGIDs = { gid2 };
398  writeLIDs = { lid2 };
399  }
400 
401  std::vector<user_t> writeUser;
402  if(comm->getRank() == 0) {
403  writeUser = { 1, 1 }; // two values because rank 0 write both gid1 and gid2
404  }
405  else {
406  writeUser = { 1 }; // one value because other ranks only write gid2
407  }
408 
409  // call update on the directory using add mode
410  directory.update(writeGIDs.size(), &writeGIDs[0], &writeLIDs[0], &writeUser[0],
411  NULL, directory_t::Replace);
412 
413  // now check them on all ranks - this could be different for different ranks
414  std::vector<test_gid_t> findGIDs = { gid1, gid2 };
415 
416  // create lid space to accept the lid values
417  std::vector<test_lid_t> findLIDs(findGIDs.size());
418 
419  // now call find which will fill findLIDs
420  directory.find(findGIDs.size(), &findGIDs[0], &findLIDs[0], NULL, NULL, NULL);
421 
422  // now check element 0 in the array is matched to lid1
423  if(findLIDs[0].val[0] != lid1.val[0] || findLIDs[0].val[1] != lid1.val[1]) {
424  std::cout << "We should have gotten [500,2009] for lid1." << std::endl;
425  ++err;
426  }
427 
428  // now check element 1 in the array is matched to lid2
429  if(findLIDs[1].val[0] != lid2.val[0] || findLIDs[1].val[1] != lid2.val[1]) {
430  std::cout << "We should have gotten [500,8000] for lid1." << std::endl;
431  ++err;
432  }
433 
434  // create user space to accept the user values
435  std::vector<user_t> findUser(findGIDs.size());
436 
437  // now call find which will fill findLIDs and findUser
438  directory.find(findGIDs.size(), &findGIDs[0], &findLIDs[0], &findUser[0],
439  NULL, NULL);
440 
441  // now check element 0 in the array which should have value 1
442  if(findUser[0] != 1) {
443  std::cout << "We should have gotten 1 for gid1. Only rank 0 wrote to gid1"
444  " so the sum should have been 1." << std::endl;
445  ++err;
446  }
447 
448  // now check element 1 in the array should have value comm->getSize()
449  // that is because each rank contributed 1 and we are in Add mode
450  if(findUser[1] != 1) {
451  std::cout << "We should have gotten the proc count " << comm->getSize()
452  << " since every rank wrote to gid2." << std::endl;
453  ++err;
454  }
455 
456  return err;
457 }
458 
459 int main(int narg, char **arg) {
460  Tpetra::ScopeGuard tscope(&narg, &arg);
461  Teuchos::RCP<const Teuchos::Comm<int> > comm =
462  Teuchos::DefaultComm<int>::getComm();
463 
464  // will reduce err on all ranks
465  int err = 0;
466 
467  // run some tests
468  err += test_simple_replace(comm);
469  err += test_aggregate(comm);
470  err += test_multiple_gid(comm);
471  err += test_multiple_lid(comm);
472 
473  // Get the global err results so we fail properly if any proc failed
474  int errGlobal;
475  Teuchos::reduceAll<int>(*comm,Teuchos::REDUCE_SUM, err,
476  Teuchos::outArg(errGlobal));
477 
478  // this proc is ok
479  comm->barrier();
480  if(comm->getRank() == 0) {
481  if(errGlobal == 0) {
482  std::cout << "Passed" << std::endl;
483  }
484  else {
485  std::cout << "FAILED!" << std::endl;
486  }
487  }
488 
489  return errGlobal;
490 }
int test_multiple_lid(Teuchos::RCP< const Teuchos::Comm< int > > comm)
int main(int narg, char **arg)
Definition: coloring1.cpp:164
lid_struct(unsigned long v0, unsigned long v1)
int test_multiple_gid(Teuchos::RCP< const Teuchos::Comm< int > > comm)
gid_struct(int v0, int v1, int v2, int v3)
int test_simple_replace(Teuchos::RCP< const Teuchos::Comm< int > > comm)
unsigned long val[2]
int test_aggregate(Teuchos::RCP< const Teuchos::Comm< int > > comm)
Zoltan2::BasicUserTypes< zscalar_t, zlno_t, zgno_t > user_t
Definition: Metric.cpp:39