Zoltan2
Loading...
Searching...
No Matches
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
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
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.
44int 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,
83 directory_t::Replace);
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,
151 directory_t::Replace);
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
183int 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,
233 directory_t::Aggregate);
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
283int 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,
328 directory_t::Add);
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.
364int 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
459int 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}
Zoltan2::BasicUserTypes< zscalar_t, zlno_t, zgno_t > user_t
Definition Metric.cpp:39
int main()
gid_struct(int v0, int v1, int v2, int v3)
lid_struct(unsigned long v0, unsigned long v1)
int test_aggregate(Teuchos::RCP< const Teuchos::Comm< int > > comm)
int test_multiple_gid(Teuchos::RCP< const Teuchos::Comm< int > > comm)
int test_multiple_lid(Teuchos::RCP< const Teuchos::Comm< int > > comm)
int test_simple_replace(Teuchos::RCP< const Teuchos::Comm< int > > comm)