Zoltan2
Loading...
Searching...
No Matches
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
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
43using Teuchos::ParameterList;
44using Teuchos::Comm;
45using Teuchos::RCP;
46using Teuchos::ArrayRCP;
47using Teuchos::XMLObject;
48using namespace Zoltan2_TestingFramework;
49
50using std::string;
51using std::map;
52using std::pair;
53using std::exception;
54using std::ostringstream;
55using std::queue;
56
57#define ERRMSG(msg) if (rank == 0){ std::cerr << "FAIL: " << msg << std::endl; }
58#define EXC_ERRMSG(msg, e) \
59if (rank==0){ std::cerr << "FAIL: " << msg << std::endl << e.what() << std::endl;}
60
61void 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
84bool 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
127bool 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
170const 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
178void 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
186bool 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;
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
425bool 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
527int 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}
Generate Adapter for testing purposes.
Generate input for testing purposes.
Defines the BasicIdentifierAdapter class.
Store and compare solution sets from different problems.
Returns a pointer to new test classes. Is not responsible for memory management!
Defines Parameter related enumerators, declares functions.
int zpart_t
Tpetra::Map ::global_ordinal_type zgno_t
keep typedefs that commonly appear in many places localized
#define Z2_TEST_UPCAST(adptr, TEMPLATE_ACTION)
Defines XpetraCrsGraphAdapter class.
Defines the XpetraCrsMatrixAdapter class.
Defines the XpetraMultiVectorAdapter.
int main()
A class for comparing solutions, metrics, and timing data of Zoltan2 problems.
A class used to save problem solutions and timers.
typename InputTraits< User >::lno_t lno_t
typename InputTraits< User >::scalar_t scalar_t
typename InputTraits< userTypes_t >::gno_t gno_t
ArrayRCP< index_t > & getPermutationRCPConst(bool inverse=false) const
Get (local) permuted GIDs by const RCP.
bool haveSeparators() const
Do we have the separators?
index_t getNumSeparatorBlocks() const
Get number of separator column blocks.
bool haveSeparatorTree() const
Do we have the separator tree?
bool haveSeparatorRange() const
Do we have the separator range?
bool haveInverse() const
Do we have the inverse permutation?
ArrayRCP< index_t > & getSeparatorRangeRCPConst() const
Get separator range by const RCP.
bool havePerm() const
Do we have the direct permutation?
ArrayRCP< index_t > & getSeparatorTreeRCPConst() const
Get separator tree by const RCP.
size_t getLocalNumVertices() const override
Returns the number of vertices on this process.
void getEdgeWeightsView(const scalar_t *&weights, int &stride, int idx) const override
void getEdgesView(const offset_t *&offsets, const gno_t *&adjIds) const override
int getNumWeightsPerEdge() const override
Returns the number (0 or greater) of edge weights.
void getVertexIDsView(const gno_t *&ids) const override
ProblemFactory class contains 1 static factory method.
ProblemFactory class contains 1 static factory method.
Zoltan2::XpetraCrsGraphAdapter< xcrsGraph_t, tMVector_t > xCG_xCG_t
void createAllParameters(Teuchos::ParameterList &pList)
Create a list of all Zoltan2 parameters and validators.
static ArrayRCP< ArrayRCP< zscalar_t > > weights
#define GET_PROBLEM_PARTS(adapterClass)
bool analyzeMetrics(RCP< EvaluateFactory > evaluateFactory, std::ostringstream &msg, const ParameterList &problem_parameters)
const zpart_t * getPartListView(RCP< ProblemFactory > problemFactory)
#define GET_IDS_VIEW(adapterClass)
#define ANALYZE_METRICS_ORDERING(adapterClass)
#define EXC_ERRMSG(msg, e)
LocalOrderingSolution< zlno_t > * getLocalOrderingSolution(RCP< ProblemFactory > problemFactory)
bool getParameterLists(const string &inputFileName, queue< ParameterList > &problems, queue< ParameterList > &comparisons, const RCP< const Teuchos::Comm< int > > &comm)
bool run(const UserInputForTests &uinput, const ParameterList &problem_parameters, bool bHasComparisons, RCP< ComparisonHelper > &comparison_helper, const RCP< const Teuchos::Comm< int > > &comm)
void getIDsView(RCP< AdapterFactory > adapterFactory, const zgno_t *&Ids)
bool mainExecute(int narg, char *arg[], RCP< const Comm< int > > &comm)
void xmlToModelPList(const Teuchos::XMLObject &xml, Teuchos::ParameterList &plist)
#define ANALYZE_METRICS_PARTITIONING(adapterClass)
#define GET_LOCAL_ORDERING(adapterClass)