Zoltan2
Loading...
Searching...
No Matches
zoltanCompare.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
19
20#include <Tpetra_MultiVector.hpp>
21#include <zoltan.h>
22
23using Teuchos::RCP;
24using Teuchos::rcp;
25using Teuchos::Comm;
26
27//
28// A few of the tests done by Zoltan in nightly testing.
29//
30
38
39
40#define NUMTESTS 30
41static string testArgs[] = {
42// Filename LB_Method ObjWeightDim NumProcs
43"simple", "phg", "0", "2",
44"simple", "rcb", "0", "2",
45"vwgt2", "rcb", "2", "2",
46
47"bug", "rcb", "1", "3",
48"drake", "rcb", "0", "3",
49"onedbug", "rcb", "0", "3",
50"simple", "rcb", "0", "3",
51"vwgt", "rcb", "1", "3",
52"vwgt", "phg", "1", "3",
53"vwgt2", "rcb", "2", "3",
54
55"simple", "default", "0", "4",
56"ewgt", "hsfc", "0", "4",
57"grid20x19", "hsfc", "0", "4",
58"grid20x19", "hsfc", "0", "4",
59"grid20x19", "hsfc", "0", "4",
60"nograph", "rib", "0", "4",
61"nograph", "phg", "0", "4",
62"simple", "rib", "0", "4",
63"simple", "rib", "0", "4",
64"vwgt2", "rib", "2", "4",
65"simple", "rib", "0", "4",
66"simple", "phg", "0", "4",
67
68"brack2_3", "rcb", "3", "5",
69
70"hammond2", "rcb", "2", "6",
71"degenerateAA", "rcb", "0", "6",
72"degenerate", "rcb", "0", "6",
73"degenerate", "rcb", "0", "6",
74
75"hammond", "rcb", "0", "8",
76"hammond", "phg", "0", "8",
77"vwgt2", "rcb", "2", "8"
78};
79
80typedef Tpetra::CrsGraph<zlno_t, zgno_t, znode_t> tGraph_t;
81typedef Tpetra::CrsMatrix<zscalar_t, zlno_t, zgno_t, znode_t> tMatrix_t;
82typedef Tpetra::MultiVector<zscalar_t, zlno_t, zgno_t, znode_t> tMVector_t;
86
87// Number of ZOLTAN_ID in a zgno_t (for NUM_GID_ENTRIES)
88static constexpr int znGidEnt = sizeof(zgno_t) / sizeof(ZOLTAN_ID_TYPE);
89#define SET_ZID(n,a,b) \
90 {int ZOLTAN_ID_LOOP; \
91 for (ZOLTAN_ID_LOOP = 0; ZOLTAN_ID_LOOP < (n); ZOLTAN_ID_LOOP++) \
92 (a)[ZOLTAN_ID_LOOP] = (b)[ZOLTAN_ID_LOOP]; \
93 }
94
95
97// Zoltan callbacks
98
99static int znumobj(void *data, int *ierr)
100{
101 *ierr = ZOLTAN_OK;
102 tMVector_t *vec = (tMVector_t *) data;
103 return vec->getLocalLength();
104}
105
106static void zobjlist(void *data, int ngid, int nlid,
107 ZOLTAN_ID_PTR gids, ZOLTAN_ID_PTR lids,
108 int nwgts, float *wgts, int *ierr)
109{
110 *ierr = ZOLTAN_OK;
111 tMVector_t *vec = (tMVector_t *) data;
112 size_t n = vec->getLocalLength();
113
114 for (size_t i = 0; i < n; i++) {
115 zgno_t vgid = vec->getMap()->getGlobalElement(i);
116 ZOLTAN_ID_PTR vgidptr = (ZOLTAN_ID_PTR) &vgid;
117 SET_ZID(znGidEnt, &(gids[i*znGidEnt]), vgidptr);
118 lids[i] = i;
119 }
120
121 for (int w = 0; w < nwgts; w++) {
122 ArrayRCP<const zscalar_t> wvec = vec->getData(w);
123 for (size_t i = 0; i < n; i++)
124 wgts[i*nwgts+w] = wvec[i];
125 }
126}
127
128static int znumgeom(void *data, int *ierr)
129{
130 *ierr = ZOLTAN_OK;
131 tMVector_t *cvec = (tMVector_t *) data;
132 return cvec->getNumVectors();
133}
134
135static void zgeom(void *data, int ngid, int nlid, int nobj,
136 ZOLTAN_ID_PTR gids, ZOLTAN_ID_PTR lids,
137 int ndim, double *coords, int *ierr)
138{
139 *ierr = ZOLTAN_OK;
140 tMVector_t *vec = (tMVector_t *) data;
141 for (int d = 0; d < ndim; d++) {
142 ArrayRCP<const zscalar_t> cvec = vec->getData(d);
143 for (int i = 0; i < nobj; i++) {
144 coords[lids[i]*ndim+d] = cvec[lids[i]];
145 }
146 }
147}
148
149static void zhgsize(void *data, int *nLists, int *nPins, int *format, int *ierr)
150{
151 tMatrix_t *matrix = (tMatrix_t *) data;
152 *nLists = matrix->getLocalNumRows();
153 *nPins = matrix->getLocalNumEntries();
154 *format = ZOLTAN_COMPRESSED_VERTEX;
155 *ierr = ZOLTAN_OK;
156}
157
158static void zhg(void *data, int ngid, int nLists, int nPins, int format,
159 ZOLTAN_ID_PTR listGids, int *offsets, ZOLTAN_ID_PTR pinGids,
160 int *ierr)
161{
162 tMatrix_t *matrix = (tMatrix_t *) data;
163 RCP<const tGraph_t> graph = matrix->getCrsGraph();
164 zlno_t nrows = graph->getLocalNumRows();
165
166 offsets[0] = 0;
167 for (zlno_t i = 0; i < nrows; i++) {
168
169 zgno_t tmp = graph->getRowMap()->getGlobalElement(i);
170 ZOLTAN_ID_PTR ztmp = (ZOLTAN_ID_PTR) &tmp;
171 SET_ZID(znGidEnt, &(listGids[i*znGidEnt]), ztmp);
172
173 size_t nEntries = graph->getNumEntriesInLocalRow(i);
174 offsets[i+1] = offsets[i] + nEntries;
175
176
177 typename tMatrix_t::local_inds_host_view_type colind;
178 graph->getLocalRowView(i, colind);
179
180 for (size_t j = 0; j < nEntries; j++) {
181 tmp = graph->getColMap()->getGlobalElement(colind[j]);
182 ztmp = (ZOLTAN_ID_PTR) &tmp;
183 SET_ZID(znGidEnt, &(pinGids[(offsets[i]+j)*znGidEnt]), ztmp);
184 }
185 }
186
187 *ierr = ZOLTAN_OK;
188}
189
191// Function to compute both Zoltan2 and Zoltan partitions and print metrics
192
193int run(
194 const RCP<const Comm<int> > &comm,
195 int numGlobalParts,
196 int testCnt,
197 std::string *thisTest
198)
199{
200#ifdef HAVE_ZOLTAN2_MPI
201 // Zoltan needs an MPI comm
202 const Teuchos::MpiComm<int> *tmpicomm =
203 dynamic_cast<const Teuchos::MpiComm<int> *>(comm.getRawPtr());
204 MPI_Comm mpiComm = *(tmpicomm->getRawMpiComm());
205#endif
206
207 int me = comm->getRank();
208 int np = comm->getSize();
209 double tolerance = 1.05;
210
211
213 // Read test data from Zoltan's test directory
215
216 UserInputForTests *uinput;
217 try{
219 thisTest[TESTNAMEOFFSET],
220 comm, true);
221 }
222 catch(std::exception &e){
223 if (me == 0)
224 std::cout << "Test " << testCnt << ": FAIL: UserInputForTests "
225 << e.what() << std::endl;
226 return 1;
227 }
228
229 RCP<tMatrix_t> matrix;
230 try{
231 matrix = uinput->getUITpetraCrsMatrix();
232 }
233 catch(std::exception &e){
234 if (me == 0)
235 std::cout << "Test " << testCnt << ": FAIL: get matrix "
236 << e.what() << std::endl;
237 return 1;
238 }
239
240 RCP<const tMatrix_t> matrixConst = rcp_const_cast<const tMatrix_t>(matrix);
241
242 RCP<tMVector_t> coords;
243 try{
244 coords = uinput->getUICoordinates();
245 }
246 catch(std::exception &e){
247 if (me == 0)
248 std::cout << "Test " << testCnt << ": FAIL: get coordinates "
249 << e.what() << std::endl;
250 return 1;
251 }
252
253 RCP<tMVector_t> weights;
254 try{
255 weights = uinput->getUIWeights();
256 }
257 catch(std::exception &e){
258 if (me == 0)
259 std::cout << "Test " << testCnt << ": FAIL: get weights "
260 << e.what() << std::endl;
261 return 1;
262 }
263 int nWeights = atoi(thisTest[TESTOBJWGTOFFSET].c_str());
264
265 if (me == 0) {
266 std::cout << "Test " << testCnt << " filename = "
267 << thisTest[TESTNAMEOFFSET] << std::endl;
268 std::cout << "Test " << testCnt << " num processors = "
269 << np << std::endl;
270 std::cout << "Test " << testCnt << " zoltan method = "
271 << thisTest[TESTMETHODOFFSET] << std::endl;
272 std::cout << "Test " << testCnt << " num_global_parts = "
273 << numGlobalParts << std::endl;
274 std::cout << "Test " << testCnt << " imbalance_tolerance = "
275 << tolerance << std::endl;
276 std::cout << "Test " << testCnt << " num weights per ID = "
277 << nWeights << std::endl;
278 }
279
281 // PARTITION USING ZOLTAN DIRECTLY
283
284 if (me == 0) std::cout << "Calling Zoltan directly" << std::endl;
285
286# ifdef HAVE_ZOLTAN2_MPI
287 Zoltan zz(mpiComm);
288# else
289 Zoltan zz;
290# endif
291
292 char tmp[56];
293 zz.Set_Param("LB_METHOD", thisTest[TESTMETHODOFFSET]);
294
295 sprintf(tmp, "%d", znGidEnt);
296 zz.Set_Param("NUM_GID_ENTRIES", tmp);
297 sprintf(tmp, "%d", numGlobalParts);
298 zz.Set_Param("NUM_GLOBAL_PARTS", tmp);
299 sprintf(tmp, "%d", nWeights);
300 zz.Set_Param("OBJ_WEIGHT_DIM", tmp);
301 sprintf(tmp, "%f", tolerance);
302 zz.Set_Param("IMBALANCE_TOL", tmp);
303 zz.Set_Param("RETURN_LISTS", "PART");
304 zz.Set_Param("FINAL_OUTPUT", "1");
305 zz.Set_Param("SEED", "1111");
306 zz.Set_Param("LB_APPROACH", "PARTITION");
307
308 zz.Set_Num_Obj_Fn(znumobj, (void *) coords.getRawPtr());
309 if (nWeights)
310 zz.Set_Obj_List_Fn(zobjlist, (void *) weights.getRawPtr());
311 else
312 zz.Set_Obj_List_Fn(zobjlist, (void *) coords.getRawPtr());
313 zz.Set_Num_Geom_Fn(znumgeom, (void *) coords.getRawPtr());
314 zz.Set_Geom_Multi_Fn(zgeom, (void *) coords.getRawPtr());
315 zz.Set_HG_Size_CS_Fn(zhgsize, (void *) &(*matrix));
316 zz.Set_HG_CS_Fn(zhg, (void *) &(*matrix));
317
318 int changes, ngid, nlid;
319 int numd, nump;
320 ZOLTAN_ID_PTR dgid = NULL, dlid = NULL, pgid = NULL, plid = NULL;
321 int *dproc = NULL, *dpart = NULL, *pproc = NULL, *ppart = NULL;
322
323 int ierr = zz.LB_Partition(changes, ngid, nlid,
324 numd, dgid, dlid, dproc, dpart,
325 nump, pgid, plid, pproc, ppart);
326 if (ierr != ZOLTAN_OK && ierr != ZOLTAN_WARN) {
327 if (me == 0)
328 std::cout << "Test " << testCnt << ": FAIL: direct Zoltan call" << std::endl;
329 zz.LB_Free_Part(&pgid, &plid, &pproc, &ppart);
330 return 1;
331 }
332
333for(int i = 0; i < nump; i++) {
334 std::cout << me << " KDD Z1 " << pgid[i] << " " << plid[i] << " " << ppart[i] << std::endl;
335}
336
338 // PARTITION USING ZOLTAN THROUGH ZOLTAN2
340
341 if (me == 0) std::cout << "Calling Zoltan through Zoltan2" << std::endl;
342
343 matrixAdapter_t *ia;
344 try{
345 ia = new matrixAdapter_t(matrixConst, nWeights);
346 }
347 catch(std::exception &e){
348 if (me == 0)
349 std::cout << "Test " << testCnt << ": FAIL: matrix adapter "
350 << e.what() << std::endl;
351 return 1;
352 }
353 for (int idx=0; idx < nWeights; idx++)
354 ia->setRowWeights(weights->getData(idx).getRawPtr(), 1, idx);
355
356 vectorAdapter_t *ca = NULL;
357 try{
358 ca = new vectorAdapter_t(coords);
359 }
360 catch(std::exception &e){
361 if (me == 0)
362 std::cout << "Test " << testCnt << ": FAIL: vector adapter "
363 << e.what() << std::endl;
364 return 1;
365 }
366 ia->setCoordinateInput(ca);
367
368 Teuchos::ParameterList params;
369 params.set("timer_output_stream" , "std::cout");
370 // params.set("debug_level" , "verbose_detailed_status");
371
372 params.set("algorithm", "zoltan");
373 params.set("imbalance_tolerance", tolerance );
374 params.set("num_global_parts", numGlobalParts);
375
376 if (thisTest[TESTMETHODOFFSET] != "default") {
377 // "default" tests case of no Zoltan parameter sublist
378 Teuchos::ParameterList &zparams = params.sublist("zoltan_parameters",false);
379 zparams.set("LB_METHOD",thisTest[TESTMETHODOFFSET]);
380 zparams.set("LB_APPROACH", "PARTITION");
381 zparams.set("SEED", "1111");
382 }
383
385# ifdef HAVE_ZOLTAN2_MPI
386 try{
387 problem = new Zoltan2::PartitioningProblem<matrixAdapter_t>(ia, &params,
388 mpiComm);
389 }
390# else
391 try{
392 problem = new Zoltan2::PartitioningProblem<matrixAdapter_t>(ia, &params);
393 }
394# endif
395 catch(std::exception &e){
396 std::cout << "Test " << testCnt << " FAIL: problem " << e.what() << std::endl;
397 return 1;
398 }
399
400 try {
401 problem->solve();
402 }
403 catch(std::exception &e){
404 std::cout << "Test " << testCnt << " FAIL: solve " << e.what() << std::endl;
405 return 1;
406 }
407
408for(int i = 0; i < nump; i++) {
409 std::cout << me << " KDD Z2 " << coords->getMap()->getGlobalElement(i) << " " << i << " " << problem->getSolution().getPartListView()[i] << std::endl;
410}
411
412 // create metric object
413
414 RCP<quality_t>metricObject = rcp(new quality_t(ia,&params,problem->getComm(),
415 &problem->getSolution()));
416 if (me == 0){
417 metricObject->printMetrics(std::cout);
418 }
419 problem->printTimers();
420
422 // COMPARE RESULTS
424 size_t nObj = coords->getLocalLength();
425 const int *z2parts = problem->getSolution().getPartListView();
426 int diffcnt = 0, gdiffcnt = 0;
427 for (size_t i = 0; i < nObj; i++) {
428 if (z2parts[plid[i]] != ppart[i]) {
429 diffcnt++;
430 std::cout << me << " DIFF for " << i << " ("
431 << coords->getMap()->getGlobalElement(i) << "): "
432 << "Z2 = " << z2parts[i] << "; Z1 = " << ppart[plid[i]] << std::endl;
433 }
434 }
435
437 // CLEAN UP
439 zz.LB_Free_Part(&pgid, &plid, &pproc, &ppart);
440 delete ia;
441 delete ca;
442 delete problem;
443 delete uinput;
444
445 Teuchos::reduceAll(*comm, Teuchos::REDUCE_SUM, 1, &diffcnt, &gdiffcnt);
446 if (gdiffcnt > 0) {
447 if (me == 0)
448 std::cout << "Test " << testCnt << " "
449 << thisTest[TESTNAMEOFFSET] << " "
450 << thisTest[TESTMETHODOFFSET] << " "
451 << thisTest[TESTOBJWGTOFFSET] << " "
452 << " FAIL: comparison " << std::endl;
453 return 1;
454 }
455
456 return 0;
457}
458
460
461int main(int narg, char *arg[])
462{
463 Tpetra::ScopeGuard tscope(&narg, &arg);
464 Teuchos::RCP<const Teuchos::Comm<int> > comm = Tpetra::getDefaultComm();
465
466 int me = comm->getRank();
467 int np = comm->getSize();
468
469 int fail=0;
470
471 Array<int> ranks(np);
472 for (int i = 0; i < np; i++) ranks[i] = i;
473
474 for (int i=0; i < NUMTESTS; i++) {
475 std::string *thisTest = &(testArgs[i*TESTNUMARGS]);
476 int nTestProcs = atoi(thisTest[TESTNUMPROCS].c_str());
477 if (nTestProcs > np) {
478 if (me == 0) {
479 std::cout << "Skipping test " << i << " on "
480 << thisTest[TESTNAMEOFFSET]
481 << "; required number of procs " << nTestProcs
482 << " is greater than available procs " << np << std::endl;
483 }
484 continue;
485 }
486
487 // Make a communicator of appropriate size for the test
488 RCP<const Comm<int> > testcomm;
489 if (nTestProcs == np)
490 testcomm = comm;
491 else
492 testcomm = comm->createSubcommunicator(ranks.view(0,nTestProcs));
493
494 // Run the test if in the communicator
495 if (me < nTestProcs) {
496 fail += run(testcomm, nTestProcs, i, thisTest);
497 }
498 }
499
500 if (me == 0 && !fail)
501 std::cout << "PASS" << std::endl;
502
503 return 0;
504}
505
Defines the PartitioningProblem class.
Defines the PartitioningSolution class.
common code used by tests
std::string zoltanTestDirectory(".")
Tpetra::Map ::local_ordinal_type zlno_t
Tpetra::Map ::global_ordinal_type zgno_t
Defines the XpetraCrsMatrixAdapter class.
Defines the XpetraMultiVectorAdapter.
int main()
RCP< tMVector_t > getUICoordinates()
RCP< tcrsMatrix_t > getUITpetraCrsMatrix()
RCP< tMVector_t > getUIWeights()
A class that computes and returns quality metrics.
void setCoordinateInput(VectorAdapter< UserCoord > *coordData) override
Allow user to provide additional data that contains coordinate info associated with the MatrixAdapter...
PartitioningProblem sets up partitioning problems for the user.
const PartitioningSolution< Adapter > & getSolution()
Get the solution to the problem.
void solve(bool updateInputData=true)
Direct the problem to create a solution.
RCP< const Comm< int > > getComm()
Return the communicator used by the problem.
void printTimers() const
Return the communicator passed to the problem.
Provides access for Zoltan2 to Xpetra::CrsMatrix data.
void setRowWeights(const scalar_t *weightVal, int stride, int idx=0)
Specify a weight for each row.
static const std::string fail
static ArrayRCP< ArrayRCP< zscalar_t > > weights
static void zobjlist(void *data, int ngid, int nlid, ZOLTAN_ID_PTR gids, ZOLTAN_ID_PTR lids, int nwgts, float *wgts, int *ierr)
static int znumobj(void *data, int *ierr)
Tpetra::MultiVector< zscalar_t, zlno_t, zgno_t, znode_t > tMVector_t
Tpetra::CrsMatrix< zscalar_t, zlno_t, zgno_t, znode_t > tMatrix_t
#define NUMTESTS
static void zhgsize(void *data, int *nLists, int *nPins, int *format, int *ierr)
static int znumgeom(void *data, int *ierr)
Tpetra::CrsGraph< zlno_t, zgno_t, znode_t > tGraph_t
Zoltan2::EvaluatePartition< matrixAdapter_t > quality_t
testFields
@ TESTNUMARGS
@ TESTNAMEOFFSET
@ TESTMETHODOFFSET
@ TESTOBJWGTOFFSET
@ TESTNUMPROCS
Zoltan2::XpetraCrsMatrixAdapter< tMatrix_t, tMVector_t > matrixAdapter_t
static string testArgs[]
static void zgeom(void *data, int ngid, int nlid, int nobj, ZOLTAN_ID_PTR gids, ZOLTAN_ID_PTR lids, int ndim, double *coords, int *ierr)
static constexpr int znGidEnt
static void zhg(void *data, int ngid, int nLists, int nPins, int format, ZOLTAN_ID_PTR listGids, int *offsets, ZOLTAN_ID_PTR pinGids, int *ierr)
int run(const RCP< const Comm< int > > &comm, int numGlobalParts, int testCnt, std::string *thisTest)
#define SET_ZID(n, a, b)
Zoltan2::XpetraMultiVectorAdapter< tMVector_t > vectorAdapter_t