Zoltan2
Loading...
Searching...
No Matches
directoryTest_Impl.hpp
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
46#include <Teuchos_RCP.hpp>
47#include <Teuchos_ArrayView.hpp>
48#include <Zoltan2_TPLTraits.hpp>
50
51namespace Zoltan2 {
52
53// The directory also has modes but currently working with an Original mode
54// as well, which doesn't have it - so created this to have a universal
55// reference to use. Eventually will delete all but Kokkos and then eliminate
56// this declaration.
61 TestMode_Max // exists to provide a loop through these modes
62};
63
64// Do gid with multiple sub gids
65// Note that the testing code (things in this file) needs knowledge
66// about this structure so it's a hard coded assumption but the directory
67// class itself does not - that is why there are sub_gid references in the
68// below classes but no special handling for this in the directory.
69// as long as sizeof works and it can be returned as a function
70// type it should be ok to use as a gid_t.
71#define GID_SET_LENGTH 3 // arbitrary for testing
72struct gid_set_t {
74};
75
76// same as gid above but this is for lid
77#define LID_SET_LENGTH 4 // just to mix things up - make it different than gid
78struct lid_set_t {
80};
81
82// a utility function to print messages out for each rank or optionally
83// just for rank 0. Used to prevent mangled output.
84void print_proc_safe(const std::string& message,
85 Teuchos::RCP<const Teuchos::Comm<int> > comm, bool only_rank0 = true) {
86 for(int proc = 0; proc < comm->getSize(); ++proc) {
87 comm->barrier();
88 if(proc == comm->getRank() && (!only_rank0 || proc == 0)) {
89 std::cout << message << std::endl;
90 }
91 }
92 comm->barrier();
93}
94
95// For the new code we use -1 as the 'unset' value to make it easier
96// to see it worked but it's arbitrary. TODO: Discuss error behaviors for
97// remove when not found.
98#define NOT_FOUND_VALUE -1 // easier to tell it's the unset value as -1
99
100// This class manages the IDs that we will update to the directory and which
101// ids to remove and find. The class also handles error checking at the end
102// to validate the find got the proper user data we expected.
103//
104// Derived classes handle vector user_t versus single user_t and
105// also multiple gids (gid = [1,2,8,3]) or single gid (gid = 10).
106// All this machinery is just for testing/evaluation and nothing really to
107// do with the directory class itself.
108
109// Because this testing code started to get complex due covering all possible
110// behaviors of the directory, another test was created called
111// directoryTest_KokkosSimple. That doesn't reference any of this stuff and is
112// a more natural example of the directory being used (but doesn't thoroughly
113// test the directory as is done here.
114//
115// The class structure is this:
116// IDs
117// Single_GID - code for gid is simple type
118// Multiple_GID - code for gid such as gid = [1,2,8,3]
119// Single_User - code for when user is simple single type
120// Vector_User - code for when user_t is a std::vector
121//
122// Then to run tests we use multiple inheritance of above classes.
123// Single_User_Single_GID - inherits Single_GID + Single_User
124// Single_User_Multiple_GID - inherits Multiple_GID + Single_User
125// Vector_User_Single_GID - inherits Single_GID + Vector_User
126// Vector_User_Multiple_GID - inherits Multiple_GID + Vector_User
127template <typename gid_t,typename lid_t,typename user_t>
128class IDs {
129 public:
130 // gcc requires this (not clang) - however it will not be used
131 // I think clang is handling this properly based on below public virtual
132 // inheritance but maybe will change this if the different compilers
133 // will handle it in non-uniform ways
134 IDs() {}
135
147 IDs(size_t totalIds_, size_t idBase_, size_t idStride_,
148 Teuchos::RCP<const Teuchos::Comm<int> > &comm_, int mode_,
149 const std::string& test_name_, bool print_detailed_output_,
150 bool performance_test_, bool bUseLocalIDs_) :
151 totalIds(totalIds_), idBase(idBase_),
152 idStride(idStride_), comm(comm_), maxPrintSize(10), mode(mode_),
153 did_setup(false), test_name(test_name_),
154 print_detailed_output(print_detailed_output_),
155 performance_test(performance_test_), bUseLocalIDs(bUseLocalIDs_) {
156 }
157
158 // prints all the subsets used as utlity to create interesting overlaps
159 // prints all the update/remove/find decisions
161 // map out the behaviors of the subsets
162 comm->barrier();
163 if(comm->getRank() == 0) {
164 // some arbitrary sub sets used for testing coverage
165 std::cout << "Sub1: " << std::endl;
166 for(int proc = 0; proc < comm->getSize(); ++proc) {
167 std::cout << " rank " << proc << ": ";
168 for(size_t n = 0; n < totalIds; ++n) {
169 std::cout << subset1(n, proc);
170 }
171 std::cout << std::endl;
172 }
173
174 // some arbitrary sub sets using for testing coverage
175 std::cout << "Sub2: " << std::endl;
176 for(int proc = 0; proc < comm->getSize(); ++proc) {
177 std::cout << " rank " << proc << ": ";
178 for(size_t n = 0; n < totalIds; ++n) {
179 std::cout << subset2(n, proc);
180 }
181 std::cout << std::endl;
182 }
183
184 // update
185 std::cout << "update: " << std::endl;
186 for(int proc = 0; proc < comm->getSize(); ++proc) {
187 std::cout << " rank " << proc << ": ";
188 for(size_t n = 0; n < totalIds; ++n) {
189 std::cout << proc_update_gid(convert_index_to_gid(n), proc);
190 }
191 std::cout << std::endl;
192 }
193
194 // remove
195 std::cout << "remove: " << std::endl;
196 for(int proc = 0; proc < comm->getSize(); ++proc) {
197 std::cout << " rank " << proc << ": ";
198 for(size_t n = 0; n < totalIds; ++n) {
199 std::cout << proc_remove_gid(convert_index_to_gid(n), proc);
200 }
201 std::cout << std::endl;
202 }
203
204 // find
205 std::cout << "find: " << std::endl;
206 for(int proc = 0; proc < comm->getSize(); ++proc) {
207 std::cout << " rank " << proc << ": ";
208 for(size_t n = 0; n < totalIds; ++n) {
209 std::cout << proc_find_gid(convert_index_to_gid(n), proc);
210 }
211 std::cout << std::endl;
212 }
213 }
214 comm->barrier();
215 }
216
217 void printResultMessage(bool pass) const {
218 std::string base_message = get_test_name() + " mode: " + get_mode_name();
219 base_message = get_test_style() + " " + base_message;
220 std::string message = pass ?
221 " Passed: " + base_message :
222 " FAILED: " + base_message + " on rank " +
223 std::to_string(comm->getRank());
224 print_proc_safe(message, comm, pass); // only print passes on rank 0
225 }
226
227 const std::string & get_test_name() const { return test_name; }
228
231 virtual bool evaluateTests() const = 0;
232
235 int getMode() const { return mode; }
236
239 bool did_test_pass() const { return passed; }
240
246 virtual std::string get_test_style() const = 0;
247
250 void print() const
251 {
252 for(int proc = 0; proc < comm->getSize(); ++proc) {
253 comm->barrier();
254 if(proc == comm->getRank()) {
255 if(proc == 0) {
256 std::cout << std::endl <<
257 "############ Output: " << get_test_name()
258 << " ############" << std::endl;
259 }
260 std::cout << "Rank: " << proc << std::endl;
261 print_gids(update_gids, "Update gids" );
262 if(bUseLocalIDs) {
263 print_lids(update_lids, "Update lids" );
264 }
265 print_gids(remove_gids, "Remove gids" );
266 print_gids(find_gids, "Find gids" );
267 if(bUseLocalIDs) {
268 print_lids(find_lids, "Find lids" );
269 }
270 print_user_data(); // specific to single user or vector user type
271 if(bUseLocalIDs) {
272 print_lid_data(); // the lids we found, if bUseLocalIDs = true
273 }
274
275 if(proc == comm->getSize()-1) {
276 std::cout << std::endl;
277 }
278 }
279 }
280 comm->barrier();
281 }
282
283 protected:
284 std::string get_mode_name() const {
285 switch(mode) {
286 case TestMode::Add: return "Add"; break;
287 case TestMode::Replace: return "Replace"; break;
288 case TestMode::Aggregate: return "Aggregate"; break;
289 default: throw std::logic_error("Bad mode."); break;
290 }
291 }
292
293 // this is guaranteed to be true for at least one of the ranks
294 bool trueForAtLeastOneProc(int index, int rank) const {
295 return ((index % comm->getSize()) == rank);
296 }
297
298 // some arbitrary subset
299 bool subset1(int index, int rank) const {
300 return ((index % (comm->getSize()+1)) == rank);
301 }
302
303 // some arbitrary subset
304 bool subset2(int index, int rank) const {
305 return ((index % (comm->getSize()+3)) == rank);
306 }
307
308 // given a gid convert it to an index 0, 1, 2, ...
309 // this removes stride and then we use index to determine placement
310 // in some arbitrary subsets to make a mix of update, remove, and find gids
311 // it's abstract because multiple gid structs will be generated differently
312 virtual int convert_gid_to_index(const gid_t& gid) const = 0;
313
314 // reverse operation will bring an index back to a gid
315 // it's abstract because multiple gid structs will be generated differently
316 virtual gid_t convert_index_to_gid(int index) const = 0;
317
318 // decide if this rank will update this gid (and how many duplicates to send)
319 int proc_update_gid(gid_t gid, int rank) const {
320 // this method should be guaranteed to return true for 1 of the procs
321 // it can optionally return true for more than one to make a nice mix
322 // the return is the number of repeats as we want the directory to work
323 // in such a case
324 int index = convert_gid_to_index(gid); // back to 0,1,2,... indexing
325
326 // note not allowing duplicate gid for update - that will throw an error
327 // however we do allow duplicate find gid where each shoudl get the same
328 // value.
329 if(trueForAtLeastOneProc(index, rank) || subset1(index, rank)) {
330 return 1;
331 }
332 else {
333 return 0; // do not update this ID
334 }
335 }
336
337 // decide if this rank will find this gid (and how many duplicates to send)
338 int proc_find_gid(gid_t gid, int rank) const {
339 // this method can be anything - things should work even if this is empty;
340 int index = convert_gid_to_index(gid); // convert back to 0,1,2,...indexing
341
342 // for a few gid's duplicate - I keep this a small percentage because
343 // I want performance testing to be based on mostly unique ids.
344 int test_duplicate_count = 2;
345 if(subset1(index, rank)) {
346 return (index < 100) ? test_duplicate_count : 1;
347 }
348 else {
349 return 0; // do not read this ID
350 }
351 }
352
353 // decide if this rank will remove this gid (and how many duplicates to send)
354 int proc_remove_gid(gid_t gid, int rank) const {
355 // this should include some proc_find_gid values but not all, but also other
356 // values for good testing - we want to remove more than we wrote locally
357 // and check for proper removal
358 int index = convert_gid_to_index(gid); // convert back to 0,1,2,...indexing
359 int test_duplicate_count = 2;
360 if(subset2(index, rank)) {
361 return (index < 100) ? test_duplicate_count : 1;
362 }
363 else {
364 return 0; // do not remove this ID
365 }
366 }
367
368 // a special case for testing - we set the user data to a 'not found' state
369 // so we can confirm it was untouched if we call remove and then find on
370 // a gid which no longer exists.
371 virtual user_t get_not_found_user() const = 0;
372
373 // same as for above gid - we want to have the lid set to a known state
374 // ahead of time.
375 virtual lid_t get_not_found_lid() const = 0;
376
377 // fill the find_user with all the initial states
378 // this is only relevant for testing - not something a normal user will do
380 find_user = std::vector<user_t>(find_gids.size(), get_not_found_user());
381 }
382
383 // fill the find_lids with all the initial states
384 // this is only relevant for testing - not something a normal user will do
386 find_lids = std::vector<lid_t>(find_gids.size(), get_not_found_lid());
387 }
388
389 // a global check for the testing framework - did any proc remove this gid?
390 bool removedIDGlobally(gid_t gid) const {
391 for(int proc = 0; proc < comm->getSize(); ++proc) {
392 if(proc_remove_gid(gid, proc)) {
393 return true;
394 }
395 }
396 return false;
397 }
398
399 // a check for the testing framework - count how many times this gid was
400 // updated (currently we allow duplicate gids in one update call). This will
401 // count each case as +1.
402 int sharedCount(gid_t gid) const {
403 int count = 0;
404 for(int proc = 0; proc < comm->getSize(); ++proc) {
405 count += proc_update_gid(gid, proc);
406 }
407 return count;
408 }
409
410 // utility function to output a list of gids
411 // handles the gid as a struct as well using gid_to_string
412 // Note the testing framework (here) 'knows' how to deciper the structure
413 // of the gid but the directory does not, hence the directory doesn't have
414 // the ability to print out what the struct contents are. We might want to
415 // have some kind of print() method required for these structs to make log
416 // outputs easier. Something like gid_to_string implemented in this file.
417 void print_gids(const std::vector<gid_t> &printIds, std::string name) const {
418 std::cout << " " << printIds.size() << " " << name << ": ";
419 for (size_t i = 0; i < printIds.size() && i < maxPrintSize; i++) {
420 std::cout << gid_to_string(printIds[i]) << " ";
421 }
422 if(printIds.size() > maxPrintSize) {
423 std::cout << "... ";
424 }
425 std::cout << std::endl;
426 }
427
428 // utility function to output a list of gids
429 // handles the lid as a struct as well using lid_to_string
430 void print_lids(const std::vector<lid_t> &printIds, std::string name) const {
431 std::cout << " " << printIds.size() << " " << name << ": ";
432 for (size_t i = 0; i < printIds.size() && i < maxPrintSize; i++) {
433 std::cout << lid_to_string(printIds[i]) << " ";
434 }
435 if(printIds.size() > maxPrintSize) {
436 std::cout << "... ";
437 }
438 std::cout << std::endl;
439 }
440
441 // user data can be simple of std::vector so handled by derived classes
442 virtual void print_user_data() const = 0;
443
444 // prints out the list of lids we found
445 virtual void print_lid_data() const {
446 std::cout << "Find LIDs ";
447 for(size_t n = 0; n < this->find_gids.size(); ++n) {
448 std::cout << this->gid_to_string(this->find_gids[n]) << ":" <<
449 this->lid_to_string(this->find_lids[n]) << " ";
450 }
451 std::cout << std::endl;
452 }
453
454 // to make some fixed conversions between gid and arbitrary user data the
455 // seed value is used. This is so the testing can validate final results are
456 // correct based on gid. For gid as a struct, this just uses the first
457 // element in the gid array.
458 virtual size_t gid_seed_value(const gid_t& gid) const = 0;
459
460 // convert gid to nice output - handled gid as a struct if that is the case
461 virtual std::string gid_to_string(gid_t gid) const = 0;
462
463 // convert lid to nice output - handled gid as a struct if that is the case
464 virtual std::string lid_to_string(lid_t lid) const = 0;
465
466 // determine if the two lids are equal - used by testing to validate result
467 // matches expected result.
468 virtual bool check_lid_equal(const lid_t & a, const lid_t & b) const = 0;
469
470 // get_expected_user is the user data we expect to have returned after a
471 // find command. It would be the same as get_initial_value for Replace mode
472 // but for Add and Aggregate, this is the calculated result we expect the
473 // directory to have performed. So for example two difference procs may both
474 // update in Aggregate mode with a different vector. They would each have
475 // their own get_initial_user, say [1,3,5] for rank 0 and [1,5,7] for rank 1
476 // but that is for the same gid. That is why get_initial_user takes both
477 // gid and rank. However get_expected_user is the grand result which would
478 // be [1,3,5,7] and independent of rank. The tests here check the results
479 // returned by the directory find command and make sure they match the
480 // value calculated by get_expected_user.
481 virtual user_t get_expected_user(gid_t gid) const = 0;
482
483 // this is the user data the specific rank sent for gid
484 // for Add and Aggregate modes different ranks will be sending different
485 // data for the same gid. The final result that the directory is supposed
486 // to generate is calculated as get_expected_user above for verification.
487 virtual user_t get_initial_user(gid_t gid, int rank) const = 0;
488
489 // for a given gid, this determines some arbitrary lid to go with it. This
490 // value is sent to the directory and then can be checked later to be sure
491 // we got the right lid back.
492 virtual lid_t get_initial_lid(gid_t gid) const = 0;
493
494 // execute() is called by the derived class so that inheritance will be
495 // executing for all methods properly. This method handles everything.
496 virtual void execute() {
497 setup(); // get things ready
498 test(); // run the actual directory code
499 analyze(); // analyze the results
500 output(); // print results
501 // debug_print_subsets_and_decisions();
502 }
503
504 // preparation for running the test
505 virtual void setup() {
506 // sanity check - make sure we have our internal logic correct - in case
507 // the class hierarchy gets more complicated this is a reminder to make
508 // sure the execute() method is being called once by the highest level.
509 if(did_setup) {
510 throw std::logic_error(
511 "setup already called - execute once in highest level class only.");
512 }
513 did_setup = true;
514
515 // sanity check on the proc_update_gid
516 // this verifies that at least one proc is actually going to write each
517 // gid which is not really a requirement but more to make sure the tests
518 // are running what we expected.
519 for(size_t n = 0; n < totalIds; ++n) {
520 gid_t gid = convert_index_to_gid(n);
521 bool bAtLeastOne = false;
522 for(int proc = 0; proc < comm->getSize(); ++proc) {
523 if(proc_update_gid(gid, proc)) {
524 bAtLeastOne = true;
525 }
526 }
527 if(!bAtLeastOne) {
528 throw std::logic_error("The proc_update_gid method must generate an"
529 " algorithm which returns true for at least 1 proce for all IDs. If"
530 " this is changed then the findID algorithm should not return true"
531 " for any of those missing IDs. This is a sanity check.");
532 }
533 }
534
535 // now decide which gids we will update, remove, and find
536 // this is somewhat arbitrary and the methods here are use to make some
537 // nice mix up.
538 for(size_t n = 0; n < totalIds; ++n) {
539 // first generate the gid from index (handles stride, base offset)
540 gid_t gid = convert_index_to_gid(n);
541
542 // determine how many of this particular gid we will update
543 int count_proc_update_gid = proc_update_gid(gid, comm->getRank());
544 for(int i = 0; i < count_proc_update_gid; ++i) {
545 update_gids.push_back(gid);
546 update_lids.push_back(get_initial_lid(gid));
547 }
548
549 // now make the lists of gids we will remove
550 // there is no purpose here - we just want to update a bunch, then
551 // remove some, then find some. The testing here will purposefully try
552 // to find gids which were removed and then verify they are in fact
553 // no longer in the directory.
554 for(int i = 0; i < proc_remove_gid(gid, comm->getRank()); ++i) {
555 remove_gids.push_back(gid);
556 }
557
558 // now make the list of gids we will find
559 for(int i = 0; i < proc_find_gid(gid, comm->getRank()); ++i) {
560 find_gids.push_back(gid);
561 }
562 }
563
564 // set up all the initial writes - update_user will be send to the
565 // directory with these values for Replace, Add, or Aggregate operations.
566 this->update_user.resize(update_gids.size());
567 for(size_t n = 0; n < update_user.size(); ++n) {
568 update_user[n] = get_initial_user(update_gids[n], comm->getRank());
569 }
570 }
571
572 // this is where we actually make a directory and do things on it
573 virtual void test() = 0;
574
575 template<typename directory_t>
577 const int debug_level = 0;
578 directory_t zz1(comm, bUseLocalIDs, debug_level);
579
580 // this step is not necessary but implemented to provide coverage of the
581 // copy constructor and the operator= method.
582 // Can be removed and then change above zz1 to just zz
583 directory_t zz2(zz1);
584 directory_t zz = zz2;
585
586 // usually this step is not necessary - for this test we initialize all
587 // the find_user values to a specific number and then check that it's not
588 // changed when we call find on an ID which was previously removed. The
589 // formal behavior for this in a normal setup is probably to just have an
590 // error but in this special case we override the throw when we call find.
592
593 // for lid note that the original mode will leave a not found gid such
594 // that the lid is set to the gid. This is how the find_local worked. In
595 // the new directory the value will be left untouched so this will be
596 // preserved. Also original mode requires this to be set but for new
597 // modes with std::vector we can make this fillled automatically.
598 if(bUseLocalIDs) { // if false it shouldn't matter if we call this
600 }
601
602 // convert generic mode to Zoltan2Directory mode. This awkward step exists
603 // because the Original mode doesn't have a directory since it's the
604 // original zoltan code. When Original mode is running the new directory
605 // code does not exist and the directory_t::Update_Mode doesn't exist.
606 // So we have generic mode (just for the unit test) which everything can
607 // understand and then convert to the directory mode here since this is
608 // not Original mode. This could probaly be done better but it helped
609 // to avoid some special casing through out.
610 auto directoryMode = directory_t::Update_Mode::Add;
611 switch(mode) {
612 case Add:
613 directoryMode = directory_t::Update_Mode::Add;
614 break;
615 case Replace:
616 directoryMode = directory_t::Update_Mode::Replace;
617 break;
618 case Aggregate:
619 directoryMode = directory_t::Update_Mode::Aggregate;
620 break;
621 }
622
623 // now create the directory data with update - this will proces Replace,
624 // Add, or Aggregate, depending on the directoryMode.
625 zz.update(update_gids.size(), &update_gids[0],
626 bUseLocalIDs ? &update_lids[0] : NULL,
627 &update_user[0], NULL, directoryMode);
628
629 zz.remove(remove_gids.size(), &remove_gids[0]);
630
631 // Now call find which will fill find_user with the correct data.
632 // Some of the tests use lids and will also fill find_lids with lid values.
633 zz.find(find_gids.size(), &find_gids[0],
634 bUseLocalIDs ? &find_lids[0] : NULL,
635 &find_user[0], NULL, NULL, false);
636 }
637
638 // set passed true/false based on the results gotten back from the directory
639 // here we use the methods of this class to calculate what the directory is
640 // supposed to have done and then compare to see if it's correct.
641 void analyze() {
643 }
644
645 // prints a bunch of output depending on flag settings
646 // this was mainly used for debugging but might be useful for learning about
647 // the tests. This is setup as a hard coded value named print_output which
648 // is set in runDirectoryTests.
649 void output() {
651 print();
652 }
653 if(!performance_test) {
655 }
656 }
657
658 // data stored by the class
659 size_t totalIds; // total gids across all procs
660 size_t idBase; // first gid value
661 size_t idStride; // stride between gid values
662 Teuchos::RCP<const
663 Teuchos::Comm<int> > comm; // communicator sent to directory
664 size_t maxPrintSize; // print only this number for debugging
665 int mode; // Replace, Add, or Aggregate
666 std::vector<gid_t> update_gids; // gids generated on this proc
667 std::vector<lid_t> update_lids; // corresponding lids generated on this proc
668 std::vector<user_t> update_user; // user data we initially updated with
669 std::vector<gid_t> find_gids; // gids to read
670 std::vector<lid_t> find_lids; // lids will be filled
671 std::vector<user_t> find_user; // user data we find
672 std::vector<gid_t> remove_gids; // gids to remove
673 bool did_setup; // error check setup not called twice
674 std::string test_name; // used for logging
675 bool passed; // did we pass
676 bool print_detailed_output; // log all the details
677 bool performance_test; // is this being run as a performance test
678 bool bUseLocalIDs; // should local ids be tested/applied
679};
680
681// Test classes are built using one of these:
682// Single_GID - gid is not a struct - just simple type like long
683// Multiple_GID - gid is a struct such as struct { int a[4]; }
684// The two classes exist to handle all the special casing when using the
685// two different types of gid.
686// see base class abstract definitions for comments on each method
687template <typename gid_t,typename lid_t, typename user_t>
688class Single_GID : public virtual IDs<gid_t,lid_t,user_t>
689{
690 public:
692
693 protected:
694 virtual lid_t get_not_found_lid() const {
695 return NOT_FOUND_VALUE;
696 }
697
698 virtual std::string gid_to_string(gid_t gid) const {
699 return "(" + std::to_string(gid) + ")";
700 };
701
702 virtual std::string lid_to_string(lid_t lid) const {
703 return "(" + std::to_string(lid) + ")";
704 };
705
706 virtual bool check_lid_equal(const lid_t & a, const lid_t & b) const {
707 return (a == b);
708 }
709
710 virtual size_t gid_seed_value(const gid_t& gid) const {
711 return gid;
712 }
713
714 virtual int convert_gid_to_index(const gid_t& gid) const {
715 return (gid-this->idBase)/this->idStride;
716 }
717
718 virtual gid_t convert_index_to_gid(int index) const {
719 return this->idBase + index * this->idStride;
720 }
721
722 virtual lid_t get_initial_lid(gid_t gid) const {
723 return gid + 1; // this is for testing - make the lid equal to gid+1
724 }
725};
726
727// Test classes are built using one of these:
728// Single_GID - gid is not a struct - just simple type like long
729// Multiple_GID - gid is a struct such as struct { int a[4]; }
730// The two classes exist to handle all the special casing when using the
731// two different types of gid.
732// see base class abstract definitions for comments on each method
733template <typename gid_t,typename lid_t, typename user_t>
734class Multiple_GID : public virtual IDs<gid_t,lid_t,user_t>
735{
736 public:
737 Multiple_GID(size_t gid_length_, size_t lid_length_) :
738 gid_length(gid_length_), lid_length(lid_length_) {
739 // currently we're making the testing for multiple gid cover
740 // multiple lid as well - further combinations would be a gid multiple
741 // type such as [1,4,5,8] and a simple lid type ( such as int ).
742 }
743
744 protected:
745 virtual lid_t get_not_found_lid() const {
746 lid_t not_found;
747 for(size_t n = 0; n < lid_length; ++n) {
748 not_found.sub_lid[n] = NOT_FOUND_VALUE;
749 }
750 return not_found;
751 }
752
753 virtual std::string gid_to_string(gid_t gid) const {
754 std::string output_string = "(";
755 for(size_t n = 0; n < gid_length; ++n) {
756 if(n!=0) output_string += " ";
757 output_string += std::to_string(gid.sub_gid[n]);
758 }
759 output_string += ")";
760 return output_string;
761 };
762
763 virtual std::string lid_to_string(lid_t lid) const {
764 std::string output_string = "(";
765 for(size_t n = 0; n < lid_length; ++n) {
766 if(n!=0) output_string += " ";
767 output_string += std::to_string(lid.sub_lid[n]);
768 }
769 output_string += ")";
770 return output_string;
771 };
772
773 virtual bool check_lid_equal(const lid_t & a, const lid_t & b) const {
774 for(size_t n = 0; n < lid_length; ++n) {
775 if(a.sub_lid[n] != b.sub_lid[n]) {
776 return false;
777 }
778 }
779 return true;
780 }
781
782 virtual size_t gid_seed_value(const gid_t& gid) const {
783 return gid.sub_gid[0]; // just uses first to generate values
784 }
785
786 virtual int convert_gid_to_index(const gid_t& gid) const {
787 // here we are testing gid with multiple elements
788 // the first element is set using the same rules as for single gid
789 // so just read that and convert back to index
790 return (gid.sub_gid[0]-this->idBase)/this->idStride;
791 }
792
793 virtual gid_t convert_index_to_gid(int index) const {
794 gid_t gid;
795 // here we are testing gid with multiple elements
796 // set the first element as if it was a single gid - same as other tests
797 // set all subsequent gid sub ids in increasing order just to have change
798 // the values don't have any impact on the behavior of the directory
799 int val = this->idBase + index * this->idStride;
800 for(size_t n = 0; n < gid_length; ++n) {
801 gid.sub_gid[n] = val + n;
802 }
803 return gid;
804 }
805
806 virtual lid_t get_initial_lid(gid_t gid) const {
807 lid_t result;
808 for(size_t n = 0; n < lid_length; ++n) {
809 result.sub_lid[n] = n + gid.sub_gid[0] + 1; // lid will be gid[0]+1, gid[1]+2, gid[2]+3...
810 }
811 return result;
812 }
813
814 private:
815 size_t gid_length;
816 size_t lid_length;
817};
818
819// Test classes are built using one of these:
820// Single_User - user data is a simple type like long
821// Vector_User - user data is a std::vector<>
822// The two classes exist to handle all the special casing when using the
823// two different types of user data.
824// see base class abstract definitions for comments on each method
825template <typename gid_t,typename lid_t, typename user_t>
826class Single_User : public virtual IDs<gid_t,lid_t,user_t>
827{
828 public:
830
831 protected:
832 virtual void test() {
834 directory_t;
835 this->template test_implement<directory_t>();
836 }
837
838 virtual user_t get_not_found_user() const {
839 return NOT_FOUND_VALUE;
840 }
841
842 virtual user_t get_expected_user(gid_t gid) const {
843 switch(this->mode) {
845 return this->get_initial_user(gid, this->comm->getRank());
846 break;
847 case TestMode::Add:
848 return this->sharedCount(gid); // should have summed
849 break;
851 throw std::logic_error("Unexpected aggregate mode for non array.");
852 break;
853 default:
854 throw std::logic_error("Unexpected mode index.");
855 break;
856 }
857 }
858
859 virtual user_t get_initial_user(gid_t gid, int rank) const {
860 switch(this->mode) {
862 // arbitrary - just make it something good to test
863 // note that replace is a tricky case because two procs could both
864 // try to update the same gid with different values. Then the result
865 // could be arbitrary based on how the messages are passed.
866 // For the testing code here the value is fixed to be the same for
867 // all procs but the question to resolve is should the directory
868 // handle conflicting replace calls with an error or simply assume the
869 // user is responsible for making it logically consistent.
870 return this->gid_seed_value(gid) + 3;
871 break;
872 case TestMode::Add:
873 return 1; // we will be testing if they sum to shared count
874 break;
876 throw std::logic_error("Aggregate requested for non array setup.");
877 break;
878 default:
879 throw std::logic_error("Unexpected mode index.");
880 break;
881 }
882 }
883
884 virtual bool evaluateTests() const {
885
886 // check the results
887 bool pass = true;
888 for(int proc = 0; proc < this->comm->getSize(); ++proc) {
889 bool passRank = true;
890 this->comm->barrier();
891 if(proc == this->comm->getRank()) {
892 for(size_t i = 0; i < this->find_gids.size(); ++i) {
893 gid_t gid = this->find_gids[i];
894
895 // verify removal - eventually I think we may have the directory
896 // just throw an error if we try to find on a gid which was removed.
897 // Or some return value will indicate. For now we pass in all values
898 // of NOT_FOUND_VALUE which will still be set to that if not found.
899 if(this->removedIDGlobally(gid)) {
900 if(this->find_user[i] != NOT_FOUND_VALUE) {
901 passRank = false;
902 std::cout << "Removed gid: " << this->gid_to_string(gid) <<
903 " but got value: " << this->find_user[i] <<
904 " when we expected to read the unset value of " <<
905 NOT_FOUND_VALUE << ". " << " This is incorrect. " <<
906 "Remove FAILED." << std::endl;
907 }
908 }
909 else {
910 user_t expected_value = this->get_expected_user(gid);
911 if(this->find_user[i] != expected_value) {
912 passRank = false;
913 std::cout << "Failed read user data for global ID: " <<
914 this->gid_to_string(gid) << ". Expected data: " <<
915 expected_value << " Got data: " <<
916 this->find_user[i] << std::endl;
917 }
918
919 if(this->bUseLocalIDs) {
920 const lid_t & find_lid = this->find_lids[i];
921 lid_t expected_lid = this->get_initial_lid(gid);
922 if(!this->check_lid_equal(find_lid, expected_lid)) {
923 passRank = false;
924 std::cout << "Failed read lid for global ID: " <<
925 this->gid_to_string(gid) << ". Expected lid: " <<
926 this->lid_to_string(expected_lid) << " Got lid: " <<
927 this->lid_to_string(find_lid) << std::endl;
928 }
929 }
930 }
931
932 if(!passRank) {
933 break; // we don't need to check further
934 }
935 }
936
937 if(!passRank) {
938 std::cout << "Checked rank: " << this->comm->getRank() <<
939 " with nIds: " << this->find_gids.size() << " which " <<
940 "FAILED" << std::endl;
941 }
942
943 if(!passRank) {
944 pass = false;
945 }
946 }
947 }
948 this->comm->barrier();
949
950 return pass;
951 }
952
953 virtual void print_user_data() const {
954 std::cout << "Write GID user data ";
955 for(size_t n = 0; n < this->update_gids.size(); ++n) {
956 std::cout << this->gid_to_string(this->update_gids[n]) << ":" <<
957 this->update_user[n] << " ";
958 }
959 std::cout << std::endl;
960
961 std::cout << "Find GID user data ";
962 for(size_t n = 0; n < this->find_gids.size(); ++n) {
963 std::cout << this->gid_to_string(this->find_gids[n]) << ":" <<
964 this->find_user[n] << " ";
965 }
966 std::cout << std::endl;
967 }
968};
969
970// Test classes are built using one of these:
971// Single_User - user data is a simple type like long
972// Vector_User - user data is a std::vector<>
973// The two classes exist to handle all the special casing when using the
974// two different types of user data.
975// see base class abstract definitions for comments on each method
976template <typename gid_t,typename lid_t, typename user_t>
978{
979 public:
981
982 protected:
983 virtual void test() {
985 directory_t;
986 this->template test_implement<directory_t>();
987 }
988
989 virtual user_t get_not_found_user() const {
990 return user_t(1, NOT_FOUND_VALUE);
991 }
992
993 virtual user_t get_expected_user(gid_t gid) const {
994 switch(this->mode) {
995 case TestMode::Add:
996 return user_t(test_create_array_length(gid, this->comm->getRank()),
997 this->sharedCount(gid)); // should have summed
998 break;
1000 return get_initial_user(gid, this->comm->getRank());
1001 break;
1003 {
1004 // for this we need the union of all the procs
1005 user_t final_aggregated;
1006 // loop through all possible updaters
1007 for(int proc = 0; proc < this->comm->getSize(); ++proc) {
1008 if(this->proc_update_gid(gid, proc)) { // did this proc update?
1009 user_t proc_input = get_initial_user(gid, proc); // get original
1010 for(size_t i = 0; i < proc_input.size(); ++i) { // scan elements
1011 auto val = proc_input[i]; // get the array element
1012 if(final_aggregated.size() == 0 ||
1013 val > final_aggregated[final_aggregated.size()-1]) {
1014 // add first element or tail end
1015 final_aggregated.push_back(val);
1016 }
1017 else { // loop and insert to keep ordering until match found
1018 for(auto itr = final_aggregated.begin();
1019 itr != final_aggregated.end(); ++itr) {
1020 if((*itr) == val) {
1021 break; // don't add - already added
1022 }
1023 else if((*itr) > val) {
1024 final_aggregated.insert(itr, val);
1025 break; // insert here to keep ordering
1026 }
1027 }
1028 }
1029 }
1030 }
1031 }
1032 return final_aggregated;
1033 }
1034 break;
1035 default:
1036 throw std::logic_error("Unexpected mode index.");
1037 break;
1038 }
1039 }
1040
1041 virtual user_t get_initial_user(gid_t gid, int rank) const {
1042 // determine length of array
1043 size_t modLength = test_create_array_length(gid, rank);
1044 user_t array(modLength);
1045 for(size_t n = 0; n < array.size(); ++n) {
1046 switch(this->mode) {
1047 case TestMode::Replace:
1048 array[n] = this->gid_seed_value(gid) + 3; // 3 is arbitrary
1049 break;
1050 case TestMode::Add:
1051 array[n] = 1; // all elements sum to comm->getSize()
1052 break;
1054 // Now we want some mix so that, for example, gid 10 will have
1055 // different but overlapping values for each array element n
1056 // For example proc 1 could update with gid=10 array={5,7,9}
1057 // while proc 2 could update with gid=10 array={3,5,7}
1058 // Then we expect the result to be {3,5,7,9}
1059 array[n] = n + rank*2; // this creates overlapping regions
1060 break;
1061 default:
1062 throw std::logic_error("Unexpected mode index.");
1063 break;
1064 }
1065 }
1066 return array;
1067 }
1068
1069 virtual bool evaluateTests() const {
1070
1071 // check the results
1072 bool pass = true;
1073 for(int proc = 0; proc < this->comm->getSize(); ++proc) {
1074 bool passRank = true;
1075 this->comm->barrier();
1076 if(proc == this->comm->getRank()) {
1077 for(size_t i = 0; i < this->find_gids.size(); ++i) {
1078 gid_t gid = this->find_gids[i];
1079
1080 // verify removal - eventually I think we may have the directory
1081 // just throw an error if we try to find on a gid which was removed.
1082 // Or some return value will indicate. For now we pass in all values
1083 // of NOT_FOUND_VALUE which will still be set to that if not found.
1084 if(this->removedIDGlobally(gid)) {
1085 // should be an array of size 1 with element NOT_FOUND_VALUE
1086 if(this->find_user[i].size() != 1 ||
1087 this->find_user[i][0] != NOT_FOUND_VALUE) {
1088 passRank = false;
1089 std::cout << "Removed array for gid: " <<
1090 this->gid_to_string(gid) <<
1091 " but something set the user data which is incorrect. "
1092 "Remove FAILED." << std::endl;
1093 }
1094 }
1095 else {
1096 user_t expected_value = get_expected_user(gid);
1097 // first validate the array length is correct
1098 if(this->find_user[i].size() != expected_value.size()) {
1099 std::cout << " Rank: " << proc << " array size is incorrect for"
1100 " gid: " << this->gid_to_string(gid) << ". Expected size: " <<
1101 expected_value.size() << " and got size: " <<
1102 this->find_user[i].size() << std::endl;
1103 passRank = false;
1104 break;
1105 }
1106
1107 // TODO: Fix this code duplicated in the other evaluateTests
1108 // Generall these two methdos can perhaps be merged better now
1109 if(this->bUseLocalIDs) {
1110 const lid_t & find_lid = this->find_lids[i];
1111 lid_t expected_lid = this->get_initial_lid(gid);
1112 if(!this->check_lid_equal(find_lid, expected_lid)) {
1113 passRank = false;
1114 std::cout << "Failed read lid for global ID: " <<
1115 this->gid_to_string(gid) << ". Expected lid: " <<
1116 this->lid_to_string(expected_lid) << " Got lid: " <<
1117 this->lid_to_string(find_lid) << std::endl;
1118 }
1119 }
1120
1121 // now loop the elements and validate each individual element
1122 for(size_t arrayIndex = 0; arrayIndex < this->find_user[i].size()
1123 && passRank; ++arrayIndex) {
1124 if(this->find_user[i][arrayIndex] != expected_value[arrayIndex]) {
1125 passRank = false;
1126 std::cout << " Failed vector read for global ID: " <<
1127 this->gid_to_string(gid)
1128 << ". Expected: " << expected_value[arrayIndex] <<
1129 " at array index " << arrayIndex << ". Got: " <<
1130 this->find_user[i][arrayIndex] << std::endl;
1131 }
1132 }
1133 }
1134
1135 if(!passRank) {
1136 break; // we don't need to check further
1137 }
1138 }
1139
1140 if(!passRank) {
1141 std::cout << " Checked rank: " << this->comm->getRank() <<
1142 " with num find gids: " << this->find_gids.size() << " which " <<
1143 "FAILED" << std::endl;
1144 }
1145
1146 if(!passRank) {
1147 pass = false;
1148 }
1149 }
1150 }
1151 this->comm->barrier();
1152
1153 return pass;
1154 }
1155
1156 virtual void print_user_data() const {
1157 for(size_t n = 0; n < this->update_gids.size(); ++n) {
1158 std::cout << " Write array for GID " <<
1159 this->gid_to_string(this->update_gids[n]) << ": ";
1160 for(size_t a = 0; a < this->update_user[n].size(); ++a) {
1161 std::cout << this->update_user[n][a] << " ";
1162 }
1163 std::cout << std::endl;
1164 }
1165 for(size_t n = 0; n < this->find_gids.size(); ++n) {
1166 std::cout << " Read array for GID " <<
1167 this->gid_to_string(this->find_gids[n]) << ": ";
1168 for(size_t a = 0; a < this->find_user[n].size(); ++a) {
1169 std::cout << this->find_user[n][a] << " ";
1170 }
1171 std::cout << std::endl;
1172 }
1173 }
1174
1175 virtual size_t test_create_array_length(gid_t gid, int proc) const {
1176 switch(this->mode) {
1177 case Replace:
1178 // replace is in some ways the trickiest because Add and Aggregate
1179 // are not order dependent. If two different procs both try to update
1180 // the same gid with replace the final result may be arbitrary ordering
1181 // and not well defined. Should the directory be responsible for
1182 // detecting a logic error from the user? Note this issue is not a
1183 // vector issue, but a general issue with replace. For now we assume
1184 // the user is consistent and never sends conflicting calls. Replace
1185 // uses the same value for type or vector and is independent of rank.
1186 return (this->gid_seed_value(gid)%7); // 1..7 inclusive same as Add
1187 break;
1188 case Add:
1189 // in this case all vector lengths must be identical for each proc
1190 // or the directory gives an error - cannot add length 2 and length 8
1191 return (this->gid_seed_value(gid)%7); // 1..7 inclusive
1192 break;
1193 case Aggregate:
1194 // in this case we want different proc but same gid to make different
1195 // vectors - so for example mix a length 3 with a length 8
1196 return (this->gid_seed_value(gid)%7)+proc; // varies per gid and proc
1197 break;
1198 default:
1199 throw std::logic_error("test_create_array_length bad mode.");
1200 }
1201 }
1202};
1203
1204// These classes build the test using a combination of the following classes:
1205// Single_User or Vector_User
1206// Single_GID or Multiple_GID
1207// The class just calls execute and exists to assemble the required inheritance.
1208template <typename gid_t,typename lid_t, typename user_t>
1211{
1212 public:
1213 Single_User_Single_GID(size_t totalIds_, size_t idBase_, size_t idStride_,
1214 Teuchos::RCP<const Teuchos::Comm<int> > &comm_, int mode_,
1215 const std::string& name_, bool print_detailed_output_,
1216 bool performance_test_, bool bUseLocalIDs_) :
1217 IDs<gid_t, lid_t, user_t>(totalIds_, idBase_, idStride_, comm_, mode_,
1218 name_, print_detailed_output_, performance_test_, bUseLocalIDs_),
1219 Single_User<gid_t, lid_t, user_t>(),
1220 Single_GID<gid_t, lid_t, user_t>() {
1221 this->execute();
1222 }
1223
1224 virtual std::string get_test_style() const {
1225 return "Single_User_Single_GID";
1226 }
1227};
1228
1229// These classes build the test using a combination of the following classes:
1230// Single_User or Vector_User
1231// Single_GID or Multiple_GID
1232// The class just calls execute and exists to assemble the required inheritance.
1233template <typename gid_t,typename lid_t, typename user_t>
1236{
1237 public:
1238 Single_User_Multiple_GID(size_t gid_length_, size_t lid_length_,
1239 size_t totalIds_, size_t idBase_, size_t idStride_,
1240 Teuchos::RCP<const Teuchos::Comm<int> > &comm_, int mode_,
1241 const std::string& name_, bool print_detailed_output_,
1242 bool performance_test_, bool bUseLocalIDs_) :
1243 IDs<gid_t, lid_t, user_t>(totalIds_, idBase_, idStride_, comm_, mode_,
1244 name_, print_detailed_output_, performance_test_, bUseLocalIDs_),
1245 Single_User<gid_t, lid_t, user_t>(),
1246 Multiple_GID<gid_t, lid_t, user_t>(gid_length_, lid_length_) {
1247 this->execute();
1248 }
1249
1250 virtual std::string get_test_style() const {
1251 return "Single_User_Multiple_GID";
1252 }
1253};
1254
1255// These classes build the test using a combination of the following classes:
1256// Single_User or Vector_User
1257// Single_GID or Multiple_GID
1258// The class just calls execute and exists to assemble the required inheritance.
1259template <typename gid_t,typename lid_t, typename user_t>
1262{
1263 public:
1264 Vector_User_Single_GID(size_t totalIds_, size_t idBase_, size_t idStride_,
1265 Teuchos::RCP<const Teuchos::Comm<int> > &comm_, int mode_,
1266 const std::string& name_, bool print_detailed_output_,
1267 bool performance_test_, bool bUseLocalIDs_) :
1268 IDs<gid_t, lid_t, user_t>(totalIds_, idBase_, idStride_, comm_, mode_,
1269 name_, print_detailed_output_, performance_test_, bUseLocalIDs_),
1270 Vector_User<gid_t, lid_t, user_t>(),
1271 Single_GID<gid_t, lid_t, user_t>() {
1272 this->execute();
1273 }
1274
1275 virtual std::string get_test_style() const {
1276 return "Vector_User_Single_GID";
1277 }
1278};
1279
1280// These classes build the test using a combination of the following classes:
1281// Single_User or Vector_User
1282// Single_GID or Multiple_GID
1283// The class just calls execute and exists to assemble the required inheritance.
1284template <typename gid_t,typename lid_t, typename user_t>
1287{
1288 public:
1289 Vector_User_Multiple_GID(size_t gid_length_, size_t lid_length_,
1290 size_t totalIds_, size_t idBase_, size_t idStride_,
1291 Teuchos::RCP<const Teuchos::Comm<int> > &comm_, int mode_,
1292 const std::string& name_, bool print_detailed_output_,
1293 bool performance_test_, bool bUseLocalIDs_) :
1294 IDs<gid_t, lid_t, user_t>(totalIds_, idBase_, idStride_, comm_, mode_,
1295 name_, print_detailed_output_, performance_test_, bUseLocalIDs_),
1296 Vector_User<gid_t, lid_t, user_t>(),
1297 Multiple_GID<gid_t, lid_t, user_t>(gid_length_, lid_length_) {
1298 this->execute();
1299 }
1300
1301 virtual std::string get_test_style() const {
1302 return "Vector_User_Multiple_GID";
1303 }
1304};
1305
1306// The TestManager holds some common values and provides 4 API calls for running
1307// tests using the different options. The Multiple GID option is just for
1308// setting up tests as the directory class itself works automatically to handle
1309// gid_t of int or a struct. For Vector User this is currently handled by
1310// a different named directory class but eventually may merge these back into
1311// a single class and just let the API distinguish. However I'm not sure about
1312// how to make that work cleanly with the templating. The 4 testing modes are:
1313// Single User + Single GID (ex. user_t int, gid=8)
1314// Single User + Multiple GID (ex. user_t int, gid=[0,1,8])
1315// Vector User + Single GID (ex. user_t std::vector<int>, gid=8)
1316// Vector User + Multiple GID (ex. user_t std::vector<int>, gid=[0,1,8]
1317// The point of this is to make sure this unit test actually tries all these
1318// combinations and fails if one is missing. Only Kokkos mode supports everything.
1320 public:
1321 TestManager(Teuchos::RCP<const Teuchos::Comm<int> > comm_, int totalIds_,
1322 bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_) :
1323 comm(comm_), totalIds(totalIds_), all_pass(true),
1324 print_detailed_output(print_detailed_output_),
1325 performance_test(performance_test_), bUseLocalIDs(bUseLocalIDs_) {
1326 }
1327
1328 // Single User + Single GID
1329 template<typename gid_t, typename lid_t, typename user_t>
1330 void run_single_user_single_gid(const std::string& name_,
1331 int mode_, size_t idBase_, int idStride_) {
1333 idBase_, idStride_, comm, mode_, name_, print_detailed_output,
1334 performance_test, bUseLocalIDs);
1335 if(!ids.did_test_pass()) { all_pass = false; }
1336 }
1337
1338 // Single User + Multiple GID
1339 template<typename gid_t, typename lid_t, typename user_t>
1340 void run_single_user_multiple_gid(size_t gid_length_, size_t lid_length_,
1341 const std::string& name_, int mode_, size_t idBase_, int idStride_) {
1342 Single_User_Multiple_GID<gid_t,lid_t,user_t> ids(gid_length_, lid_length_,
1343 totalIds, idBase_, idStride_, comm, mode_, name_, print_detailed_output,
1344 performance_test, bUseLocalIDs);
1345 if(!ids.did_test_pass()) { all_pass = false; }
1346 }
1347
1348 // Vector User + Single GID
1349 template<typename gid_t, typename lid_t, typename user_t>
1350 void run_vector_user_single_gid(const std::string& name_,
1351 int mode_, size_t idBase_, int idStride_) {
1353 idBase_, idStride_, comm, mode_, name_, print_detailed_output,
1354 performance_test, bUseLocalIDs);
1355 if(!ids.did_test_pass()) { all_pass = false; }
1356 }
1357
1358 // Vector User + Multiple GID
1359 template<typename gid_t, typename lid_t, typename user_t >
1360 void run_vector_user_multiple_gid(size_t gid_length_, size_t lid_length_,
1361 const std::string& name_, int mode_, size_t idBase_, int idStride_) {
1362 Vector_User_Multiple_GID<gid_t,lid_t,user_t> ids(gid_length_, lid_length_,
1363 totalIds, idBase_, idStride_, comm, mode_, name_, print_detailed_output,
1364 performance_test, bUseLocalIDs);
1365 if(!ids.did_test_pass()) { all_pass = false; }
1366 }
1367
1368 bool did_all_pass() const { return all_pass; }
1369
1370 private:
1371 Teuchos::RCP<const Teuchos::Comm<int> > comm;
1372 int totalIds;
1373 bool all_pass;
1374 bool print_detailed_output;
1375 bool performance_test;
1376 bool bUseLocalIDs;
1377};
1378
1379// This is a systematic grind through all the possible options the directory
1380// can do and makes use of above classes to hit on various features like
1381// vector user type versus single user type, multiple gid, different types,
1382// etc. This is intended to be a thorough test of the directory but is
1383// not transparent as a starting point so another test was created called
1384// directoryTest_KokkosSimple which demonstrates a more natural usage of the
1385// directory - that provides examples of how to use the directory which is
1386// independent of all the above stuff.
1387//
1388// Setting print_output true below will print a lot of information about the
1389// gid lists for update, remove, find, as well as the user data and associated
1390// lids after running find.
1391int runDirectoryTests(int narg, char **arg) {
1392 Tpetra::ScopeGuard tscope(&narg, &arg);
1393 Teuchos::RCP<const Teuchos::Comm<int> > comm =
1394 Teuchos::DefaultComm<int>::getComm();
1395
1396 // run the tests through a range of totalIds
1397 // we pick 0 and 1 and some low values for edge cases, then just
1398 // try to hit on some random spots
1399 std::vector<size_t> run_with_totalIds = {0, 1, 2, 3, 5, 27, 63, 456, 1093};
1400
1401 // note setting run_with_totalIds to something simpler may help to see what
1402 // the logs mean if print_output is turned on. For example:
1403 // run_with_totalIds = { 20 };
1404
1405 int err = 0;
1406
1407 const bool print_output = false; // noisy output with values for each gid
1408 const bool performance_test = false;
1409
1410 for(int run_with_local_ids = 0; run_with_local_ids <= 1; ++run_with_local_ids) {
1411
1412 for(size_t n = 0; n < run_with_totalIds.size(); ++n) {
1413
1414 print_proc_safe("Testing totalIds: " + std::to_string(run_with_totalIds[n]),
1415 comm, true);
1416
1417 comm->barrier();
1418
1419 TestManager manager(comm, run_with_totalIds[n], print_output,
1420 performance_test, run_with_local_ids ? true : false);
1421
1422 // loop the modes: Replace, Add, Aggregate and then do various tests on
1423 // each which vary gid_t, single or vector user type, single or multipe gid
1424 // some of the non-kokkos modes don't support everything and skip tests but
1425 // eventually they will all be deleted leaving only Kokkos
1426 for(int test_mode = 0; test_mode < TestMode_Max; ++test_mode) {
1427 // Aggregate mode is for vector user type only
1428 if(test_mode != Aggregate) {
1429 manager.run_single_user_single_gid<int, int, int>
1430 ("contiguous int", test_mode, 0, 1);
1431 manager.run_single_user_single_gid<int, int, int>
1432 ("non-contiguous int", test_mode, 20, 3);
1433
1434 manager.run_single_user_single_gid<long long, int, int>
1435 ("long long", test_mode, 200, 4);
1436 }
1437
1438 manager.run_vector_user_single_gid<int, int, std::vector<int>>
1439 ("contiguous int", TestMode::Aggregate, 0, 1);
1440 manager.run_vector_user_single_gid<int, int, std::vector<int>>
1441 ("non-contiguous int", TestMode::Aggregate, 20, 3);
1442 manager.run_vector_user_single_gid<long long, int, std::vector<int>>
1443 ("non-contiguous long long", TestMode::Aggregate, 200, 4);
1444
1445 // Aggregate mode is for vector user type only
1446 if(test_mode != Aggregate) {
1448 (GID_SET_LENGTH, LID_SET_LENGTH, "contiguous int", test_mode, 0, 1);
1450 (GID_SET_LENGTH, LID_SET_LENGTH, "non-contiguous int", test_mode, 20, 3);
1451 }
1452
1453 manager.run_vector_user_multiple_gid<gid_set_t, lid_set_t, std::vector<int>>
1454 (GID_SET_LENGTH, LID_SET_LENGTH, "contiguous int", test_mode, 0, 1);
1455
1456 if(!manager.did_all_pass()) {
1457 err = 1; // if we fail at some point just drop out
1458 }
1459 }
1460 }
1461 }
1462
1463 // Get the global err results so we fail properly if any proc failed
1464 int errGlobal;
1465 Teuchos::reduceAll<int>(*comm,Teuchos::REDUCE_SUM, err,
1466 Teuchos::outArg(errGlobal));
1467
1468 return errGlobal; // only 0 if all tests and all proc return 0
1469}
1470
1471} // namespace Zoltan2
Zoltan2::BasicUserTypes< zscalar_t, zlno_t, zgno_t > user_t
Definition: Metric.cpp:74
Traits class to handle conversions between gno_t/lno_t and TPL data types (e.g., ParMETIS's idx_t,...
std::vector< gid_t > update_gids
virtual std::string gid_to_string(gid_t gid) const =0
virtual std::string lid_to_string(lid_t lid) const =0
virtual int convert_gid_to_index(const gid_t &gid) const =0
bool did_test_pass() const
did_test_pass - did test pass
void print() const
detailed notes on update IDs, find IDs, etc
virtual void test()=0
int proc_remove_gid(gid_t gid, int rank) const
virtual bool evaluateTests() const =0
evaluateTests - determine if test worked
int proc_update_gid(gid_t gid, int rank) const
virtual size_t gid_seed_value(const gid_t &gid) const =0
void print_gids(const std::vector< gid_t > &printIds, std::string name) const
int getMode() const
getMode - Replace, Add, or Aggregate
virtual lid_t get_initial_lid(gid_t gid) const =0
virtual bool check_lid_equal(const lid_t &a, const lid_t &b) const =0
virtual void print_user_data() const =0
IDs(size_t totalIds_, size_t idBase_, size_t idStride_, Teuchos::RCP< const Teuchos::Comm< int > > &comm_, int mode_, const std::string &test_name_, bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_)
Construct IDs.
std::vector< user_t > update_user
Teuchos::RCP< const Teuchos::Comm< int > > comm
bool subset2(int index, int rank) const
virtual gid_t convert_index_to_gid(int index) const =0
std::vector< lid_t > find_lids
std::string get_mode_name() const
std::vector< user_t > find_user
void print_lids(const std::vector< lid_t > &printIds, std::string name) const
virtual std::string get_test_style() const =0
get_test_style the test is either vector user_t or single user_t the test is either multiple gids or ...
virtual void setup()
std::string test_name
bool subset1(int index, int rank) const
std::vector< lid_t > update_lids
virtual void debug_print_subsets_and_decisions()
std::vector< gid_t > remove_gids
virtual void initialize_with_not_found_lid()
std::vector< gid_t > find_gids
int sharedCount(gid_t gid) const
virtual void print_lid_data() const
bool trueForAtLeastOneProc(int index, int rank) const
virtual user_t get_initial_user(gid_t gid, int rank) const =0
virtual void execute()
virtual lid_t get_not_found_lid() const =0
int proc_find_gid(gid_t gid, int rank) const
virtual user_t get_expected_user(gid_t gid) const =0
void printResultMessage(bool pass) const
const std::string & get_test_name() const
bool removedIDGlobally(gid_t gid) const
virtual void initialize_with_not_found_user()
virtual user_t get_not_found_user() const =0
virtual lid_t get_not_found_lid() const
virtual lid_t get_initial_lid(gid_t gid) const
virtual std::string gid_to_string(gid_t gid) const
virtual size_t gid_seed_value(const gid_t &gid) const
Multiple_GID(size_t gid_length_, size_t lid_length_)
virtual gid_t convert_index_to_gid(int index) const
virtual int convert_gid_to_index(const gid_t &gid) const
virtual bool check_lid_equal(const lid_t &a, const lid_t &b) const
virtual std::string lid_to_string(lid_t lid) const
virtual lid_t get_initial_lid(gid_t gid) const
virtual bool check_lid_equal(const lid_t &a, const lid_t &b) const
virtual gid_t convert_index_to_gid(int index) const
virtual lid_t get_not_found_lid() const
virtual size_t gid_seed_value(const gid_t &gid) const
virtual int convert_gid_to_index(const gid_t &gid) const
virtual std::string gid_to_string(gid_t gid) const
virtual std::string lid_to_string(lid_t lid) const
Single_User_Multiple_GID(size_t gid_length_, size_t lid_length_, size_t totalIds_, size_t idBase_, size_t idStride_, Teuchos::RCP< const Teuchos::Comm< int > > &comm_, int mode_, const std::string &name_, bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_)
virtual std::string get_test_style() const
get_test_style the test is either vector user_t or single user_t the test is either multiple gids or ...
virtual std::string get_test_style() const
get_test_style the test is either vector user_t or single user_t the test is either multiple gids or ...
Single_User_Single_GID(size_t totalIds_, size_t idBase_, size_t idStride_, Teuchos::RCP< const Teuchos::Comm< int > > &comm_, int mode_, const std::string &name_, bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_)
virtual user_t get_initial_user(gid_t gid, int rank) const
virtual user_t get_expected_user(gid_t gid) const
virtual bool evaluateTests() const
evaluateTests - determine if test worked
virtual user_t get_not_found_user() const
virtual void print_user_data() const
void run_single_user_multiple_gid(size_t gid_length_, size_t lid_length_, const std::string &name_, int mode_, size_t idBase_, int idStride_)
TestManager(Teuchos::RCP< const Teuchos::Comm< int > > comm_, int totalIds_, bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_)
void run_vector_user_multiple_gid(size_t gid_length_, size_t lid_length_, const std::string &name_, int mode_, size_t idBase_, int idStride_)
void run_vector_user_single_gid(const std::string &name_, int mode_, size_t idBase_, int idStride_)
void run_single_user_single_gid(const std::string &name_, int mode_, size_t idBase_, int idStride_)
virtual std::string get_test_style() const
get_test_style the test is either vector user_t or single user_t the test is either multiple gids or ...
Vector_User_Multiple_GID(size_t gid_length_, size_t lid_length_, size_t totalIds_, size_t idBase_, size_t idStride_, Teuchos::RCP< const Teuchos::Comm< int > > &comm_, int mode_, const std::string &name_, bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_)
virtual std::string get_test_style() const
get_test_style the test is either vector user_t or single user_t the test is either multiple gids or ...
Vector_User_Single_GID(size_t totalIds_, size_t idBase_, size_t idStride_, Teuchos::RCP< const Teuchos::Comm< int > > &comm_, int mode_, const std::string &name_, bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_)
virtual void print_user_data() const
virtual bool evaluateTests() const
evaluateTests - determine if test worked
virtual user_t get_initial_user(gid_t gid, int rank) const
virtual user_t get_not_found_user() const
virtual size_t test_create_array_length(gid_t gid, int proc) const
virtual user_t get_expected_user(gid_t gid) const
#define LID_SET_LENGTH
#define NOT_FOUND_VALUE
#define GID_SET_LENGTH
Created by mbenlioglu on Aug 31, 2020.
void print_proc_safe(const std::string &message, Teuchos::RCP< const Teuchos::Comm< int > > comm, bool only_rank0=true)
static const std::string pass
int runDirectoryTests(int narg, char **arg)
int sub_gid[GID_SET_LENGTH]
int sub_lid[LID_SET_LENGTH]