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 
size_t getLocalNumIDs() const
Returns the number of objects on this process.
OldSchoolVectorAdapterStrided(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
void printMetrics(std::ostream &os) const
Print all metrics.
static ArrayRCP< ArrayRCP< zscalar_t > > weights
int getNumWeightsPerID() const
Returns the number of weights per object. Number of weights per object should be zero or greater...
map_t::global_ordinal_type gno_t
Definition: mapRemotes.cpp:18
Zoltan2::InputTraits< User >::scalar_t scalar_t
A simple class that can be the User template argument for an InputAdapter.
Defines the VectorAdapter interface.
void getWeightsView(const scalar_t *&wgt, int &stride, int idx=0) const override
Zoltan2::EvaluatePartition< matrixAdapter_t > quality_t
Defines the PartitioningSolution class.
scalar_t getObjectCountImbalance() const
Return the object count imbalance.
void getIDsView(const gno_t *&ids) const
void getWeightsView(const scalar_t *&wgt, int &stride, int idx=0) const
void getEntriesView(const scalar_t *&elements, int &stride, int idx=0) const override
int main(int narg, char *arg[])
void getIDsView(const gno_t *&ids) const
void getWeightsView(const scalar_t *&wgt, int &stride, int idx=0) const
int getNumWeightsPerID() const
Returns the number of weights per object. Number of weights per object should be zero or greater...
VectorAdapter defines the interface for vector input.
int run_test_strided_versus_contig(const std::string &algorithm)
Traits for application input objects.
void getEntriesView(const scalar_t *&coo, int &stride, int idx=0) const
default_gno_t gno_t
The ordinal type (e.g., int, long, int64_t) that can represent global counts and identifiers.
Zoltan2::InputTraits< User >::gno_t gno_t
const PartitioningSolution< Adapter > & getSolution()
Get the solution to the problem.
PartitioningProblem sets up partitioning problems for the user.
void getIDsView(const gno_t *&ids) const override
virtual void getIDsKokkosView(Kokkos::View< const gno_t *, typename node_t::device_type > &ids) const
int getNumEntriesPerID() const
Return the number of vectors.
Tpetra::Map ::node_type node_t
void getEntriesView(const scalar_t *&coo, int &stride, int idx=0) const
int getNumEntriesPerID() const
Return the number of vectors.
size_t getLocalNumIDs() const
Returns the number of objects on this process.
Defines the PartitioningProblem class.
Zoltan2::InputTraits< User >::gno_t gno_t
A class that computes and returns quality metrics.
virtual void getWeightsKokkosView(Kokkos::View< scalar_t **, typename node_t::device_type > &wgt) const
scalar_t getWeightImbalance(int weightIndex) const
Return the imbalance for the requested weight.
size_t getLocalNumIDs() const
Returns the number of objects on this process.
int getNumEntriesPerID() const
Return the number of vectors.
void solve(bool updateInputData=true)
Direct the problem to create a solution.
default_scalar_t scalar_t
The data type for weights and coordinates.
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)
Zoltan2::InputTraits< User >::scalar_t scalar_t
KokkosVectorAdapter(const size_t nids_, const gno_t *gids_, const int dim_, const scalar_t *coords_, const scalar_t *weights_=NULL)
Zoltan2::InputTraits< User >::scalar_t scalar_t