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#include <Teuchos_StackedTimer.hpp>
37
38#include <sstream>
39#include <string>
40#include <map>
41#include <iostream>
42#include <queue>
43
44using Teuchos::ParameterList;
45using Teuchos::Comm;
46using Teuchos::RCP;
47using Teuchos::ArrayRCP;
48using Teuchos::XMLObject;
49using namespace Zoltan2_TestingFramework;
50
51using std::string;
52using std::map;
53using std::pair;
54using std::exception;
55using std::ostringstream;
56using std::queue;
57
58#define ERRMSG(msg) if (rank == 0){ std::cerr << "FAIL: " << msg << std::endl; }
59#define EXC_ERRMSG(msg, e) \
60if (rank==0){ std::cerr << "FAIL: " << msg << std::endl << e.what() << std::endl;}
61
62void xmlToModelPList(const Teuchos::XMLObject &xml,
63 Teuchos::ParameterList & plist)
64{
65 // This method composes a plist for the problem
66 Teuchos::XMLParameterListReader reader;
67 plist = reader.toParameterList(xml);
68
69 // Get list of valid Zoltan2 Parameters
70 // Zoltan 2 parameters appear in the input file
71 // Right now we have default values stored in
72 // the parameter list, we would like to apply
73 // the options specified by the user in their
74 // input file
75 Teuchos::ParameterList zoltan2Parameters;
76 Zoltan2::createAllParameters(zoltan2Parameters);
77
78 if (plist.isSublist("Zoltan2Parameters")) {
79 // Apply user specified zoltan2Parameters
80 ParameterList &sub = plist.sublist("Zoltan2Parameters");
81 zoltan2Parameters.setParameters(sub);
82 }
83}
84
85bool getParameterLists(const string &inputFileName,
86 queue<ParameterList> &problems,
87 queue<ParameterList> &comparisons,
88 const RCP<const Teuchos::Comm<int> > & comm)
89{
90 int rank = comm->getRank();
91 // return a parameter list of problem definitions
92 // and a parameter list for solution comparisons
93 Teuchos::FileInputSource inputSource(inputFileName);
94 if(rank == 0) {
95 std::cout << "Input file source: " << inputFileName << std::endl;
96 }
97 XMLObject xmlInput;
98
99 // Try to get xmlObject from inputfile
100 try
101 {
102 xmlInput = inputSource.getObject();
103 }
104 catch(exception &e)
105 {
106 EXC_ERRMSG("Test Driver error: reading", e); // error reading input
107 return false;
108 }
109
110 // get the parameter lists for each model
111 for(int i = 0; i < xmlInput.numChildren(); i++)
112 {
113 ParameterList plist;
114 xmlToModelPList(xmlInput.getChild(i), plist);
115
116 if(plist.name() == "Comparison") {
117 comparisons.emplace(plist);
118 }
119 else {
120 problems.emplace(plist);
121 }
122 }
123
124 return true;
125}
126
127// Utility function for safe type conversion of adapter
128bool analyzeMetrics(RCP<EvaluateFactory> evaluateFactory,
129 std::ostringstream & msg,
130 const ParameterList &problem_parameters) {
131 #define ANALYZE_METRICS(adapterClass, metricAnalyzerClass) \
132 RCP<EvaluateBaseClass<adapterClass>> pCast = \
133 rcp_dynamic_cast<EvaluateBaseClass<adapterClass>>( \
134 evaluateFactory->getEvaluateClass()); \
135 if(pCast == Teuchos::null) throw std::logic_error( \
136 "Bad evaluate class cast in analyzeMetrics!" ); \
137 metricAnalyzerClass analyzer(pCast); \
138 return analyzer.analyzeMetrics( \
139 problem_parameters.sublist("Metrics"), msg);
140
141 #define ANALYZE_METRICS_PARTITIONING(adapterClass) \
142 ANALYZE_METRICS(adapterClass, \
143 MetricAnalyzerEvaluatePartition<adapterClass>)
144
145 #define ANALYZE_METRICS_ORDERING(adapterClass) \
146 ANALYZE_METRICS(adapterClass, \
147 MetricAnalyzerEvaluateOrdering<adapterClass>)
148
149 if(evaluateFactory->getProblemName() == "partitioning") {
150 Z2_TEST_UPCAST(evaluateFactory->getAdapterType(), ANALYZE_METRICS_PARTITIONING)
151 }
152 else if(evaluateFactory->getProblemName() == "ordering") {
153 Z2_TEST_UPCAST(evaluateFactory->getAdapterType(), ANALYZE_METRICS_ORDERING)
154 }
155 else {
156 throw std::logic_error(
157 "analyzeMetrics not implemented for this problem type!" );
158 }
159}
160
161// Utility function for safe type conversion of adapter
163 RCP<ProblemFactory> problemFactory) {
164 #define GET_LOCAL_ORDERING(adapterClass) \
165 return (rcp_dynamic_cast<OrderingProblem<adapterClass>>( \
166 problemFactory->getProblem()))->getLocalOrderingSolution();
167 Z2_TEST_UPCAST(problemFactory->getAdapterType(), GET_LOCAL_ORDERING)
168}
169
170// Utility function for safe type conversion of adapter
171const zpart_t * getPartListView(RCP<ProblemFactory> problemFactory) {
172 #define GET_PROBLEM_PARTS(adapterClass) \
173 return (rcp_dynamic_cast<PartitioningProblem<adapterClass>>( \
174 problemFactory->getProblem()))->getSolution().getPartListView();
175 Z2_TEST_UPCAST(problemFactory->getAdapterType(), GET_PROBLEM_PARTS)
176}
177
178// Utility function for safe type conversion of adapter
179void getIDsView(RCP<AdapterFactory> adapterFactory, const zgno_t *&Ids) {
180 #define GET_IDS_VIEW(adapterClass) \
181 return dynamic_cast<adapterClass*>( \
182 adapterFactory->getMainAdapter())->getIDsView(Ids);
183 Z2_TEST_UPCAST(adapterFactory->getMainAdapterType(), GET_IDS_VIEW);
184 throw std::logic_error( "getIDsView() failed to match adapter name" );
185}
186
187bool run(const UserInputForTests &uinput,
188 const ParameterList &problem_parameters,
189 bool bHasComparisons,
190 RCP<ComparisonHelper> & comparison_helper,
191 const RCP<const Teuchos::Comm<int> > & comm)
192{
193 // Major steps in running a problem in zoltan 2
194 // 1. get an input adapter
195 // 2. construct the problem
196 // 3. solve the problem
197 // 4. analyze metrics
198 // 5. clean up
199
200 int rank = comm->getRank();
201 if(!problem_parameters.isParameter("kind"))
202 {
203 if(rank == 0) {
204 std::cout << "Problem kind not provided" << std::endl;
205 }
206 return false;
207 }
208 if(!problem_parameters.isParameter("InputAdapterParameters"))
209 {
210 if(rank == 0) {
211 std::cout << "Input adapter parameters not provided" << std::endl;
212 }
213 return false;
214 }
215 if(!problem_parameters.isParameter("Zoltan2Parameters"))
216 {
217 if(rank == 0) {
218 std::cout << "Zoltan2 problem parameters not provided" << std::endl;
219 }
220 return false;
221 }
222
223 if(rank == 0) {
224 std::cout << "\n\nRunning test: " << problem_parameters.name() << std::endl;
225 }
226
228 // 0. add comparison source
230 auto comparison_source = comparison_helper->AddSource(problem_parameters.name());
231
232 comparison_source->startBaseTimer();
233
235 // 1. get basic input adapter
237 const ParameterList &adapterPlist =
238 problem_parameters.sublist("InputAdapterParameters");
239 comparison_source->startTimer("adapter construction time");
240
241 // a pointer to a basic type
242 RCP<AdapterFactory> adapterFactory = rcp(new AdapterFactory(
243 const_cast<UserInputForTests*>(&uinput), adapterPlist, comm));
244
245 comparison_source->stopTimer("adapter construction time");
246
247 comparison_source->adapterFactory = adapterFactory; // saves until done
248
250 // 2. construct a Zoltan2 problem
252 // If we are here we have an input adapter, no need to check for one.
253 string adapter_name = adapterPlist.get<string>("input adapter");
254 // get Zoltan2 Parameters
255 ParameterList zoltan2_parameters =
256 const_cast<ParameterList &>(problem_parameters.sublist("Zoltan2Parameters"));
257 if(rank == 0) {
258 std::cout << std::endl;
259 }
260
261 comparison_source->startTimer("problem construction time");
262 std::string problem_kind = problem_parameters.get<std::string>("kind");
263 if (rank == 0) {
264 std::cout << "Creating a new " << problem_kind << " problem." << std::endl;
265 }
266
267 RCP<ProblemFactory> problemFactory = rcp(new ProblemFactory(
268 problem_kind,
269 adapterFactory,
270 &zoltan2_parameters
271 #ifdef HAVE_ZOLTAN2_MPI
272 ,MPI_COMM_WORLD
273 #endif
274 ));
275
276 if(rank == 0) {
277 std::cout << "Using input adapter type: " + adapter_name << std::endl;
278 }
279
280 comparison_source->stopTimer("problem construction time");
281
282 comparison_source->problemFactory = problemFactory; // saves until done
283
285 // 3. Solve the problem
287 comparison_source->startTimer("solve time");
288
289 problemFactory->getProblem()->solve();
290
291 comparison_source->stopTimer("solve time");
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->stopBaseTimer();
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{
427 Teuchos::RCP<Teuchos::StackedTimer> stacked_timer = Teuchos::rcp(new Teuchos::StackedTimer("Zoltan2_Driver"));;
428
430 // (0) Set up MPI environment and timer
432 int rank = comm->getRank(); // get rank
433
435 // (1) Get and read the input file
436 // the input file defines tests to be run
438 string inputFileName("");
439 if(narg > 1)
440 inputFileName = arg[1]; // user has provided an input file
441 else{
442 if(rank == 0){
443 std::cout << "\nFAILED to specify xml input file!" << std::endl;
444 std::ostringstream msg;
445 msg << "\nStandard use of test_driver.cpp:\n";
446 msg << "mpiexec -n <procs> ./Zoltan2_test_driver.exe <input_file.xml>\n";
447 std::cout << msg.str() << std::endl;
448 }
449 return false;
450 }
451
453 // (2) Get All Input Parameter Lists
455 queue<ParameterList> problems, comparisons;
456 if( !getParameterLists(inputFileName, problems, comparisons, comm) ) {
457 return false;
458 }
459
461 // (3) Get Input Data Parameters
463
464 // assumes that first block will always be the input block
465 const ParameterList inputParameters = problems.front();
466 if(inputParameters.name() != "InputParameters")
467 {
468 if(rank == 0)
469 std::cout << "InputParameters not defined. Testing FAILED." << std::endl;
470 return false;
471 }
472
473 // get the user input for all tests
474 // UserInputForTests uinput(inputParameters,comm);
475
476 problems.pop();
477 comm->barrier();
478
479 bool bPass = true;
480 if(true)
481 {
483 // (4) Perform all tests
485// pamgen debugging
486// MyUtils::writeMesh(uinput,comm);
487// MyUtils::getConnectivityGraph(uinput, comm);
488
489 RCP<ComparisonHelper> comparison_manager = rcp(new ComparisonHelper(stacked_timer));
490 while (!problems.empty()) {
491 UserInputForTests uinput(inputParameters,comm);
492
493 if(uinput.hasInput()) {
494 if (!run(uinput, problems.front(), !comparisons.empty(),
495 comparison_manager, comm)) {
496 std::cout << "Problem run returned false" << std::endl;
497 bPass = false;
498 }
499 }
500 problems.pop();
501 }
502
504 // (5) Compare solutions
506
507 while (!comparisons.empty()) {
508 if (!comparison_manager->Compare(comparisons.front(),comm)) {
509 if (rank == 0) {
510 std::cout << "Comparison manager returned false so the "
511 << "test should fail." << std::endl;
512 }
513 bPass = false;
514 }
515 comparisons.pop();
516 }
517 }
518 else {
519 if(rank == 0) {
520 std::cout << "\nFAILED to load input data source. Skipping "
521 "all tests." << std::endl;
522 return false;
523 }
524 }
525
526 stacked_timer->stopBaseTimer();
527 Teuchos::StackedTimer::OutputOptions options;
528 options.output_fraction = options.output_histogram = options.output_minmax = true;
529 stacked_timer->report(std::cout, comm, options);
530 std::string watchrProblemName = std::string("Zoltan2 test driver ") + std::to_string(comm->getSize()) + " ranks";
531 auto xmlOut = stacked_timer->reportWatchrXML(watchrProblemName, comm);
532 if (xmlOut.length())
533 std::cout << "\nAlso created Watchr performance report " << xmlOut << '\n';
534
535 return bPass;
536}
537
538int main(int narg, char *arg[])
539{
540 Tpetra::ScopeGuard tscope(&narg, &arg);
541 Teuchos::RCP<const Teuchos::Comm<int> > comm = Tpetra::getDefaultComm();
542
543 int result = 0;
544 int rank = comm->getRank();
545 try {
546 result = mainExecute(narg, arg, comm) ? 0 : 1; // code 0 is ok,
547 // 1 is a failed test
548 }
549 catch(std::logic_error &e) {
550 // logic_error exceptions can be thrown by EvaluatePartition or
551 // MetricAnalyzer if any problem is detected in the formatting of the
552 // input xml
553 if (rank == 0) {
554 std::cout << "Test driver for rank " << rank
555 << " caught the following exception: " << e.what() << std::endl;
556 }
557 result = 1; // fail for any exception
558 }
559 catch(std::runtime_error &e) {
560 std::cout << "Test driver for rank " << rank
561 << " caught the following exception: " << e.what() << std::endl;
562 result = 1; // fail for any exception
563 }
564 catch(std::exception &e) {
565 std::cout << "Test driver for rank " << rank
566 << " caught the following exception: " << e.what() << std::endl;
567 result = 1; // fail for any exception
568 }
569
570 // clean up - reduce the result codes
571 comm->barrier();
572 int resultReduced;
573
574 // for a passed test all of these values should return 0 -
575 // if any result is 1 this will reduce to 1 and the test will fail
576 reduceAll<int,int>(*comm, Teuchos::EReductionType::REDUCE_MAX, 1,
577 &result, &resultReduced);
578
579 // provide a final message which guarantees that the test will fail
580 // if any of the processes failed
581 if (rank == 0) {
582 std::cout << "Test Driver with " << comm->getSize()
583 << " processes has reduced result code " << resultReduced
584 << " and is exiting in the "
585 << ((resultReduced == 0 ) ? "PASSED" : "FAILED") << " state."
586 << std::endl;
587 }
588
589 return result;
590}
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.
typename InputTraits< User >::lno_t lno_t
typename InputTraits< User >::scalar_t scalar_t
typename InputTraits< User >::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.
Provides access for Zoltan2 to Xpetra::CrsGraph data.
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
Provide a pointer to the edge weights, if any.
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.
map_t::local_ordinal_type lno_t
map_t::global_ordinal_type gno_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)