Zoltan2
Mapping.cpp
Go to the documentation of this file.
1// @HEADER
2//
3// ***********************************************************************
4//
5// Zoltan2: A package of combinatorial algorithms for scientific computing
6// Copyright 2012 Sandia Corporation
7//
8// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9// the U.S. Government retains certain rights in this software.
10//
11// Redistribution and use in source and binary forms, with or without
12// modification, are permitted provided that the following conditions are
13// met:
14//
15// 1. Redistributions of source code must retain the above copyright
16// notice, this list of conditions and the following disclaimer.
17//
18// 2. Redistributions in binary form must reproduce the above copyright
19// notice, this list of conditions and the following disclaimer in the
20// documentation and/or other materials provided with the distribution.
21//
22// 3. Neither the name of the Corporation nor the names of the
23// contributors may be used to endorse or promote products derived from
24// this software without specific prior written permission.
25//
26// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37//
38// Questions? Contact Karen Devine (kddevin@sandia.gov)
39// Erik Boman (egboman@sandia.gov)
40// Siva Rajamanickam (srajama@sandia.gov)
41//
42// ***********************************************************************
43//
44// @HEADER
45//
46// Test the MappingProblem and MappingSolution classes.
47//
48
49#include <Teuchos_ParameterList.hpp>
50#include <Teuchos_CommHelpers.hpp>
51
54
57
59
61template <typename User>
63{
64public:
65 // Defines an (np x nCoordPerRank) grid of points
66 // Total number of parts is np * nPartsPerRow;
67 // Half of each row of points belongs to a single part.
68 // Lowest part number is lowestPartNum.
73
74 // Problem dimensions are fixed
75 static const int nCoordDim = 2;
76 static const int nCoordPerRank = 6;
77
78 // Constructor
80 const Teuchos::Comm<int> &comm_,
81 int nPartsPerRow_,
82 int lowestPartNum_,
83 bool useInputParts_=false)
84 :
85 me(comm_.getRank()),
86 np(comm_.getSize()),
87 nPartsPerRow(nPartsPerRow_),
88 lowestPartNum(lowestPartNum_),
89 useInputParts(useInputParts_)
90 {
91 for (int j = 0; j < nCoordPerRank; j++)
92 ids[j] = me * nCoordPerRank + j;
93
94 for (int i = 0, j = 0; i < nPartsPerRow; i++)
95 for (int k = 0; k < nCoordPerRank / nPartsPerRow; k++, j++)
96 inputparts[j] = lowestPartNum + i + me*nPartsPerRow;
97
98 for (int j = 0; j < nCoordPerRank; j++) {
99 coords[0][j] = scalar_t(j);
100 coords[1][j] = scalar_t(me);
101 if (nCoordDim > 2) coords[2][j] = scalar_t(np);
102 }
103 }
104
105 // Print the data as received by the methods.
106 void print(std::string hi) {
107 // print ids
108 const gno_t *mids;
109 getIDsView(mids);
110 std::cout << hi << " methods Rank " << me << " ids: ";
111 for (size_t j = 0; j < getLocalNumIDs(); j++) std::cout << mids[j] << " ";
112 std::cout << std::endl;
113
114 // print coords
115 const scalar_t **mcoords = new const scalar_t*[getNumEntriesPerID()];
116 int *stride = new int[getNumEntriesPerID()];
117 for (int k = 0; k < getNumEntriesPerID(); k++)
118 getEntriesView(mcoords[k], stride[k], k);
119
120 std::cout << hi << " methods Rank " << me << " coords: ";
121 for (size_t j = 0; j < getLocalNumIDs(); j++) {
122 std::cout << "(";
123 for (int k = 0; k < getNumEntriesPerID(); k++)
124 std::cout << mcoords[k][j*stride[k]] << ",";
125 std::cout << ") ";
126 }
127 std::cout << std::endl;
128 delete [] mcoords;
129 delete [] stride;
130
131 // print inputparts
132 const part_t *minputparts;
133 getPartsView(minputparts);
134 std::cout << hi << " methods Rank " << me << " parts: ";
135 if (minputparts != NULL)
136 for (size_t j = 0; j < getLocalNumIDs(); j++)
137 std::cout << minputparts[j] << " ";
138 else
139 std::cout << "not provided";
140 std::cout << std::endl;
141 }
142
143 // Return values given to the constructor
144 bool adapterUsesInputParts() { return useInputParts; }
145 int adapterNPartsPerRow() { return nPartsPerRow; }
146 int adapterLowestPartNum() { return lowestPartNum; }
147
148 // Methods for the VectorAdapter interface
149 size_t getLocalNumIDs() const { return nCoordPerRank; }
150 void getIDsView(const gno_t *&Ids) const { Ids = ids; }
151
152 int getNumEntriesPerID() const { return nCoordDim; }
153 void getEntriesView(const scalar_t *&Coords, int &Stride, int Idx) const
154 {
155 Coords = coords[Idx];
156 Stride = 1;
157 }
158
159 void getPartsView(const part_t *&InputPart) const {
160 if (useInputParts)
161 InputPart = inputparts;
162 else
163 InputPart = NULL;
164 }
165
166private:
167 int me; // my rank
168 int np; // number of ranks
169 int nPartsPerRow; // number of parts created per row of the grid
170 int lowestPartNum; // lowest part number; not necessarily zero
171 bool useInputParts; // use the input partition in the tests? If not,
172 // Zoltan2 uses rank as default part number
173 gno_t ids[nCoordPerRank]; // IDs of this rank's grid points
174 part_t inputparts[nCoordPerRank]; // Input part for each local point
175 scalar_t coords[nCoordDim][nCoordPerRank]; // Coordinate for each local point
176};
177
179// Validate a mapping solution obtained without a partitioning solution
180template <typename Adapter>
183 Adapter &ia,
184 const Teuchos::Comm<int> &comm
185)
186{
187 // Correctness of a particular mapping algorithm is beyond the scope of
188 // this test.
189 typedef typename Adapter::part_t part_t;
190
191 bool aok = true;
192
193 // All returned processors must be in range [0,np-1].
194
195 if (ia.adapterUsesInputParts()) {
196 // Adapter provided input parts
197 const part_t *inputParts;
198 ia.getPartsView(inputParts);
199
200 aok = validMappingSolution(msoln, ia, inputParts, comm);
201 }
202 else {
203 // Adapter did not provide input parts; use default part = me for all IDs
204 int me = comm.getRank();
205
206 part_t *defaultParts = new part_t[ia.getLocalNumIDs()];
207 for (size_t i = 0; i < ia.getLocalNumIDs(); i++)
208 defaultParts[i] = me;
209
210 aok = validMappingSolution(msoln, ia, defaultParts, comm);
211
212 delete [] defaultParts;
213 }
214
215 return aok;
216}
217
219// Validate a mapping solution obtained with a partitioning solution
220template <typename Adapter>
223 Adapter &ia,
225 const Teuchos::Comm<int> &comm
226)
227{
228 typedef typename Adapter::part_t part_t;
229
230 const part_t *assignedParts = psoln.getPartListView();
231
232 return(validMappingSolution(msoln, ia, assignedParts, comm));
233}
234
236// Validate a mapping solution.
237// This test checks only whether the mapping solution is valid. Correctness
238// of a particular mapping algorithm is beyond the scope of this test.
239template <typename Adapter>
242 Adapter &ia,
243 const typename Adapter::part_t *useTheseParts,
244 // Parts to check for correct mapping to ranks;
245 // may be from Adapter,
246 // from PartitioningSolution, or
247 // default to current rank
248 const Teuchos::Comm<int> &comm
249)
250{
251 typedef typename Adapter::part_t part_t;
252
253 int np = comm.getSize();
254
255 bool aok = true;
256
257 // Min and max part numbers in useTheseParts
258 part_t globalmin, localmin = std::numeric_limits<part_t>::max();
259 part_t globalmax, localmax = 0;
260
261 for (size_t i = 0; i < ia.getLocalNumIDs(); i++) {
262
263 // All returned processors must be in range [0,np-1].
264 int r = msoln.getRankForPart(useTheseParts[i]);
265 if (r < 0 || r >= np) {
266 aok = false;
267 std::cout << __FILE__ << ":" << __LINE__ << " "
268 << "Invalid rank " << r << " of " << np << " returned"
269 << std::endl;
270 }
271
272 // Find local max/min part number
273 part_t p = useTheseParts[i];
274 if (p > localmax) localmax = p;
275 if (p < localmin) localmin = p;
276 }
277
278 // Find global max/min part number
279 Teuchos::reduceAll<int, part_t>(comm, Teuchos::REDUCE_MAX, 1,
280 &localmax, &globalmax);
281 Teuchos::reduceAll<int, part_t>(comm, Teuchos::REDUCE_MIN, 1,
282 &localmin, &globalmin);
283
284 part_t *parts;
286 msoln.getMyPartsView(nParts, parts);
287
288 for (part_t i = 0; i < nParts; i++) {
289
290 // All returned parts must at least be in the range of part numbers
291 // from useTheseParts
292 part_t p = parts[i];
293 if ((p < globalmin) || (p > globalmax)) {
294 aok = false;
295 std::cout << __FILE__ << ":" << __LINE__ << " "
296 << "Invalid part " << p << " of " << np << " returned"
297 << std::endl;
298 }
299 }
300
301 // Test the error checking in mapping solution;
302 // each test should throw an error.
303
304 part_t ret;
305 bool errorThrownCorrectly = false;
306 part_t sillyPart = globalmax+10;
307 try {
308 ret = msoln.getRankForPart(sillyPart);
309 }
310 catch (std::exception &e) {
311 errorThrownCorrectly = true;
312 }
313 if (errorThrownCorrectly == false) {
314 aok = false;
315 std::cout << __FILE__ << ":" << __LINE__ << " "
316 << "Mapping Solution accepted a too-high part number "
317 << sillyPart << " returned " << ret << std::endl;
318 }
319
320 errorThrownCorrectly = false;
321 sillyPart = globalmin - 1;
322 try {
323 ret = msoln.getRankForPart(sillyPart);
324 }
325 catch (std::exception &e) {
326 errorThrownCorrectly = true;
327 }
328 if (errorThrownCorrectly == false) {
329 aok = false;
330 std::cout << __FILE__ << ":" << __LINE__ << " "
331 << "Mapping Solution accepted a too-low part number "
332 << sillyPart << " returned " << ret << std::endl;
333 }
334
335 return aok;
336}
337
339
340template <typename Adapter>
342 Adapter &ia,
343 const RCP<const Teuchos::Comm<int> > &comm,
344 std::string hi
345)
346{
347 typedef typename Adapter::part_t part_t;
348 typedef typename Adapter::scalar_t scalar_t;
349
350 int me = comm->getRank();
351 int np = comm->getSize();
352
353 bool allgood = true;
354
355 Teuchos::ParameterList params;
356 params.set("mapping_algorithm", "block");
357
358 // Test mapping using default machine
359 if (me == 0)
360 std::cout << "Testing Mapping using default machine" << std::endl;
361
362 Zoltan2::MappingProblem<Adapter> mprob1(&ia, &params, comm);
363 mprob1.solve();
364
366
367 if (!validMappingSolution<Adapter>(*msoln1, ia, *comm)) {
368 allgood = false;
369 if (me == 0)
370 std::cout << hi << " FAILED: invalid mapping solution" << std::endl;
371 }
372
373
374 // Test mapping explicitly using default machine
376 machine_t defMachine(*comm);
377
378#ifdef KDD
379 if (me == 0)
380 std::cout << "Testing Mapping using explicit machine" << std::endl;
381
382 Zoltan2::MappingProblem<Adapter, machine_t> mprob2(&ia, &params, comm,
383 NULL, &defMachine);
384 mprob2.solve();
385
387
388 if (!sameMappingSolution(*msoln1, *msoln2, *comm)) {
389 allgood = false;
390 if (me == 0)
391 std::cout << hi << " FAILED: solution with explicit machine "
392 "differs from default" << std::endl;
393 }
394#endif
395
396 // Test mapping with a partitioning solution
397 if (me == 0)
398 std::cout << "Testing Mapping using a partitioning solution" << std::endl;
399
400 RCP<const Zoltan2::Environment> env = rcp(new Zoltan2::Environment(comm));
401 Zoltan2::PartitioningSolution<Adapter> psoln(env, comm, 0);
402
403 ArrayRCP<part_t> partList(ia.getLocalNumIDs());
404 for (size_t i = 0; i < ia.getLocalNumIDs(); i++)
405 partList[i] = (me + 1) % np;
406
407 psoln.setParts(partList);
408
409#ifdef HAVE_ZOLTAN2_MPI
410 // Use an MPI_Comm, just to exercise that bit of code
411 // In real life, no one should extract the MPI_Comm from the Teuchos::Comm;
412 // he should use the Teuchos::Comm. But for testing,
413 // we need to exercise the MPI_Comm interface.
414 MPI_Comm mpicomm = Teuchos::getRawMpiComm(*comm);
415 Zoltan2::MappingProblem<Adapter, machine_t> mprob3(&ia, &params, mpicomm,
416 NULL, &defMachine);
417#else
418 Zoltan2::MappingProblem<Adapter, machine_t> mprob3(&ia, &params, comm,
419 NULL, &defMachine);
420#endif
421
422 mprob3.solve();
423
425
426 if (!validMappingSolution(*msoln3, ia, psoln, *comm)) {
427 allgood = false;
428 if (me == 0)
429 std::cout << hi << " FAILED: invalid mapping solution "
430 "from partitioning solution" << std::endl;
431 }
432 return allgood;
433}
434
436
437int main(int narg, char *arg[])
438{
439 Tpetra::ScopeGuard tscope(&narg, &arg);
440 Teuchos::RCP<const Teuchos::Comm<int> > comm = Tpetra::getDefaultComm();
441
442 int me = comm->getRank();
443 bool allgood = true;
444
445 typedef VerySimpleVectorAdapter<zzuser_t> vecAdapter_t;
446 //typedef vecAdapter_t::part_t part_t;
447 //typedef vecAdapter_t::scalar_t scalar_t;
448
449 // TEST 1
450 {
451 int nPartsPerRow = 1;
452 int firstPart = 0;
453 bool useInputParts = true;
454
455 vecAdapter_t ia(*comm, nPartsPerRow, firstPart, useInputParts);
456 ia.print("test1");
457
458 allgood = runTest(ia, comm, "test1");
459 }
460
461#ifdef KDD
462 // TEST 2
463 {
464 // Create a very simple input adapter.
465 int nPartsPerRow = 1;
466 int firstPart = 0;
467 bool useInputParts = false;
468 vecAdapter_t ia(*comm, nPartsPerRow, firstPart, useInputParts);
469 ia.print("test2");
470 }
471
472 // TEST 3
473 {
474 // Create a very simple input adapter.
475 int nPartsPerRow = 2;
476 int firstPart = 4;
477 bool useInputParts = true;
478 vecAdapter_t ia(*comm, nPartsPerRow, firstPart, useInputParts);
479 ia.print("test3");
480 }
481
482 // TEST 4
483 {
484 // Create a very simple input adapter.
485 int nPartsPerRow = 3;
486 int firstPart = 10;
487 bool useInputParts = true;
488 vecAdapter_t ia(*comm, nPartsPerRow, firstPart, useInputParts);
489 ia.print("test4");
490 }
491#endif
492
493 if (allgood && (me == 0))
494 std::cout << "PASS" << std::endl;
495}
int main(int narg, char *arg[])
Definition: Mapping.cpp:437
Zoltan2::BasicUserTypes< zscalar_t, zlno_t, zgno_t > zzuser_t
Definition: Mapping.cpp:58
bool validMappingSolution(Zoltan2::MappingSolution< Adapter > &msoln, Adapter &ia, const Teuchos::Comm< int > &comm)
Definition: Mapping.cpp:181
bool runTest(Adapter &ia, const RCP< const Teuchos::Comm< int > > &comm, std::string hi)
Definition: Mapping.cpp:341
#define nParts
Defines the MappingProblem class.
Defines the MappingSolution class.
common code used by tests
Defines the VectorAdapter interface.
void print(std::string hi)
Definition: Mapping.cpp:106
static const int nCoordDim
Definition: Mapping.cpp:75
void getIDsView(const gno_t *&Ids) const
Definition: Mapping.cpp:150
static const int nCoordPerRank
Definition: Mapping.cpp:76
void getEntriesView(const scalar_t *&Coords, int &Stride, int Idx) const
Definition: Mapping.cpp:153
Zoltan2::VectorAdapter< User >::gno_t gno_t
Definition: Mapping.cpp:70
void getPartsView(const part_t *&InputPart) const
Definition: Mapping.cpp:159
Zoltan2::VectorAdapter< User >::lno_t lno_t
Definition: Mapping.cpp:69
Zoltan2::VectorAdapter< User >::part_t part_t
Definition: Mapping.cpp:72
VerySimpleVectorAdapter(const Teuchos::Comm< int > &comm_, int nPartsPerRow_, int lowestPartNum_, bool useInputParts_=false)
Definition: Mapping.cpp:79
Zoltan2::VectorAdapter< User >::scalar_t scalar_t
Definition: Mapping.cpp:71
size_t getLocalNumIDs() const
Returns the number of objects on this process.
Definition: Mapping.cpp:149
int getNumEntriesPerID() const
Return the number of vectors.
Definition: Mapping.cpp:152
InputTraits< User >::part_t part_t
InputTraits< User >::scalar_t scalar_t
InputTraits< User >::lno_t lno_t
InputTraits< User >::gno_t gno_t
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
Definition: mapRemotes.cpp:18
SparseMatrixAdapter_t::part_t part_t