Zoltan2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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 
14 #include <Zoltan2_TestHelpers.hpp>
19 
20 #include <Tpetra_MultiVector.hpp>
21 #include <zoltan.h>
22 
23 using Teuchos::RCP;
24 using Teuchos::rcp;
25 using Teuchos::Comm;
26 
27 //
28 // A few of the tests done by Zoltan in nightly testing.
29 //
30 
31 enum testFields {
37 };
38 
39 
40 #define NUMTESTS 30
41 static 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 
80 typedef Tpetra::CrsGraph<zlno_t, zgno_t, znode_t> tGraph_t;
81 typedef Tpetra::CrsMatrix<zscalar_t, zlno_t, zgno_t, znode_t> tMatrix_t;
82 typedef 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)
88 static 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 
99 static int znumobj(void *data, int *ierr)
100 {
101  *ierr = ZOLTAN_OK;
102  tMVector_t *vec = (tMVector_t *) data;
103  return vec->getLocalLength();
104 }
105 
106 static 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 
128 static int znumgeom(void *data, int *ierr)
129 {
130  *ierr = ZOLTAN_OK;
131  tMVector_t *cvec = (tMVector_t *) data;
132  return cvec->getNumVectors();
133 }
134 
135 static 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 
149 static 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 
158 static 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 
193 int 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 
333 for(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 
408 for(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 
461 int 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 
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)
void setRowWeights(const scalar_t *weightVal, int stride, int idx=0)
Specify a weight for each row.
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)
RCP< tcrsMatrix_t > getUITpetraCrsMatrix()
static ArrayRCP< ArrayRCP< zscalar_t > > weights
Provides access for Zoltan2 to Xpetra::CrsMatrix data.
Zoltan2::XpetraCrsMatrixAdapter< tMatrix_t, tMVector_t > matrixAdapter_t
static void zobjlist(void *data, int ngid, int nlid, ZOLTAN_ID_PTR gids, ZOLTAN_ID_PTR lids, int nwgts, float *wgts, int *ierr)
Zoltan2::EvaluatePartition< matrixAdapter_t > quality_t
int main(int narg, char **arg)
Definition: coloring1.cpp:164
Defines the PartitioningSolution class.
common code used by tests
#define NUMTESTS
static void zhgsize(void *data, int *nLists, int *nPins, int *format, int *ierr)
RCP< tMVector_t > getUIWeights()
Tpetra::CrsGraph< zlno_t, zgno_t, znode_t > tGraph_t
Zoltan2::XpetraMultiVectorAdapter< tMVector_t > vectorAdapter_t
Defines the XpetraMultiVectorAdapter.
Defines the XpetraCrsMatrixAdapter class.
RCP< const Comm< int > > getComm()
Return the communicator used by the problem.
void printTimers() const
Return the communicator passed to the problem.
An adapter for Xpetra::MultiVector.
static constexpr int znGidEnt
Tpetra::Map::local_ordinal_type zlno_t
static const std::string fail
testFields
const PartitioningSolution< Adapter > & getSolution()
Get the solution to the problem.
PartitioningProblem sets up partitioning problems for the user.
#define SET_ZID(n, a, b)
Tpetra::CrsMatrix< zscalar_t, zlno_t, zgno_t, znode_t > tMatrix_t
RCP< tMVector_t > getUICoordinates()
int run(const RCP< const Comm< int > > &comm, int numGlobalParts, int testCnt, std::string *thisTest)
Defines the PartitioningProblem class.
void setCoordinateInput(VectorAdapter< UserCoord > *coordData) override
Allow user to provide additional data that contains coordinate info associated with the MatrixAdapter...
A class that computes and returns quality metrics.
static string testArgs[]
Tpetra::MultiVector< zscalar_t, zlno_t, zgno_t, znode_t > tMVector_t
Tpetra::Map::global_ordinal_type zgno_t
void solve(bool updateInputData=true)
Direct the problem to create a solution.
std::string zoltanTestDirectory(".")
static int znumobj(void *data, int *ierr)
static int znumgeom(void *data, int *ierr)