Zoltan2
Loading...
Searching...
No Matches
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
19
22
24
26template <typename User>
28{
29public:
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
131private:
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
145template <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
185template <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.
204template <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;
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
305template <typename Adapter>
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
402int 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}
Zoltan2::BasicUserTypes< zscalar_t, zlno_t, zgno_t > zzuser_t
Definition Mapping.cpp:23
bool validMappingSolution(Zoltan2::MappingSolution< Adapter > &msoln, Adapter &ia, const Teuchos::Comm< int > &comm)
Definition Mapping.cpp:146
bool runTest(Adapter &ia, const RCP< const Teuchos::Comm< int > > &comm, std::string hi)
Definition Mapping.cpp:306
#define nParts
Defines the MappingProblem class.
Defines the MappingSolution class.
common code used by tests
Defines the VectorAdapter interface.
int main()
void print(std::string hi)
Definition Mapping.cpp:71
static const int nCoordDim
Definition Mapping.cpp:40
void getIDsView(const gno_t *&Ids) const
Definition Mapping.cpp:115
static const int nCoordPerRank
Definition Mapping.cpp:41
void getEntriesView(const scalar_t *&Coords, int &Stride, int Idx) const
Definition Mapping.cpp:118
Zoltan2::VectorAdapter< User >::gno_t gno_t
Definition Mapping.cpp:35
void getPartsView(const part_t *&InputPart) const
Definition Mapping.cpp:124
Zoltan2::VectorAdapter< User >::lno_t lno_t
Definition Mapping.cpp:34
Zoltan2::VectorAdapter< User >::part_t part_t
Definition Mapping.cpp:37
VerySimpleVectorAdapter(const Teuchos::Comm< int > &comm_, int nPartsPerRow_, int lowestPartNum_, bool useInputParts_=false)
Definition Mapping.cpp:44
Zoltan2::VectorAdapter< User >::scalar_t scalar_t
Definition Mapping.cpp:36
size_t getLocalNumIDs() const
Returns the number of objects on this process.
Definition Mapping.cpp:114
int getNumEntriesPerID() const
Return the number of vectors.
Definition Mapping.cpp:117
typename InputTraits< User >::lno_t lno_t
typename InputTraits< User >::scalar_t scalar_t
typename InputTraits< User >::gno_t gno_t
typename InputTraits< User >::part_t part_t
A simple class that can be the User template argument for an InputAdapter.
The user parameters, debug, timing and memory profiling output objects, and error checking methods.
MachineRepresentation Class Base class for representing machine coordinates, networks,...
MappingProblem enables mapping of a partition (either computed or input) to MPI ranks.
mapsoln_t * getSolution()
Get the solution to the problem.
void solve(bool updateInputData=true)
Direct the problem to create a solution.
PartitionMapping maps a solution or an input distribution to ranks.
int getRankForPart(part_t part)
Get the rank containing a part. Simplifying assumption: a part is wholy assigned to a rank; it is not...
void getMyPartsView(part_t &numParts, part_t *&parts)
Get the parts belonging to this rank.
A PartitioningSolution is a solution to a partitioning problem.
void setParts(ArrayRCP< part_t > &partList)
The algorithm uses setParts to set the solution.
const part_t * getPartListView() const
Returns the part list corresponding to the global ID list.
VectorAdapter defines the interface for vector input.
map_t::global_ordinal_type gno_t
SparseMatrixAdapter_t::part_t part_t