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 // ***********************************************************************
4 //
5 // Zoltan2: A package of combinatorial algorithms for scientific computing
6 // Copyright 2012 Sandia Corporation
7 //
8 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Redistribution and use in source and binary forms, with or without
12 // modification, are permitted provided that the following conditions are
13 // met:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 //
18 // 2. Redistributions in binary form must reproduce the above copyright
19 // notice, this list of conditions and the following disclaimer in the
20 // documentation and/or other materials provided with the distribution.
21 //
22 // 3. Neither the name of the Corporation nor the names of the
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
38 // Questions? Contact Karen Devine (kddevin@sandia.gov)
39 // Erik Boman (egboman@sandia.gov)
40 // Siva Rajamanickam (srajama@sandia.gov)
41 //
42 // ***********************************************************************
43 //
44 // @HEADER
45 
47 #include "Tpetra_Core.hpp"
48 
49 // This type will be used by some of these tests
50 class gid_struct {
51  public:
52  // Kokkos will need an empty constructor so either provide one or
53  // make a simple struct with no constructors will also work
55  gid_struct(int v0, int v1, int v2, int v3) {
56  val[0] = v0;
57  val[1] = v1;
58  val[2] = v2;
59  val[3] = v3;
60  }
61  int val[4]; // can be any length but this can't have mem alloc (like std::vector)
62 };
63 
64 // same as gid but for better coverage of testing, make it different
65 class lid_struct {
66  public:
67  // Kokkos will need an empty constructor so either provide one or
68  // make a simple struct with no constructors will also work
70  lid_struct(unsigned long v0, unsigned long v1) {
71  val[0] = v0;
72  val[1] = v1;
73  }
74  unsigned long val[2]; // can be any length but this can't have mem alloc (like std::vector)
75 };
76 
77 // a very simple test of replace
78 // after calling update and find we do a second round with new gids
79 // to make sure repeated update calls will work.
80 int test_simple_replace(Teuchos::RCP<const Teuchos::Comm<int> > comm) {
81 
82  int err = 0;
83 
84  // define a few constants/helpers
85  typedef int test_lid_t; // lid will be ignored in this case
86  typedef int test_gid_t;
87  typedef long user_t;
88 
89  // set up the directory type - this is the single user mode (not vector user)
91 
92  // create the directory
93  directory_t directory(comm, false, 0);
94 
95  // now create some gids to write
96  // to keep things interesting we'll have just proc 0 write 4 Ids and all the
97  // other procs right the same gid (3).
98  std::vector<test_gid_t> writeGIDs;
99 
100  if(comm->getRank() == 0) {
101  writeGIDs = { 1, 5, 7, 10 };
102  }
103  else {
104  writeGIDs = { 3 };
105  }
106 
107  // now create some user values associated with above gids
108  // for easy reference just make them gid x 10
109  std::vector<user_t> writeUser;
110  if(comm->getRank() == 0) {
111  writeUser = { 10, 50, 70, 100 };
112  }
113  else {
114  writeUser = { 30 };
115  }
116 
117  // call update on the directory
118  directory.update(writeGIDs.size(), &writeGIDs[0], NULL, &writeUser[0], NULL,
120 
121  // now pick some gids to find
122  std::vector<test_gid_t> findIds = { 3, 5 };
123 
124  // now create a user space to accept the values
125  // Setting this empty will turn off this option. The original directory uses
126  // ptrs and null which has some advantages so this API choice might need some
127  // better setup.
128  std::vector<user_t> findUser(findIds.size());
129 
130  // now call find which will fill findUser
131  // for special case of mpi proc 1 the find of gid 3 will give a throw
132  // because rank 1 never existed to write it. This test is currently setup
133  // so it will pass for any mpi count so for this special case we turn this
134  // into a test to verify the throw happened. If mpi count is not 1, then
135  // a throw will not happen (or if it does it's a real error).
136  // However maybe it's better to change this so the simple test example
137  // doesn't have this complication ... to do.
138  try {
139  directory.find(findIds.size(), &findIds[0], NULL, &findUser[0], NULL, NULL);
140 
141  // now check element 0 in the array - make sure it matches writeUser above
142  // all procs except 0 sent this one
143  if(findUser[0] != 30) {
144  std::cout << "We should have gotten 30 for this value." << std::endl;
145  ++err;
146  }
147 
148  // only proc 0 sent the second one - make sure all procs can read it
149  if(findUser[1] != 50) {
150  std::cout << "We should have gotten 50 for this value." << std::endl;
151  ++err;
152  }
153  }
154  catch(std::logic_error &e) {
155  if(comm->getSize() != 1) {
156  err = 1;
157  }
158  else {
159  std::cout << "Successfully detected throw since find failed." << std::endl;
160  }
161  }
162 
163  // Phase 2
164  // now let's test further by updating with some more values
165  // we'll try a mix of preexisting values and new values
166  // this adds some unit testing coverage for the case when update is called
167  // twice on the same directory, not implemented in the general unit tests.
168  std::vector<test_gid_t> writeGIDs2;
169 
170  if(comm->getRank() == 0) {
171  writeGIDs2 = { 5, 700, 1000 };
172  }
173  else {
174  writeGIDs2 = { 3, 200 };
175  }
176 
177  std::vector<user_t> writeUser2;
178  if(comm->getRank() == 0) {
179  writeUser2 = { 50, 7000, 10000 };
180  }
181  else {
182  writeUser2 = { 30, 2000 };
183  }
184 
185  // call update on the directory (this will be the second round)
186  directory.update(writeGIDs2.size(), &writeGIDs2[0], NULL, &writeUser2[0], NULL,
188 
189  // now pick some gids to find
190  std::vector<test_gid_t> findIds2 = { 1, 5, 1000 };
191 
192  // now create a user space to accept the values
193  std::vector<user_t> findUser2(findIds2.size());
194  directory.find(findIds2.size(), &findIds2[0], NULL, &findUser2[0], NULL, NULL);
195 
196  // validate the results
197  // index 0 (gid 1) was updated on the first round (not second)
198  // index 1 (gid 5) was updated on both rounds
199  // index 2 (gid 1000) was updated on the second round (not first)
200  if(findUser2[0] != 10) {
201  std::cout << "We should have gotten 10 for this value. " << std::endl;
202  ++err;
203  }
204  if(findUser2[1] != 50) {
205  std::cout << "We should have gotten 50 for this value." << std::endl;
206  ++err;
207  }
208 
209  // only proc 0 sent the second one - make sure all procs can read it
210  if(findUser2[2] != 10000) {
211  std::cout << "We should have gotten 10000 for this value." << std::endl;
212  ++err;
213  }
214 
215  return err;
216 }
217 
218 // an example of aggregate mode using vector type
219 int test_aggregate(Teuchos::RCP<const Teuchos::Comm<int> > comm) {
220 
221  int err = 0;
222 
223  // define a few constants/helpers
224  typedef int test_lid_t; // lid will be ignored in this case
225  typedef long test_gid_t;
226  typedef std::vector<long long> user_t; // now user is a vector of long long
227 
228  // set up the directory type - this is the vector user mode
229  // the way things are setup right now a std::vector user_t must use this
230  // class - maybe eventually this could all be handling by templating but
231  // I think this might be cleaner and more performant
233  directory_t;
234 
235  // create the directory
236  directory_t directory(comm, false, 0);
237 
238  // now create some gids to write based on the rank
239  std::vector<test_gid_t> writeGIDs;
240  switch(comm->getRank()) {
241  case 0:
242  writeGIDs = { 3, 5 }; // rank 0 writes only 3 and 5
243  break;
244  case 1:
245  writeGIDs = { 3, 7 }; // rank 1 writes only 3 and 7
246  break;
247  default:
248  writeGIDs = { 5 }; // all other ranks write only 5
249  break;
250  }
251 
252  // now create some user values associated with above gids
253  // in this case we can make the values different lengths
254  std::vector<user_t> writeUser;
255  switch(comm->getRank()) {
256  case 0:
257  writeUser = { {1,2}, {4,8,10} }; // rank 0 writes only 3 and 5
258  break;
259  case 1:
260  writeUser = { {2,10}, {6,8} }; // rank 1 writes only 3 and 7
261  break;
262  default:
263  writeUser = { {1,2,10} }; // all other ranks write only 5
264  break;
265  }
266 
267  // call update on the directory
268  directory.update(writeGIDs.size(), &writeGIDs[0], NULL, &writeUser[0], NULL,
270 
271  // now pick some gids to find - could make this rank specific
272  std::vector<test_gid_t> findIds = { 3, 5 };
273 
274  // now create a user space to accept the values
275  // Setting this empty will turn off this option. The original directory uses
276  // ptrs and null which has some advantages so this API choice might need some
277  // better setup. Note that findUser will be a set of empty std::vector
278  // in this case and each will be individually filled by the directory with
279  // the correct aggregated result
280  std::vector<user_t> findUser(findIds.size());
281 
282  // now call find which will fill findUser
283  directory.find(findIds.size(), &findIds[0], NULL, &findUser[0], NULL, NULL);
284 
285  // now check element 0 in findUser
286  // that was for gid = 3 so from the above we expect the following:
287  // rank 0 provided user value {1,2}
288  // rank 1 provided user value {2,10}
289  // all other ranks provided nothing for gid 3
290  // Therefore the aggregated result should be {1,2} + {2,10} = {1,2,10}
291  // For a little variation run as MPI 1 proc, then rank 1 will never write
292  // and the expected value is just {1,2}.
293  user_t expectedValue0 =
294  (comm->getSize() == 1) ? user_t({1,2}) : user_t({1,2,10});
295  if(findUser[0] != expectedValue0) {
296  std::cout << "findUser[0] did not match expected." << std::endl;
297  ++err;
298  }
299 
300  // now check element 1 in findUser
301  // that was for gid = 5 so from the above we expect the following:
302  // rank 0 provided user value {4,8,10}
303  // rank 1 provided nothing
304  // all other ranks provided {1,2,10}
305  // Therefore the aggregated result should be {4,8,10} + {1,2,10} = {1,2,4,8,10}
306  // Again for variation can run this with proc count < 2, in which case
307  // the expected resut will be just {4,8}.
308  user_t expectedValue1 =
309  (comm->getSize() <= 2) ? user_t({4,8,10}) : user_t({1,2,4,8,10});
310  if(findUser[1] != expectedValue1) {
311  std::cout << "findUser[1] did not match expected." << std::endl;
312  ++err;
313  }
314 
315  return err;
316 }
317 
318 // an example of using the directory with a gid defined as a struct
319 int test_multiple_gid(Teuchos::RCP<const Teuchos::Comm<int> > comm) {
320 
321  int err = 0;
322 
323  // define a few constants/helpers
324  typedef int test_lid_t; // lid will be ignored in this case
325  typedef gid_struct test_gid_t;
326  typedef int user_t;
327 
328  // set up the directory type - this is the single user mode (not vector user)
330  directory_t;
331 
332  // create the directory
333  directory_t directory(comm, false, 0);
334 
335  // set up some test gids
336  // in this case the entire set of values is the gid
337  std::vector<test_gid_t> writeGIDs;
338  test_gid_t gid1(1, 8, 7, 3);
339  test_gid_t gid2(1, 8, 7, 4); // this is a completely different gid from the prior
340 
341  // let's have rank 0 write gid1 and gid2 while other ranks write gid2 only
342  if(comm->getRank() == 0) {
343  writeGIDs = { gid1, gid2 };
344  }
345  else {
346  writeGIDs = { gid2 };
347  }
348 
349  // now create some user values associated with above gids
350  // since this will be an add test, just write 1 for all of them
351  // we expect gid1 to end up unchanged (only rank 0 writes it)
352  // but we expect gid2 to end up with value equal to comm->getSize()
353  // because all ranks will contribute 1 to the sum
354  std::vector<user_t> writeUser;
355  if(comm->getRank() == 0) {
356  writeUser = { 1, 1 }; // two values because rank 0 write both gid1 and gid2
357  }
358  else {
359  writeUser = { 1 }; // one value because other ranks only write gid2
360  }
361 
362  // call update on the directory using add mode
363  directory.update(writeGIDs.size(), &writeGIDs[0], NULL, &writeUser[0], NULL,
365 
366  // now check them on all ranks - this could be different for different ranks
367  std::vector<test_gid_t> findIds = { gid1, gid2 };
368 
369  // create a user space to accept the values
370  // Setting this empty will turn off this option. The original directory uses
371  // ptrs and null which has some advantages so this API choice might need some
372  // better setup.
373  std::vector<user_t> findUser(findIds.size());
374 
375  // now call find which will fill findUser
376  directory.find(findIds.size(), &findIds[0], NULL, &findUser[0], NULL, NULL);
377 
378  // now check element 0 in the array which should have value 1
379  if(findUser[0] != 1) {
380  std::cout << "We should have gotten 1 for gid1. Only rank 0 wrote to gid1"
381  " so the sum should have been 1." << std::endl;
382  ++err;
383  }
384 
385  // now check element 1 in the array should have value comm->getSize()
386  // that is because each rank contributed 1 and we are in Add mode
387  if(findUser[1] != comm->getSize()) {
388  std::cout << "We should have gotten the proc count " << comm->getSize()
389  << " since every rank wrote to gid2." << std::endl;
390  ++err;
391  }
392 
393  return err;
394 }
395 
396 // an example of using the directory with an lid defined as a struct
397 // this is similar to the prior test but here we will use both
398 // gid and lid as different structs and also do a find with user data
399 // set ignored just to get some more coverage of the possibilities.
400 int test_multiple_lid(Teuchos::RCP<const Teuchos::Comm<int> > comm) {
401 
402  int err = 0;
403 
404  // define a few constants/helpers
405  typedef gid_struct test_gid_t;
406  typedef lid_struct test_lid_t;
407  typedef int user_t;
408 
410  directory_t;
411 
412  // create the directory
413  directory_t directory(comm, true, 0);
414 
415  // set up some test gids
416  // in this case the entire set of values is the gid
417  std::vector<test_gid_t> writeGIDs;
418  test_gid_t gid1(1, 8, 7, 3);
419  test_gid_t gid2(1, 8, 7, 4); // this is a completely different gid from the prior
420 
421  // set up some test lids
422  // in this case the entire set of values is the lid
423  std::vector<test_lid_t> writeLIDs;
424  test_lid_t lid1(500, 2009);
425  test_lid_t lid2(500, 8000); // this is a completely different lid from the prior
426 
427  // let's have rank 0 write gid1 and gid2 while other ranks write gid2 only
428  if(comm->getRank() == 0) {
429  writeGIDs = { gid1, gid2 };
430  writeLIDs = { lid1, lid2 };
431  }
432  else {
433  writeGIDs = { gid2 };
434  writeLIDs = { lid2 };
435  }
436 
437  std::vector<user_t> writeUser;
438  if(comm->getRank() == 0) {
439  writeUser = { 1, 1 }; // two values because rank 0 write both gid1 and gid2
440  }
441  else {
442  writeUser = { 1 }; // one value because other ranks only write gid2
443  }
444 
445  // call update on the directory using add mode
446  directory.update(writeGIDs.size(), &writeGIDs[0], &writeLIDs[0], &writeUser[0],
447  NULL, directory_t::Replace);
448 
449  // now check them on all ranks - this could be different for different ranks
450  std::vector<test_gid_t> findGIDs = { gid1, gid2 };
451 
452  // create lid space to accept the lid values
453  std::vector<test_lid_t> findLIDs(findGIDs.size());
454 
455  // now call find which will fill findLIDs
456  directory.find(findGIDs.size(), &findGIDs[0], &findLIDs[0], NULL, NULL, NULL);
457 
458  // now check element 0 in the array is matched to lid1
459  if(findLIDs[0].val[0] != lid1.val[0] || findLIDs[0].val[1] != lid1.val[1]) {
460  std::cout << "We should have gotten [500,2009] for lid1." << std::endl;
461  ++err;
462  }
463 
464  // now check element 1 in the array is matched to lid2
465  if(findLIDs[1].val[0] != lid2.val[0] || findLIDs[1].val[1] != lid2.val[1]) {
466  std::cout << "We should have gotten [500,8000] for lid1." << std::endl;
467  ++err;
468  }
469 
470  // create user space to accept the user values
471  std::vector<user_t> findUser(findGIDs.size());
472 
473  // now call find which will fill findLIDs and findUser
474  directory.find(findGIDs.size(), &findGIDs[0], &findLIDs[0], &findUser[0],
475  NULL, NULL);
476 
477  // now check element 0 in the array which should have value 1
478  if(findUser[0] != 1) {
479  std::cout << "We should have gotten 1 for gid1. Only rank 0 wrote to gid1"
480  " so the sum should have been 1." << std::endl;
481  ++err;
482  }
483 
484  // now check element 1 in the array should have value comm->getSize()
485  // that is because each rank contributed 1 and we are in Add mode
486  if(findUser[1] != 1) {
487  std::cout << "We should have gotten the proc count " << comm->getSize()
488  << " since every rank wrote to gid2." << std::endl;
489  ++err;
490  }
491 
492  return err;
493 }
494 
495 int main(int narg, char **arg) {
496  Tpetra::ScopeGuard tscope(&narg, &arg);
497  Teuchos::RCP<const Teuchos::Comm<int> > comm =
498  Teuchos::DefaultComm<int>::getComm();
499 
500  // will reduce err on all ranks
501  int err = 0;
502 
503  // run some tests
504  err += test_simple_replace(comm);
505  err += test_aggregate(comm);
506  err += test_multiple_gid(comm);
507  err += test_multiple_lid(comm);
508 
509  // Get the global err results so we fail properly if any proc failed
510  int errGlobal;
511  Teuchos::reduceAll<int>(*comm,Teuchos::REDUCE_SUM, err,
512  Teuchos::outArg(errGlobal));
513 
514  // this proc is ok
515  comm->barrier();
516  if(comm->getRank() == 0) {
517  if(errGlobal == 0) {
518  std::cout << "Passed" << std::endl;
519  }
520  else {
521  std::cout << "FAILED!" << std::endl;
522  }
523  }
524 
525  return errGlobal;
526 }
int test_multiple_lid(Teuchos::RCP< const Teuchos::Comm< int > > comm)
int main(int narg, char **arg)
Definition: coloring1.cpp:199
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:74