Zoltan2
mj_backwardcompat.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 
53 #include <Zoltan2_InputTraits.hpp>
54 #include <Tpetra_Map.hpp>
55 #include <vector>
56 #include <cstdlib>
57 
59 // Simple adapter with contiguous coordinates
60 template <typename User>
62 {
63 public:
66 
68  const size_t nids_,
69  const gno_t *gids_,
70  const int dim_,
71  const scalar_t *coords_,
72  const scalar_t *weights_ = NULL)
73  : nids(nids_), gids(gids_), dim(dim_), coords(coords_), weights(weights_)
74  { }
75 
76  size_t getLocalNumIDs() const { return nids; }
77 
78  void getIDsView(const gno_t *&ids) const { ids = gids; }
79 
80  int getNumWeightsPerID() const { return (weights != NULL); }
81 
82  void getWeightsView(const scalar_t *&wgt, int &stride, int idx = 0) const
83  { wgt = weights; stride = 1; }
84 
85  int getNumEntriesPerID() const { return dim; }
86 
87  void getEntriesView(const scalar_t *&coo, int &stride, int idx = 0) const
88  {
89  coo = &(coords[idx*nids]);
90  stride = 1;
91  }
92 
93 private:
94  const size_t nids;
95  const gno_t *gids;
96  const int dim;
97  const scalar_t *coords;
98  const scalar_t *weights;
99 };
100 
102 // Simple adapter with strided coordinates
103 template <typename User>
105 {
106 public:
109 
111  const size_t nids_,
112  const gno_t *gids_,
113  const int dim_,
114  const scalar_t *coords_,
115  const scalar_t *weights_ = NULL)
116  : nids(nids_), gids(gids_), dim(dim_), coords(coords_), weights(weights_)
117  { }
118 
119  size_t getLocalNumIDs() const { return nids; }
120 
121  void getIDsView(const gno_t *&ids) const { ids = gids; }
122 
123  int getNumWeightsPerID() const { return (weights != NULL); }
124 
125  void getWeightsView(const scalar_t *&wgt, int &stride, int idx = 0) const
126  { wgt = weights; stride = 1; }
127 
128  int getNumEntriesPerID() const { return dim; }
129 
130  void getEntriesView(const scalar_t *&coo, int &stride, int idx = 0) const
131  {
132  coo = &(coords[idx]);
133  stride = dim;
134  }
135 
136 private:
137  const size_t nids;
138  const gno_t *gids;
139  const int dim;
140  const scalar_t *coords;
141  const scalar_t *weights;
142 };
143 
145 // Same adapter as above but now we supply the Kokkos calls, not the original
146 // calls. This is to verify that backwards and forward compatibility are all
147 // working properly.
148 template <typename User>
150 {
151 public:
154  typedef Tpetra::Map<>::node_type node_t;
155 
157  const size_t nids_,
158  const gno_t *gids_,
159  const int dim_,
160  const scalar_t *coords_,
161  const scalar_t *weights_ = NULL)
162  : nids(nids_), dim(dim_)
163  {
164  // create kokkos_gids in default memory space
165  {
166  typedef Kokkos::DualView<gno_t *> view_ids_t;
167  kokkos_gids = view_ids_t(
168  Kokkos::ViewAllocateWithoutInitializing("gids"), nids);
169 
170  auto host_gids = kokkos_gids.h_view;
171  for(size_t n = 0; n < nids; ++n) {
172  host_gids(n) = gids_[n];
173  }
174 
175  kokkos_gids.template modify<typename view_ids_t::host_mirror_space>();
176  kokkos_gids.sync_host();
177  kokkos_gids.template sync<typename node_t::device_type>();
178  }
179 
180  // create kokkos_weights in default memory space
181  if(weights_ != NULL)
182  {
183  typedef Kokkos::DualView<scalar_t **> view_weights_t;
184  kokkos_weights = view_weights_t(
185  Kokkos::ViewAllocateWithoutInitializing("weights"), nids, 0);
186  auto host_kokkos_weights = kokkos_weights.h_view;
187  for(size_t n = 0; n < nids; ++n) {
188  host_kokkos_weights(n,0) = weights_[n];
189  }
190 
191  kokkos_weights.template modify<typename view_weights_t::host_mirror_space>();
192  kokkos_weights.sync_host();
193  kokkos_weights.template sync<typename node_t::device_type>();
194  }
195 
196  // create kokkos_coords in default memory space
197  {
198  typedef Kokkos::DualView<scalar_t **, Kokkos::LayoutLeft> kokkos_coords_t;
199  kokkos_coords = kokkos_coords_t(
200  Kokkos::ViewAllocateWithoutInitializing("coords"), nids, dim);
201  auto host_kokkos_coords = kokkos_coords.h_view;
202 
203  for(size_t n = 0; n < nids; ++n) {
204  for(int idx = 0; idx < dim; ++idx) {
205  host_kokkos_coords(n,idx) = coords_[n+idx*nids];
206  }
207  }
208 
209  kokkos_coords.template modify<typename kokkos_coords_t::host_mirror_space>();
210  kokkos_coords.sync_host();
211  kokkos_coords.template sync<typename node_t::device_type>();
212  }
213  }
214 
215  size_t getLocalNumIDs() const { return nids; }
216 
217  void getIDsView(const gno_t *&ids) const override {
218  auto kokkosIds = kokkos_gids.view_host();
219  ids = kokkosIds.data();
220  }
221 
222  virtual void getIDsKokkosView(Kokkos::View<const gno_t *,
223  typename node_t::device_type> &ids) const {
224  ids = kokkos_gids.template view<typename node_t::device_type>();;
225  }
226 
227  int getNumWeightsPerID() const { return (kokkos_weights.h_view.size() != 0); }
228 
229  void getWeightsView(const scalar_t *&wgt, int &stride,
230  int idx = 0) const override
231  {
232  auto h_wgts_2d = kokkos_weights.view_host();
233 
234  wgt = Kokkos::subview(h_wgts_2d, Kokkos::ALL, idx).data();
235  stride = 1;
236  }
237 
238  virtual void getWeightsKokkosView(Kokkos::View<scalar_t **,
239  typename node_t::device_type> & wgt) const {
240  wgt = kokkos_weights.template view<typename node_t::device_type>();;
241  }
242 
243  int getNumEntriesPerID() const { return dim; }
244 
245  void getEntriesView(const scalar_t *&elements,
246  int &stride, int idx = 0) const override {
247  elements = kokkos_coords.view_host().data();
248  stride = 1;
249  }
250 
251  virtual void getEntriesKokkosView(Kokkos::View<scalar_t **,
252  Kokkos::LayoutLeft, typename node_t::device_type> & coo) const {
253  coo = kokkos_coords.template view<typename node_t::device_type>();
254  }
255 
256 private:
257  const size_t nids;
258  Kokkos::DualView<gno_t *> kokkos_gids;
259  const int dim;
260  Kokkos::DualView<scalar_t **, Kokkos::LayoutLeft> kokkos_coords;
261  Kokkos::DualView<scalar_t **> kokkos_weights;
262 };
263 
265 int run_test_strided_versus_contig(const std::string & algorithm) {
266 
267  int nFail = 0;
268 
269  const Teuchos::RCP<const Teuchos::Comm<int> > comm = Tpetra::getDefaultComm();
270 
271  int rank = comm->getRank();
272  int nprocs = comm->getSize();
273 
274  typedef Tpetra::Map<> Map_t;
275  typedef Map_t::local_ordinal_type localId_t;
276  typedef Map_t::global_ordinal_type globalId_t;
277  typedef double scalar_t;
278 
280  typedef OldSchoolVectorAdapterStrided<myTypes> stridedAdapter_t;
281  typedef OldSchoolVectorAdapterContig<myTypes> contigAdapter_t;
282  typedef KokkosVectorAdapter<myTypes> kokkosAdapter_t;
284 
286  // Create input data.
287 
288  size_t localCount = 40;
289  int dim = 3; // no higher since we are testing rcb as a control which supports dim <= 3
290 
291  // Create coordinates strided
292  scalar_t *cStrided = new scalar_t [dim * localCount];
293  size_t cnt = 0;
294  for (size_t i = 0; i < localCount; i++)
295  for (int d = 0; d < dim; d++)
296  cStrided[cnt++] = d*1000 + rank*100 + i;
297 
298  // Create same coords, stored contiguously
299  scalar_t *cContig = new scalar_t [dim * localCount];
300  cnt = 0;
301  for (int d = 0; d < dim; d++)
302  for (size_t i = 0; i < localCount; i++)
303  cContig[cnt++] = d*1000 + rank*100 + i;
304 
305  // Create global ids for the coordinates.
306  globalId_t *globalIds = new globalId_t [localCount];
307  globalId_t offset = rank * localCount;
308  for (size_t i=0; i < localCount; i++) globalIds[i] = offset++;
309 
311  // Create parameters for an MJ problem
312 
313  Teuchos::ParameterList params("test params");
314  params.set("debug_level", "basic_status");
315  params.set("error_check_level", "debug_mode_assertions");
316 
317  params.set("algorithm", algorithm); // test runs multijagged and rcb
318  params.set("num_global_parts", nprocs+1);
319 
321  // Test one: No weights
322 
323  // Partition using strided coords
324  stridedAdapter_t *ia1 =
325  new stridedAdapter_t(localCount,globalIds,dim,cStrided);
326 
329 
330  problem1->solve();
331 
332  quality_t *metricObject1 = new quality_t(ia1, &params, comm,
333  &problem1->getSolution());
334  if (rank == 0){
335 
336  metricObject1->printMetrics(std::cout);
337 
338  double imb = metricObject1->getObjectCountImbalance();
339  if (imb <= 1.03) // Should get perfect balance
340  std::cout << "no weights -- balance satisfied: " << imb << std::endl;
341  else {
342  std::cout << "no weights -- balance failure: " << imb << std::endl;
343  nFail++;
344  }
345  std::cout << std::endl;
346  }
347 
348  // Partition using contiguous coords
349  contigAdapter_t *ia2 = new contigAdapter_t(localCount,globalIds,dim,cContig);
350 
353 
354  problem2->solve();
355 
356  // Partition using contiguous coords to generate kokkos adapter
357  kokkosAdapter_t *ia3 = new kokkosAdapter_t(localCount,globalIds,dim,cContig);
358 
361 
362  problem3->solve();
363 
364  // compare strided vs contiguous vs kokkos
365  size_t ndiff = 0;
366  for (size_t i = 0; i < localCount; i++) {
367  if((problem1->getSolution().getPartListView()[i] !=
368  problem2->getSolution().getPartListView()[i]) ||
369  (problem2->getSolution().getPartListView()[i] !=
370  problem3->getSolution().getPartListView()[i]))
371  {
372  std::cout << rank << " Error: differing parts for index " << i
373  << problem1->getSolution().getPartListView()[i] << " "
374  << problem2->getSolution().getPartListView()[i] << " "
375  << problem3->getSolution().getPartListView()[i] << std::endl;
376 
377  ndiff++;
378  }
379  }
380  if (ndiff > 0) nFail++;
381  else if (rank == 0) std::cout << "no weights -- comparisons OK " << std::endl;
382 
383  delete metricObject1;
384  delete problem1;
385  delete problem2;
386  delete problem3;
387  delete ia1;
388  delete ia2;
389  delete ia3;
390 
392  // Test two: weighted
393  // Create a Zoltan2 input adapter that includes weights.
394 
395  scalar_t *weights = new scalar_t [localCount];
396  for (size_t i=0; i < localCount; i++) weights[i] = 1 + scalar_t(rank);
397 
398  // Test with strided coords
399  ia1 = new stridedAdapter_t(localCount, globalIds, dim, cStrided, weights);
400 
401  problem1 = new Zoltan2::PartitioningProblem<stridedAdapter_t>(ia1, &params);
402 
403  problem1->solve();
404 
405  metricObject1 = new quality_t(ia1, &params, comm, &problem1->getSolution());
406 
407  if (rank == 0){
408 
409  metricObject1->printMetrics(std::cout);
410 
411  double imb = metricObject1->getWeightImbalance(0);
412  if (imb <= 1.03)
413  std::cout << "weighted -- balance satisfied " << imb << std::endl;
414  else {
415  std::cout << "weighted -- balance failed " << imb << std::endl;
416  nFail++;
417  }
418  std::cout << std::endl;
419  }
420 
421  // Partition using contiguous coords
422  ia2 = new contigAdapter_t(localCount, globalIds, dim, cContig, weights);
423 
424  problem2 = new Zoltan2::PartitioningProblem<contigAdapter_t>(ia2, &params);
425 
426  problem2->solve();
427 
428  // compare strided vs contiguous
429  ndiff = 0;
430  for (size_t i = 0; i < localCount; i++) {
431  if (problem1->getSolution().getPartListView()[i] !=
432  problem2->getSolution().getPartListView()[i]) {
433  std::cout << rank << " Error: differing parts for index " << i
434  << problem1->getSolution().getPartListView()[i] << " "
435  << problem2->getSolution().getPartListView()[i] << std::endl;
436 
437  ndiff++;
438  }
439  }
440  if (ndiff > 0) nFail++;
441  else if (rank == 0) std::cout << "weighted -- comparisons OK " << std::endl;
442 
443  delete metricObject1;
444  delete problem1;
445  delete problem2;
446  delete ia1;
447  delete ia2;
448 
449  // Test with strided coords
450  if (weights) delete [] weights;
451  if (cStrided) delete [] cStrided;
452  if (cContig) delete [] cContig;
453  if (globalIds) delete [] globalIds;
454 
455  // check result
456 
457  int gnFail;
458  Teuchos::reduceAll(*comm, Teuchos::REDUCE_SUM, 1, &nFail, &gnFail);
459  return gnFail;
460 }
461 
462 
463 int main(int narg, char *arg[])
464 {
465  Tpetra::ScopeGuard scope(&narg, &arg);
466  const Teuchos::RCP<const Teuchos::Comm<int> > comm = Tpetra::getDefaultComm();
467 
468  int err = 0;
469 
470  // err += run_test_strided_versus_contig("multijagged");
471  err += run_test_strided_versus_contig("rcb");
472 
473  if (comm->getRank() == 0) {
474  if (err == 0) std::cout << "PASS" << std::endl;
475  else std::cout << "FAIL: " << err << " tests failed" << std::endl;
476  }
477 
478  return 0;
479 }
480 
Traits for application input objects.
Defines the PartitioningProblem class.
Defines the PartitioningSolution class.
Defines the VectorAdapter interface.
int getNumEntriesPerID() const
Return the number of vectors.
void getIDsView(const gno_t *&ids) const override
Tpetra::Map ::node_type node_t
void getEntriesView(const scalar_t *&elements, int &stride, int idx=0) const override
size_t getLocalNumIDs() const
Returns the number of objects on this process.
int getNumWeightsPerID() const
Returns the number of weights per object. Number of weights per object should be zero or greater....
virtual void getWeightsKokkosView(Kokkos::View< scalar_t **, typename node_t::device_type > &wgt) const
KokkosVectorAdapter(const size_t nids_, const gno_t *gids_, const int dim_, const scalar_t *coords_, const scalar_t *weights_=NULL)
virtual void getEntriesKokkosView(Kokkos::View< scalar_t **, Kokkos::LayoutLeft, typename node_t::device_type > &coo) const
Zoltan2::InputTraits< User >::gno_t gno_t
virtual void getIDsKokkosView(Kokkos::View< const gno_t *, typename node_t::device_type > &ids) const
Zoltan2::InputTraits< User >::scalar_t scalar_t
void getWeightsView(const scalar_t *&wgt, int &stride, int idx=0) const override
Zoltan2::InputTraits< User >::scalar_t scalar_t
int getNumEntriesPerID() const
Return the number of vectors.
int getNumWeightsPerID() const
Returns the number of weights per object. Number of weights per object should be zero or greater....
OldSchoolVectorAdapterContig(const size_t nids_, const gno_t *gids_, const int dim_, const scalar_t *coords_, const scalar_t *weights_=NULL)
void getIDsView(const gno_t *&ids) const
size_t getLocalNumIDs() const
Returns the number of objects on this process.
Zoltan2::InputTraits< User >::gno_t gno_t
void getWeightsView(const scalar_t *&wgt, int &stride, int idx=0) const
void getEntriesView(const scalar_t *&coo, int &stride, int idx=0) const
Zoltan2::InputTraits< User >::scalar_t scalar_t
int getNumWeightsPerID() const
Returns the number of weights per object. Number of weights per object should be zero or greater....
size_t getLocalNumIDs() const
Returns the number of objects on this process.
int getNumEntriesPerID() const
Return the number of vectors.
void getIDsView(const gno_t *&ids) const
void getEntriesView(const scalar_t *&coo, int &stride, int idx=0) const
void getWeightsView(const scalar_t *&wgt, int &stride, int idx=0) const
OldSchoolVectorAdapterStrided(const size_t nids_, const gno_t *gids_, const int dim_, const scalar_t *coords_, const scalar_t *weights_=NULL)
Zoltan2::InputTraits< User >::gno_t gno_t
A simple class that can be the User template argument for an InputAdapter.
A class that computes and returns quality metrics.
void printMetrics(std::ostream &os) const
Print all metrics.
scalar_t getWeightImbalance(int weightIndex) const
Return the imbalance for the requested weight.
scalar_t getObjectCountImbalance() const
Return the object count imbalance.
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.
VectorAdapter defines the interface for vector input.
map_t::global_ordinal_type gno_t
Definition: mapRemotes.cpp:18
int run_test_strided_versus_contig(const std::string &algorithm)
int main(int narg, char *arg[])
static ArrayRCP< ArrayRCP< zscalar_t > > weights
default_gno_t gno_t
The ordinal type (e.g., int, long, int64_t) that can represent global counts and identifiers.
default_scalar_t scalar_t
The data type for weights and coordinates.
Zoltan2::EvaluatePartition< matrixAdapter_t > quality_t