Zoltan2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Mapping.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 //
11 // Test the MappingProblem and MappingSolution classes.
12 //
13 
14 #include <Teuchos_ParameterList.hpp>
15 #include <Teuchos_CommHelpers.hpp>
16 
17 #include <Zoltan2_TestHelpers.hpp>
19 
22 
24 
26 template <typename User>
28 {
29 public:
30  // Defines an (np x nCoordPerRank) grid of points
31  // Total number of parts is np * nPartsPerRow;
32  // Half of each row of points belongs to a single part.
33  // Lowest part number is lowestPartNum.
38 
39  // Problem dimensions are fixed
40  static const int nCoordDim = 2;
41  static const int nCoordPerRank = 6;
42 
43  // Constructor
45  const Teuchos::Comm<int> &comm_,
46  int nPartsPerRow_,
47  int lowestPartNum_,
48  bool useInputParts_=false)
49  :
50  me(comm_.getRank()),
51  np(comm_.getSize()),
52  nPartsPerRow(nPartsPerRow_),
53  lowestPartNum(lowestPartNum_),
54  useInputParts(useInputParts_)
55  {
56  for (int j = 0; j < nCoordPerRank; j++)
57  ids[j] = me * nCoordPerRank + j;
58 
59  for (int i = 0, j = 0; i < nPartsPerRow; i++)
60  for (int k = 0; k < nCoordPerRank / nPartsPerRow; k++, j++)
61  inputparts[j] = lowestPartNum + i + me*nPartsPerRow;
62 
63  for (int j = 0; j < nCoordPerRank; j++) {
64  coords[0][j] = scalar_t(j);
65  coords[1][j] = scalar_t(me);
66  if (nCoordDim > 2) coords[2][j] = scalar_t(np);
67  }
68  }
69 
70  // Print the data as received by the methods.
71  void print(std::string hi) {
72  // print ids
73  const gno_t *mids;
74  getIDsView(mids);
75  std::cout << hi << " methods Rank " << me << " ids: ";
76  for (size_t j = 0; j < getLocalNumIDs(); j++) std::cout << mids[j] << " ";
77  std::cout << std::endl;
78 
79  // print coords
80  const scalar_t **mcoords = new const scalar_t*[getNumEntriesPerID()];
81  int *stride = new int[getNumEntriesPerID()];
82  for (int k = 0; k < getNumEntriesPerID(); k++)
83  getEntriesView(mcoords[k], stride[k], k);
84 
85  std::cout << hi << " methods Rank " << me << " coords: ";
86  for (size_t j = 0; j < getLocalNumIDs(); j++) {
87  std::cout << "(";
88  for (int k = 0; k < getNumEntriesPerID(); k++)
89  std::cout << mcoords[k][j*stride[k]] << ",";
90  std::cout << ") ";
91  }
92  std::cout << std::endl;
93  delete [] mcoords;
94  delete [] stride;
95 
96  // print inputparts
97  const part_t *minputparts;
98  getPartsView(minputparts);
99  std::cout << hi << " methods Rank " << me << " parts: ";
100  if (minputparts != NULL)
101  for (size_t j = 0; j < getLocalNumIDs(); j++)
102  std::cout << minputparts[j] << " ";
103  else
104  std::cout << "not provided";
105  std::cout << std::endl;
106  }
107 
108  // Return values given to the constructor
109  bool adapterUsesInputParts() { return useInputParts; }
110  int adapterNPartsPerRow() { return nPartsPerRow; }
111  int adapterLowestPartNum() { return lowestPartNum; }
112 
113  // Methods for the VectorAdapter interface
114  size_t getLocalNumIDs() const { return nCoordPerRank; }
115  void getIDsView(const gno_t *&Ids) const { Ids = ids; }
116 
117  int getNumEntriesPerID() const { return nCoordDim; }
118  void getEntriesView(const scalar_t *&Coords, int &Stride, int Idx) const
119  {
120  Coords = coords[Idx];
121  Stride = 1;
122  }
123 
124  void getPartsView(const part_t *&InputPart) const {
125  if (useInputParts)
126  InputPart = inputparts;
127  else
128  InputPart = NULL;
129  }
130 
131 private:
132  int me; // my rank
133  int np; // number of ranks
134  int nPartsPerRow; // number of parts created per row of the grid
135  int lowestPartNum; // lowest part number; not necessarily zero
136  bool useInputParts; // use the input partition in the tests? If not,
137  // Zoltan2 uses rank as default part number
138  gno_t ids[nCoordPerRank]; // IDs of this rank's grid points
139  part_t inputparts[nCoordPerRank]; // Input part for each local point
140  scalar_t coords[nCoordDim][nCoordPerRank]; // Coordinate for each local point
141 };
142 
144 // Validate a mapping solution obtained without a partitioning solution
145 template <typename Adapter>
148  Adapter &ia,
149  const Teuchos::Comm<int> &comm
150 )
151 {
152  // Correctness of a particular mapping algorithm is beyond the scope of
153  // this test.
154  typedef typename Adapter::part_t part_t;
155 
156  bool aok = true;
157 
158  // All returned processors must be in range [0,np-1].
159 
160  if (ia.adapterUsesInputParts()) {
161  // Adapter provided input parts
162  const part_t *inputParts;
163  ia.getPartsView(inputParts);
164 
165  aok = validMappingSolution(msoln, ia, inputParts, comm);
166  }
167  else {
168  // Adapter did not provide input parts; use default part = me for all IDs
169  int me = comm.getRank();
170 
171  part_t *defaultParts = new part_t[ia.getLocalNumIDs()];
172  for (size_t i = 0; i < ia.getLocalNumIDs(); i++)
173  defaultParts[i] = me;
174 
175  aok = validMappingSolution(msoln, ia, defaultParts, comm);
176 
177  delete [] defaultParts;
178  }
179 
180  return aok;
181 }
182 
184 // Validate a mapping solution obtained with a partitioning solution
185 template <typename Adapter>
188  Adapter &ia,
190  const Teuchos::Comm<int> &comm
191 )
192 {
193  typedef typename Adapter::part_t part_t;
194 
195  const part_t *assignedParts = psoln.getPartListView();
196 
197  return(validMappingSolution(msoln, ia, assignedParts, comm));
198 }
199 
201 // Validate a mapping solution.
202 // This test checks only whether the mapping solution is valid. Correctness
203 // of a particular mapping algorithm is beyond the scope of this test.
204 template <typename Adapter>
207  Adapter &ia,
208  const typename Adapter::part_t *useTheseParts,
209  // Parts to check for correct mapping to ranks;
210  // may be from Adapter,
211  // from PartitioningSolution, or
212  // default to current rank
213  const Teuchos::Comm<int> &comm
214 )
215 {
216  typedef typename Adapter::part_t part_t;
217 
218  int np = comm.getSize();
219 
220  bool aok = true;
221 
222  // Min and max part numbers in useTheseParts
223  part_t globalmin, localmin = std::numeric_limits<part_t>::max();
224  part_t globalmax, localmax = 0;
225 
226  for (size_t i = 0; i < ia.getLocalNumIDs(); i++) {
227 
228  // All returned processors must be in range [0,np-1].
229  int r = msoln.getRankForPart(useTheseParts[i]);
230  if (r < 0 || r >= np) {
231  aok = false;
232  std::cout << __FILE__ << ":" << __LINE__ << " "
233  << "Invalid rank " << r << " of " << np << " returned"
234  << std::endl;
235  }
236 
237  // Find local max/min part number
238  part_t p = useTheseParts[i];
239  if (p > localmax) localmax = p;
240  if (p < localmin) localmin = p;
241  }
242 
243  // Find global max/min part number
244  Teuchos::reduceAll<int, part_t>(comm, Teuchos::REDUCE_MAX, 1,
245  &localmax, &globalmax);
246  Teuchos::reduceAll<int, part_t>(comm, Teuchos::REDUCE_MIN, 1,
247  &localmin, &globalmin);
248 
249  part_t *parts;
250  part_t nParts;
251  msoln.getMyPartsView(nParts, parts);
252 
253  for (part_t i = 0; i < nParts; i++) {
254 
255  // All returned parts must at least be in the range of part numbers
256  // from useTheseParts
257  part_t p = parts[i];
258  if ((p < globalmin) || (p > globalmax)) {
259  aok = false;
260  std::cout << __FILE__ << ":" << __LINE__ << " "
261  << "Invalid part " << p << " of " << np << " returned"
262  << std::endl;
263  }
264  }
265 
266  // Test the error checking in mapping solution;
267  // each test should throw an error.
268 
269  part_t ret;
270  bool errorThrownCorrectly = false;
271  part_t sillyPart = globalmax+10;
272  try {
273  ret = msoln.getRankForPart(sillyPart);
274  }
275  catch (std::exception &e) {
276  errorThrownCorrectly = true;
277  }
278  if (errorThrownCorrectly == false) {
279  aok = false;
280  std::cout << __FILE__ << ":" << __LINE__ << " "
281  << "Mapping Solution accepted a too-high part number "
282  << sillyPart << " returned " << ret << std::endl;
283  }
284 
285  errorThrownCorrectly = false;
286  sillyPart = globalmin - 1;
287  try {
288  ret = msoln.getRankForPart(sillyPart);
289  }
290  catch (std::exception &e) {
291  errorThrownCorrectly = true;
292  }
293  if (errorThrownCorrectly == false) {
294  aok = false;
295  std::cout << __FILE__ << ":" << __LINE__ << " "
296  << "Mapping Solution accepted a too-low part number "
297  << sillyPart << " returned " << ret << std::endl;
298  }
299 
300  return aok;
301 }
302 
304 
305 template <typename Adapter>
306 bool runTest(
307  Adapter &ia,
308  const RCP<const Teuchos::Comm<int> > &comm,
309  std::string hi
310 )
311 {
312  typedef typename Adapter::part_t part_t;
313  typedef typename Adapter::scalar_t scalar_t;
314 
315  int me = comm->getRank();
316  int np = comm->getSize();
317 
318  bool allgood = true;
319 
320  Teuchos::ParameterList params;
321  params.set("mapping_algorithm", "block");
322 
323  // Test mapping using default machine
324  if (me == 0)
325  std::cout << "Testing Mapping using default machine" << std::endl;
326 
327  Zoltan2::MappingProblem<Adapter> mprob1(&ia, &params, comm);
328  mprob1.solve();
329 
331 
332  if (!validMappingSolution<Adapter>(*msoln1, ia, *comm)) {
333  allgood = false;
334  if (me == 0)
335  std::cout << hi << " FAILED: invalid mapping solution" << std::endl;
336  }
337 
338 
339  // Test mapping explicitly using default machine
341  machine_t defMachine(*comm);
342 
343 #ifdef KDD
344  if (me == 0)
345  std::cout << "Testing Mapping using explicit machine" << std::endl;
346 
347  Zoltan2::MappingProblem<Adapter, machine_t> mprob2(&ia, &params, comm,
348  NULL, &defMachine);
349  mprob2.solve();
350 
352 
353  if (!sameMappingSolution(*msoln1, *msoln2, *comm)) {
354  allgood = false;
355  if (me == 0)
356  std::cout << hi << " FAILED: solution with explicit machine "
357  "differs from default" << std::endl;
358  }
359 #endif
360 
361  // Test mapping with a partitioning solution
362  if (me == 0)
363  std::cout << "Testing Mapping using a partitioning solution" << std::endl;
364 
365  RCP<const Zoltan2::Environment> env = rcp(new Zoltan2::Environment(comm));
366  Zoltan2::PartitioningSolution<Adapter> psoln(env, comm, 0);
367 
368  ArrayRCP<part_t> partList(ia.getLocalNumIDs());
369  for (size_t i = 0; i < ia.getLocalNumIDs(); i++)
370  partList[i] = (me + 1) % np;
371 
372  psoln.setParts(partList);
373 
374 #ifdef HAVE_ZOLTAN2_MPI
375  // Use an MPI_Comm, just to exercise that bit of code
376  // In real life, no one should extract the MPI_Comm from the Teuchos::Comm;
377  // he should use the Teuchos::Comm. But for testing,
378  // we need to exercise the MPI_Comm interface.
379  MPI_Comm mpicomm = Teuchos::getRawMpiComm(*comm);
380  Zoltan2::MappingProblem<Adapter, machine_t> mprob3(&ia, &params, mpicomm,
381  NULL, &defMachine);
382 #else
383  Zoltan2::MappingProblem<Adapter, machine_t> mprob3(&ia, &params, comm,
384  NULL, &defMachine);
385 #endif
386 
387  mprob3.solve();
388 
390 
391  if (!validMappingSolution(*msoln3, ia, psoln, *comm)) {
392  allgood = false;
393  if (me == 0)
394  std::cout << hi << " FAILED: invalid mapping solution "
395  "from partitioning solution" << std::endl;
396  }
397  return allgood;
398 }
399 
401 
402 int main(int narg, char *arg[])
403 {
404  Tpetra::ScopeGuard tscope(&narg, &arg);
405  Teuchos::RCP<const Teuchos::Comm<int> > comm = Tpetra::getDefaultComm();
406 
407  int me = comm->getRank();
408  bool allgood = true;
409 
410  typedef VerySimpleVectorAdapter<zzuser_t> vecAdapter_t;
411  //typedef vecAdapter_t::part_t part_t;
412  //typedef vecAdapter_t::scalar_t scalar_t;
413 
414  // TEST 1
415  {
416  int nPartsPerRow = 1;
417  int firstPart = 0;
418  bool useInputParts = true;
419 
420  vecAdapter_t ia(*comm, nPartsPerRow, firstPart, useInputParts);
421  ia.print("test1");
422 
423  allgood = runTest(ia, comm, "test1");
424  }
425 
426 #ifdef KDD
427  // TEST 2
428  {
429  // Create a very simple input adapter.
430  int nPartsPerRow = 1;
431  int firstPart = 0;
432  bool useInputParts = false;
433  vecAdapter_t ia(*comm, nPartsPerRow, firstPart, useInputParts);
434  ia.print("test2");
435  }
436 
437  // TEST 3
438  {
439  // Create a very simple input adapter.
440  int nPartsPerRow = 2;
441  int firstPart = 4;
442  bool useInputParts = true;
443  vecAdapter_t ia(*comm, nPartsPerRow, firstPart, useInputParts);
444  ia.print("test3");
445  }
446 
447  // TEST 4
448  {
449  // Create a very simple input adapter.
450  int nPartsPerRow = 3;
451  int firstPart = 10;
452  bool useInputParts = true;
453  vecAdapter_t ia(*comm, nPartsPerRow, firstPart, useInputParts);
454  ia.print("test4");
455  }
456 #endif
457 
458  if (allgood && (me == 0))
459  std::cout << "PASS" << std::endl;
460 }
bool validMappingSolution(Zoltan2::MappingSolution< Adapter > &msoln, Adapter &ia, const Teuchos::Comm< int > &comm)
Definition: Mapping.cpp:146
#define nParts
typename InputTraits< User >::scalar_t scalar_t
Zoltan2::BasicUserTypes< zscalar_t, zlno_t, zgno_t > zzuser_t
Definition: Mapping.cpp:23
PartitionMapping maps a solution or an input distribution to ranks.
VerySimpleVectorAdapter(const Teuchos::Comm< int > &comm_, int nPartsPerRow_, int lowestPartNum_, bool useInputParts_=false)
Definition: Mapping.cpp:44
map_t::global_ordinal_type gno_t
Definition: mapRemotes.cpp:27
void solve(bool updateInputData=true)
Direct the problem to create a solution.
bool runTest(Adapter &ia, const RCP< const Teuchos::Comm< int > > &comm, std::string hi)
Definition: Mapping.cpp:306
Defines the VectorAdapter interface.
void getEntriesView(const scalar_t *&Coords, int &Stride, int Idx) const
Provide a pointer to the elements of the specified vector.
Definition: Mapping.cpp:118
int main(int narg, char **arg)
Definition: coloring1.cpp:164
common code used by tests
typename InputTraits< User >::part_t part_t
void print(std::string hi)
Definition: Mapping.cpp:71
mapsoln_t * getSolution()
Get the solution to the problem.
Zoltan2::VectorAdapter< User >::scalar_t scalar_t
Definition: Mapping.cpp:36
SparseMatrixAdapter_t::part_t part_t
A PartitioningSolution is a solution to a partitioning problem.
size_t getLocalNumIDs() const
Returns the number of objects on this process.
Definition: Mapping.cpp:114
const part_t * getPartListView() const
Returns the part list corresponding to the global ID list.
typename InputTraits< User >::gno_t gno_t
Zoltan2::VectorAdapter< User >::gno_t gno_t
Definition: Mapping.cpp:35
Zoltan2::VectorAdapter< User >::lno_t lno_t
Definition: Mapping.cpp:34
VectorAdapter defines the interface for vector input.
The user parameters, debug, timing and memory profiling output objects, and error checking methods...
MachineRepresentation Class Base class for representing machine coordinates, networks, etc.
static const int nCoordPerRank
Definition: Mapping.cpp:41
void setParts(ArrayRCP< part_t > &partList)
The algorithm uses setParts to set the solution.
int getRankForPart(part_t part)
Get the rank containing a part. Simplifying assumption: a part is wholy assigned to a rank; it is not...
Defines the MappingSolution class.
void getMyPartsView(part_t &numParts, part_t *&parts)
Get the parts belonging to this rank.
void getIDsView(const gno_t *&Ids) const
Provide a pointer to this process&#39; identifiers.
Definition: Mapping.cpp:115
int getNumEntriesPerID() const
Return the number of vectors.
Definition: Mapping.cpp:117
void getPartsView(const part_t *&InputPart) const
Provide pointer to an array containing the input part assignment for each ID. The input part informat...
Definition: Mapping.cpp:124
static const int nCoordDim
Definition: Mapping.cpp:40
MappingProblem enables mapping of a partition (either computed or input) to MPI ranks.
typename InputTraits< User >::lno_t lno_t
Zoltan2::VectorAdapter< User >::part_t part_t
Definition: Mapping.cpp:37
Defines the MappingProblem class.