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