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