Zoltan2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
test_driver.cpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Zoltan2: A package of combinatorial algorithms for scientific computing
4 //
5 // Copyright 2012 NTESS and the Zoltan2 contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 /* \file test_driver.cpp
11  * \brief Test driver for Zoltan2. Facilitates generation of test problem via
12  * a simple .xml input interface
13  */
14 
15 // taking headers from existing driver template
16 // will keep or remove as needed
17 #include <UserInputForTests.hpp>
18 #include <Zoltan2_Typedefs.hpp>
19 #include <AdapterForTests.hpp>
22 
25 
30 
31 #include <Zoltan2_Parameters.hpp>
32 
33 #include <Teuchos_DefaultComm.hpp>
34 #include <Teuchos_XMLObject.hpp>
35 #include <Teuchos_FileInputSource.hpp>
36 
37 #include <sstream>
38 #include <string>
39 #include <map>
40 #include <iostream>
41 #include <queue>
42 
43 using Teuchos::ParameterList;
44 using Teuchos::Comm;
45 using Teuchos::RCP;
46 using Teuchos::ArrayRCP;
47 using Teuchos::XMLObject;
48 using namespace Zoltan2_TestingFramework;
49 
50 using std::string;
51 using std::map;
52 using std::pair;
53 using std::exception;
54 using std::ostringstream;
55 using std::queue;
56 
57 #define ERRMSG(msg) if (rank == 0){ std::cerr << "FAIL: " << msg << std::endl; }
58 #define EXC_ERRMSG(msg, e) \
59 if (rank==0){ std::cerr << "FAIL: " << msg << std::endl << e.what() << std::endl;}
60 
61 void xmlToModelPList(const Teuchos::XMLObject &xml,
62  Teuchos::ParameterList & plist)
63 {
64  // This method composes a plist for the problem
65  Teuchos::XMLParameterListReader reader;
66  plist = reader.toParameterList(xml);
67 
68  // Get list of valid Zoltan2 Parameters
69  // Zoltan 2 parameters appear in the input file
70  // Right now we have default values stored in
71  // the parameter list, we would like to apply
72  // the options specified by the user in their
73  // input file
74  Teuchos::ParameterList zoltan2Parameters;
75  Zoltan2::createAllParameters(zoltan2Parameters);
76 
77  if (plist.isSublist("Zoltan2Parameters")) {
78  // Apply user specified zoltan2Parameters
79  ParameterList &sub = plist.sublist("Zoltan2Parameters");
80  zoltan2Parameters.setParameters(sub);
81  }
82 }
83 
84 bool getParameterLists(const string &inputFileName,
85  queue<ParameterList> &problems,
86  queue<ParameterList> &comparisons,
87  const RCP<const Teuchos::Comm<int> > & comm)
88 {
89  int rank = comm->getRank();
90  // return a parameter list of problem definitions
91  // and a parameter list for solution comparisons
92  Teuchos::FileInputSource inputSource(inputFileName);
93  if(rank == 0) {
94  std::cout << "Input file source: " << inputFileName << std::endl;
95  }
96  XMLObject xmlInput;
97 
98  // Try to get xmlObject from inputfile
99  try
100  {
101  xmlInput = inputSource.getObject();
102  }
103  catch(exception &e)
104  {
105  EXC_ERRMSG("Test Driver error: reading", e); // error reading input
106  return false;
107  }
108 
109  // get the parameter lists for each model
110  for(int i = 0; i < xmlInput.numChildren(); i++)
111  {
112  ParameterList plist;
113  xmlToModelPList(xmlInput.getChild(i), plist);
114 
115  if(plist.name() == "Comparison") {
116  comparisons.emplace(plist);
117  }
118  else {
119  problems.emplace(plist);
120  }
121  }
122 
123  return true;
124 }
125 
126 // Utility function for safe type conversion of adapter
127 bool analyzeMetrics(RCP<EvaluateFactory> evaluateFactory,
128  std::ostringstream & msg,
129  const ParameterList &problem_parameters) {
130  #define ANALYZE_METRICS(adapterClass, metricAnalyzerClass) \
131  RCP<EvaluateBaseClass<adapterClass>> pCast = \
132  rcp_dynamic_cast<EvaluateBaseClass<adapterClass>>( \
133  evaluateFactory->getEvaluateClass()); \
134  if(pCast == Teuchos::null) throw std::logic_error( \
135  "Bad evaluate class cast in analyzeMetrics!" ); \
136  metricAnalyzerClass analyzer(pCast); \
137  return analyzer.analyzeMetrics( \
138  problem_parameters.sublist("Metrics"), msg);
139 
140  #define ANALYZE_METRICS_PARTITIONING(adapterClass) \
141  ANALYZE_METRICS(adapterClass, \
142  MetricAnalyzerEvaluatePartition<adapterClass>)
143 
144  #define ANALYZE_METRICS_ORDERING(adapterClass) \
145  ANALYZE_METRICS(adapterClass, \
146  MetricAnalyzerEvaluateOrdering<adapterClass>)
147 
148  if(evaluateFactory->getProblemName() == "partitioning") {
149  Z2_TEST_UPCAST(evaluateFactory->getAdapterType(), ANALYZE_METRICS_PARTITIONING)
150  }
151  else if(evaluateFactory->getProblemName() == "ordering") {
152  Z2_TEST_UPCAST(evaluateFactory->getAdapterType(), ANALYZE_METRICS_ORDERING)
153  }
154  else {
155  throw std::logic_error(
156  "analyzeMetrics not implemented for this problem type!" );
157  }
158 }
159 
160 // Utility function for safe type conversion of adapter
162  RCP<ProblemFactory> problemFactory) {
163  #define GET_LOCAL_ORDERING(adapterClass) \
164  return (rcp_dynamic_cast<OrderingProblem<adapterClass>>( \
165  problemFactory->getProblem()))->getLocalOrderingSolution();
166  Z2_TEST_UPCAST(problemFactory->getAdapterType(), GET_LOCAL_ORDERING)
167 }
168 
169 // Utility function for safe type conversion of adapter
170 const zpart_t * getPartListView(RCP<ProblemFactory> problemFactory) {
171  #define GET_PROBLEM_PARTS(adapterClass) \
172  return (rcp_dynamic_cast<PartitioningProblem<adapterClass>>( \
173  problemFactory->getProblem()))->getSolution().getPartListView();
174  Z2_TEST_UPCAST(problemFactory->getAdapterType(), GET_PROBLEM_PARTS)
175 }
176 
177 // Utility function for safe type conversion of adapter
178 void getIDsView(RCP<AdapterFactory> adapterFactory, const zgno_t *&Ids) {
179  #define GET_IDS_VIEW(adapterClass) \
180  return dynamic_cast<adapterClass*>( \
181  adapterFactory->getMainAdapter())->getIDsView(Ids);
182  Z2_TEST_UPCAST(adapterFactory->getMainAdapterType(), GET_IDS_VIEW);
183  throw std::logic_error( "getIDsView() failed to match adapter name" );
184 }
185 
186 bool run(const UserInputForTests &uinput,
187  const ParameterList &problem_parameters,
188  bool bHasComparisons,
189  RCP<ComparisonHelper> & comparison_helper,
190  const RCP<const Teuchos::Comm<int> > & comm)
191 {
192  // Major steps in running a problem in zoltan 2
193  // 1. get an input adapter
194  // 2. construct the problem
195  // 3. solve the problem
196  // 4. analyze metrics
197  // 5. clean up
198 
199  int rank = comm->getRank();
200  if(!problem_parameters.isParameter("kind"))
201  {
202  if(rank == 0) {
203  std::cout << "Problem kind not provided" << std::endl;
204  }
205  return false;
206  }
207  if(!problem_parameters.isParameter("InputAdapterParameters"))
208  {
209  if(rank == 0) {
210  std::cout << "Input adapter parameters not provided" << std::endl;
211  }
212  return false;
213  }
214  if(!problem_parameters.isParameter("Zoltan2Parameters"))
215  {
216  if(rank == 0) {
217  std::cout << "Zoltan2 problem parameters not provided" << std::endl;
218  }
219  return false;
220  }
221 
222  if(rank == 0) {
223  std::cout << "\n\nRunning test: " << problem_parameters.name() << std::endl;
224  }
225 
227  // 0. add comparison source
229  RCP<ComparisonSource> comparison_source = rcp(new ComparisonSource);
230 
231  comparison_helper->AddSource(problem_parameters.name(), comparison_source);
232  comparison_source->addTimer("adapter construction time");
233  comparison_source->addTimer("problem construction time");
234  comparison_source->addTimer("solve time");
235 
237  // 1. get basic input adapter
239  const ParameterList &adapterPlist =
240  problem_parameters.sublist("InputAdapterParameters");
241  comparison_source->timers["adapter construction time"]->start();
242 
243  // a pointer to a basic type
244  RCP<AdapterFactory> adapterFactory = rcp(new AdapterFactory(
245  const_cast<UserInputForTests*>(&uinput), adapterPlist, comm));
246 
247  comparison_source->timers["adapter construction time"]->stop();
248 
249  comparison_source->adapterFactory = adapterFactory; // saves until done
250 
252  // 2. construct a Zoltan2 problem
254  // If we are here we have an input adapter, no need to check for one.
255  string adapter_name = adapterPlist.get<string>("input adapter");
256  // get Zoltan2 Parameters
257  ParameterList zoltan2_parameters =
258  const_cast<ParameterList &>(problem_parameters.sublist("Zoltan2Parameters"));
259  if(rank == 0) {
260  std::cout << std::endl;
261  }
262 
263  comparison_source->timers["problem construction time"]->start();
264  std::string problem_kind = problem_parameters.get<std::string>("kind");
265  if (rank == 0) {
266  std::cout << "Creating a new " << problem_kind << " problem." << std::endl;
267  }
268 
269  RCP<ProblemFactory> problemFactory = rcp(new ProblemFactory(
270  problem_kind,
271  adapterFactory,
272  &zoltan2_parameters
273  #ifdef HAVE_ZOLTAN2_MPI
274  ,MPI_COMM_WORLD
275  #endif
276  ));
277 
278  if(rank == 0) {
279  std::cout << "Using input adapter type: " + adapter_name << std::endl;
280  }
281 
282  comparison_source->problemFactory = problemFactory; // saves until done
283 
285  // 3. Solve the problem
287  comparison_source->timers["solve time"]->start();
288 
289  problemFactory->getProblem()->solve();
290 
291  comparison_source->timers["solve time"]->stop();
292  if (rank == 0) {
293  std::cout << problem_kind + " problem solved." << std::endl;
294  }
295 
296 #undef KDDKDD
297 #ifdef KDDKDD
298  if(problem_kind == "partitioning") {
299  const base_adapter_t::gno_t *kddIDs = NULL;
300  getIDsView(adapterFactory, kddIDs);
301  for (size_t i = 0;
302  i < adapterFactory->getMainAdapter()->getLocalNumIDs(); i++) {
303  std::cout << rank << " LID " << i
304  << " GID " << kddIDs[i]
305  << " PART "
306  << getPartListView(problemFactory)[i]
307  << std::endl;
308  }
309  }
310  if (adapter_name == "XpetraCrsGraph") {
311  typedef xCG_xCG_t::lno_t lno_t;
312  typedef xCG_xCG_t::gno_t gno_t;
313  typedef xCG_xCG_t::scalar_t scalar_t;
314  const xCG_xCG_t * xscrsGraphAdapter =
315  dynamic_cast<const xCG_xCG_t *>(adapterFactory->getMainAdapter());
316 
317  int ewgtDim = xscrsGraphAdapter->getNumWeightsPerEdge();
318  lno_t localNumObj = xscrsGraphAdapter->getLocalNumVertices();
319  const gno_t *vertexIds;
320  xscrsGraphAdapter->getVertexIDsView(vertexIds);
321  const offset_t *offsets;
322  const gno_t *adjIds;
323  xscrsGraphAdapter->getEdgesView(offsets, adjIds);
324  for (int edim = 0; edim < ewgtDim; edim++) {
325  const scalar_t *weights;
326  int stride=0;
327  xscrsGraphAdapter->getEdgeWeightsView(weights, stride, edim);
328  for (lno_t i=0; i < localNumObj; i++)
329  for (offset_t j=offsets[i]; j < offsets[i+1]; j++)
330  std::cout << edim << " " << vertexIds[i] << " "
331  << adjIds[j] << " " << weights[stride*j] << std::endl;
332  }
333  }
334 #endif
335 
337  // 4. Print problem metrics
339  bool bSuccess = true;
340  if(problem_parameters.isSublist("Metrics") || bHasComparisons) {
341  RCP<EvaluateFactory> evaluateFactory = rcp(new EvaluateFactory(
342  problem_kind,
343  adapterFactory,
344  &zoltan2_parameters,
345  problemFactory));
346 
347  if(rank == 0) {
348  std::cout << "Create evaluate class for: " + problem_kind << std::endl;
349  }
350 
351  // must add for proper deletion
352  comparison_source->evaluateFactory = evaluateFactory; // saves until done
353 
354  std::ostringstream msgSummary;
355 
356  evaluateFactory->getEvaluateClass()->printMetrics(msgSummary);
357  if(rank == 0) {
358  std::cout << msgSummary.str();
359  }
360 
361  std::ostringstream msgResults;
362  if (!analyzeMetrics(evaluateFactory, msgResults, problem_parameters)) {
363  bSuccess = false;
364  std::cout << "MetricAnalyzer::analyzeMetrics() "
365  << "returned false and the test is FAILED." << std::endl;
366  }
367  if(rank == 0) {
368  std::cout << msgResults.str();
369  }
370 
371 //#define BDD
372 #ifdef BDD
373  if (problem_kind == "ordering") {
374  std::cout << "\nLet's examine the solution..." << std::endl;
375  LocalOrderingSolution<zlno_t> * localOrderingSolution =
376  getLocalOrderingSolution(problemFactory);
377  if (localOrderingSolution->haveSeparators() ) {
378  std::cout << "Number of column blocks: "
379  << localOrderingSolution->getNumSeparatorBlocks() << std::endl;
380  {
381  if (localOrderingSolution->havePerm()) {
382  std::cout << "permutation: {";
383  for (auto &x : localOrderingSolution->getPermutationRCPConst(false))
384  std::cout << " " << x;
385  std::cout << "}" << std::endl;
386  }
387 
388  if (localOrderingSolution->haveInverse()) {
389  std::cout << "inverse permutation: {";
390  for (auto &x : localOrderingSolution->getPermutationRCPConst(true))
391  std::cout << " " << x;
392  std::cout << "}" << std::endl;
393  }
394 
395  if (localOrderingSolution->haveSeparatorRange()) {
396  std::cout << "separator range: {";
397  for (auto &x : localOrderingSolution->getSeparatorRangeRCPConst())
398  std::cout << " " << x;
399  std::cout << "}" << std::endl;
400  }
401 
402  if (localOrderingSolution->haveSeparatorTree()) {
403  std::cout << "separator tree: {";
404  for (auto &x : localOrderingSolution->getSeparatorTreeRCPConst())
405  std::cout << " " << x;
406  std::cout << "}" << std::endl;
407  }
408  }
409  }
410  }
411 #endif
412 
413  comparison_source->printTimers();
414 
415  // write mesh solution
416  // if(problem_kind == "partitioning") {
417  // auto sol = reinterpret_cast<partitioning_problem_t *>(problem)->getSolution();
418  // MyUtils::writePartionSolution(sol.getPartListView(), ia->getLocalNumIDs(), comm);
419  // }
420  }
421 
422  return bSuccess;
423 }
424 
425 bool mainExecute(int narg, char *arg[], RCP<const Comm<int> > &comm)
426 {
428  // (0) Set up MPI environment and timer
430  int rank = comm->getRank(); // get rank
431 
433  // (1) Get and read the input file
434  // the input file defines tests to be run
436  string inputFileName("");
437  if(narg > 1)
438  inputFileName = arg[1]; // user has provided an input file
439  else{
440  if(rank == 0){
441  std::cout << "\nFAILED to specify xml input file!" << std::endl;
442  std::ostringstream msg;
443  msg << "\nStandard use of test_driver.cpp:\n";
444  msg << "mpiexec -n <procs> ./Zoltan2_test_driver.exe <input_file.xml>\n";
445  std::cout << msg.str() << std::endl;
446  }
447  return false;
448  }
449 
451  // (2) Get All Input Parameter Lists
453  queue<ParameterList> problems, comparisons;
454  if( !getParameterLists(inputFileName, problems, comparisons, comm) ) {
455  return false;
456  }
457 
459  // (3) Get Input Data Parameters
461 
462  // assumes that first block will always be the input block
463  const ParameterList inputParameters = problems.front();
464  if(inputParameters.name() != "InputParameters")
465  {
466  if(rank == 0)
467  std::cout << "InputParameters not defined. Testing FAILED." << std::endl;
468  return false;
469  }
470 
471  // get the user input for all tests
472  // UserInputForTests uinput(inputParameters,comm);
473 
474  problems.pop();
475  comm->barrier();
476 
477  bool bPass = true;
478  if(true)
479  {
481  // (4) Perform all tests
483 // pamgen debugging
484 // MyUtils::writeMesh(uinput,comm);
485 // MyUtils::getConnectivityGraph(uinput, comm);
486 
487  RCP<ComparisonHelper> comparison_manager = rcp(new ComparisonHelper);
488  while (!problems.empty()) {
489  UserInputForTests uinput(inputParameters,comm);
490 
491  if(uinput.hasInput()) {
492  if (!run(uinput, problems.front(), !comparisons.empty(),
493  comparison_manager, comm)) {
494  std::cout << "Problem run returned false" << std::endl;
495  bPass = false;
496  }
497  }
498  problems.pop();
499  }
500 
502  // (5) Compare solutions
504 
505  while (!comparisons.empty()) {
506  if (!comparison_manager->Compare(comparisons.front(),comm)) {
507  if (rank == 0) {
508  std::cout << "Comparison manager returned false so the "
509  << "test should fail." << std::endl;
510  }
511  bPass = false;
512  }
513  comparisons.pop();
514  }
515  }
516  else {
517  if(rank == 0) {
518  std::cout << "\nFAILED to load input data source. Skipping "
519  "all tests." << std::endl;
520  return false;
521  }
522  }
523 
524  return bPass;
525 }
526 
527 int main(int narg, char *arg[])
528 {
529  Tpetra::ScopeGuard tscope(&narg, &arg);
530  Teuchos::RCP<const Teuchos::Comm<int> > comm = Tpetra::getDefaultComm();
531 
532  int result = 0;
533  int rank = comm->getRank();
534  try {
535  result = mainExecute(narg, arg, comm) ? 0 : 1; // code 0 is ok,
536  // 1 is a failed test
537  }
538  catch(std::logic_error &e) {
539  // logic_error exceptions can be thrown by EvaluatePartition or
540  // MetricAnalyzer if any problem is detected in the formatting of the
541  // input xml
542  if (rank == 0) {
543  std::cout << "Test driver for rank " << rank
544  << " caught the following exception: " << e.what() << std::endl;
545  }
546  result = 1; // fail for any exception
547  }
548  catch(std::runtime_error &e) {
549  std::cout << "Test driver for rank " << rank
550  << " caught the following exception: " << e.what() << std::endl;
551  result = 1; // fail for any exception
552  }
553  catch(std::exception &e) {
554  std::cout << "Test driver for rank " << rank
555  << " caught the following exception: " << e.what() << std::endl;
556  result = 1; // fail for any exception
557  }
558 
559  // clean up - reduce the result codes
560  comm->barrier();
561  int resultReduced;
562 
563  // for a passed test all of these values should return 0 -
564  // if any result is 1 this will reduce to 1 and the test will fail
565  reduceAll<int,int>(*comm, Teuchos::EReductionType::REDUCE_MAX, 1,
566  &result, &resultReduced);
567 
568  // provide a final message which guarantees that the test will fail
569  // if any of the processes failed
570  if (rank == 0) {
571  std::cout << "Test Driver with " << comm->getSize()
572  << " processes has reduced result code " << resultReduced
573  << " and is exiting in the "
574  << ((resultReduced == 0 ) ? "PASSED" : "FAILED") << " state."
575  << std::endl;
576  }
577 
578  return result;
579 }
keep typedefs that commonly appear in many places localized
#define GET_LOCAL_ORDERING(adapterClass)
Generate input for testing purposes.
const zpart_t * getPartListView(RCP< ProblemFactory > problemFactory)
LocalOrderingSolution< zlno_t > * getLocalOrderingSolution(RCP< ProblemFactory > problemFactory)
#define ANALYZE_METRICS_PARTITIONING(adapterClass)
bool getParameterLists(const string &inputFileName, queue< ParameterList > &problems, queue< ParameterList > &comparisons, const RCP< const Teuchos::Comm< int > > &comm)
Definition: test_driver.cpp:84
typename InputTraits< User >::scalar_t scalar_t
ProblemFactory class contains 1 static factory method.
Defines Parameter related enumerators, declares functions.
static ArrayRCP< ArrayRCP< zscalar_t > > weights
ArrayRCP< lno_t > & getPermutationRCPConst(bool inverse=false) const
Get (local) permuted GIDs by const RCP.
Returns a pointer to new test classes. Is not responsible for memory management!
ArrayRCP< lno_t > & getSeparatorRangeRCPConst() const
Get separator range by const RCP.
Store and compare solution sets from different problems.
map_t::global_ordinal_type gno_t
Definition: mapRemotes.cpp:27
Provides access for Zoltan2 to Xpetra::CrsGraph data.
bool havePerm() const
Do we have the direct permutation?
int main(int narg, char **arg)
Definition: coloring1.cpp:164
size_t getLocalNumVertices() const override
Returns the number of vertices on this process.
void createAllParameters(Teuchos::ParameterList &pList)
Create a list of all Zoltan2 parameters and validators.
bool haveSeparatorRange() const
Do we have the separator range?
ProblemFactory class contains 1 static factory method.
Defines the XpetraMultiVectorAdapter.
bool analyzeMetrics(RCP< EvaluateFactory > evaluateFactory, std::ostringstream &msg, const ParameterList &problem_parameters)
Defines XpetraCrsGraphAdapter class.
Defines the XpetraCrsMatrixAdapter class.
int zpart_t
#define Z2_TEST_UPCAST(adptr, TEMPLATE_ACTION)
int getNumWeightsPerEdge() const override
Returns the number (0 or greater) of edge weights.
lno_t getNumSeparatorBlocks() const
Get number of separator column blocks.
typename InputTraits< User >::gno_t gno_t
map_t::local_ordinal_type lno_t
Definition: mapRemotes.cpp:26
bool haveSeparators() const
Do we have the separators?
A class for comparing solutions, metrics, and timing data of Zoltan2 problems.
Defines the BasicIdentifierAdapter class.
void getVertexIDsView(const gno_t *&ids) const override
void xmlToModelPList(const Teuchos::XMLObject &xml, Teuchos::ParameterList &plist)
Definition: test_driver.cpp:61
void getEdgeWeightsView(const scalar_t *&weights, int &stride, int idx) const override
Provide a pointer to the edge weights, if any.
bool haveSeparatorTree() const
Do we have the separator tree?
void getIDsView(RCP< AdapterFactory > adapterFactory, const zgno_t *&Ids)
int run(const RCP< const Comm< int > > &comm, int numGlobalParts, int testCnt, std::string *thisTest)
void getEdgesView(const offset_t *&offsets, const gno_t *&adjIds) const override
A class used to save problem solutions and timers.
#define ANALYZE_METRICS_ORDERING(adapterClass)
bool mainExecute(int narg, char *arg[], RCP< const Comm< int > > &comm)
#define EXC_ERRMSG(msg, e)
Definition: test_driver.cpp:58
#define GET_IDS_VIEW(adapterClass)
Tpetra::Map::global_ordinal_type zgno_t
typename InputTraits< User >::lno_t lno_t
ArrayRCP< lno_t > & getSeparatorTreeRCPConst() const
Get separator tree by const RCP.
#define GET_PROBLEM_PARTS(adapterClass)
Generate Adapter for testing purposes.
bool haveInverse() const
Do we have the inverse permutation?