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 // ***********************************************************************
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 
51 namespace 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.
57 enum TestMode {
58  Replace = 0,
59  Add,
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
72 struct 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
78 struct 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.
84 void 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
127 template <typename gid_t,typename lid_t,typename user_t>
128 class 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>
576  void test_implement() {
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() {
642  passed = evaluateTests();
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
687 template <typename gid_t,typename lid_t, typename user_t>
688 class 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
733 template <typename gid_t,typename lid_t, typename user_t>
734 class 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
825 template <typename gid_t,typename lid_t, typename user_t>
826 class 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) {
844  case TestMode::Replace:
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;
850  case TestMode::Aggregate:
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) {
861  case TestMode::Replace:
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;
875  case TestMode::Aggregate:
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
976 template <typename gid_t,typename lid_t, typename user_t>
977 class Vector_User : public virtual IDs<gid_t,lid_t,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;
999  case TestMode::Replace:
1000  return get_initial_user(gid, this->comm->getRank());
1001  break;
1002  case TestMode::Aggregate:
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;
1053  case TestMode::Aggregate:
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.
1208 template <typename gid_t,typename lid_t, typename user_t>
1210  public Single_User<gid_t,lid_t,user_t>, public Single_GID<gid_t,lid_t,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.
1233 template <typename gid_t,typename lid_t, typename user_t>
1235  public Single_User<gid_t,lid_t,user_t>, public Multiple_GID<gid_t,lid_t,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.
1259 template <typename gid_t,typename lid_t, typename user_t>
1261  public Vector_User<gid_t,lid_t,user_t>, public Single_GID<gid_t,lid_t,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.
1284 template <typename gid_t,typename lid_t, typename user_t>
1286  public Vector_User<gid_t,lid_t,user_t>, public Multiple_GID<gid_t,lid_t,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.
1391 int 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);
1449  manager.run_single_user_multiple_gid<gid_set_t, lid_set_t, int>
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
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:74