Zoltan2
Zoltan2_AlgParMETIS.hpp
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 #ifndef _ZOLTAN2_ALGPARMETIS_HPP_
46 #define _ZOLTAN2_ALGPARMETIS_HPP_
47 
48 #include <Zoltan2_GraphModel.hpp>
49 #include <Zoltan2_Algorithm.hpp>
51 #include <Zoltan2_Util.hpp>
52 
57 
58 #ifndef HAVE_ZOLTAN2_PARMETIS
59 
60 // Error handling for when ParMETIS is requested
61 // but Zoltan2 not built with ParMETIS.
62 
63 namespace Zoltan2 {
64 template <typename Adapter>
65 class AlgParMETIS : public Algorithm<Adapter>
66 {
67 public:
68  AlgParMETIS(const RCP<const Environment> &env,
69  const RCP<const Comm<int> > &problemComm,
71  )
72  {
73  throw std::runtime_error(
74  "BUILD ERROR: ParMETIS requested but not compiled into Zoltan2.\n"
75  "Please set CMake flag Zoltan2_ENABLE_ParMETIS:BOOL=ON.");
76  }
77 };
78 }
79 
80 #endif
81 
84 
85 #ifdef HAVE_ZOLTAN2_PARMETIS
86 
87 #ifndef HAVE_ZOLTAN2_MPI
88 
89 // ParMETIS requires compilation with MPI.
90 // If MPI is not available, make compilation fail.
91 #error "TPL ParMETIS requires compilation with MPI. Configure with -DTPL_ENABLE_MPI:BOOL=ON or -DZoltan2_ENABLE_ParMETIS:BOOL=OFF"
92 
93 #else
94 
95 extern "C" {
96 #include "parmetis.h"
97 }
98 
99 #if (PARMETIS_MAJOR_VERSION < 4)
100 
101 // Zoltan2 requires ParMETIS v4.x.
102 // Make compilation fail for earlier versions of ParMETIS.
103 #error "Specified version of ParMETIS is not compatible with Zoltan2; upgrade to ParMETIS v4 or later, or build Zoltan2 without ParMETIS."
104 
105 #else
106 
107 // MPI and ParMETIS version requirements are met. Proceed.
108 
109 namespace Zoltan2 {
110 
111 template <typename Adapter>
112 class AlgParMETIS : public Algorithm<Adapter>
113 {
114 public:
115 
117  typedef typename Adapter::lno_t lno_t;
118  typedef typename Adapter::gno_t gno_t;
119  typedef typename Adapter::scalar_t scalar_t;
120  typedef typename Adapter::part_t part_t;
121 
122  typedef idx_t pm_idx_t;
123  typedef real_t pm_real_t;
124 
135  AlgParMETIS(const RCP<const Environment> &env__,
136  const RCP<const Comm<int> > &problemComm__,
137  const RCP<graphModel_t> &model__) :
138  env(env__), problemComm(problemComm__),
139  mpicomm(TeuchosConst2MPI(problemComm__)),
140  model(model__)
141  { }
142 
143  void partition(const RCP<PartitioningSolution<Adapter> > &solution);
144 
145 private:
146 
147  const RCP<const Environment> env;
148  const RCP<const Comm<int> > problemComm;
149  MPI_Comm mpicomm;
150  const RCP<GraphModel<typename Adapter::base_adapter_t> > model;
151 
152  void scale_weights(size_t n, ArrayView<StridedData<lno_t, scalar_t> > &fwgts,
153  pm_idx_t *iwgts);
154 };
155 
156 
158 template <typename Adapter>
160  const RCP<PartitioningSolution<Adapter> > &solution
161 )
162 {
163  HELLO;
164 
165  size_t numGlobalParts = solution->getTargetGlobalNumberOfParts();
166 
167  int np = problemComm->getSize();
168 
169  // Get vertex info
170  ArrayView<const gno_t> vtxgnos;
171  ArrayView<StridedData<lno_t, scalar_t> > vwgts;
172  int nVwgt = model->getNumWeightsPerVertex();
173  size_t nVtx = model->getVertexList(vtxgnos, vwgts);
174  pm_idx_t pm_nVtx;
175  TPL_Traits<pm_idx_t,size_t>::ASSIGN_TPL_T(pm_nVtx, nVtx, env);
176 
177  pm_idx_t *pm_vwgts = NULL;
178  if (nVwgt) {
179  pm_vwgts = new pm_idx_t[nVtx*nVwgt];
180  scale_weights(nVtx, vwgts, pm_vwgts);
181  }
182 
183  // Get edge info
184  ArrayView<const gno_t> adjgnos;
185  ArrayView<const lno_t> offsets;
186  ArrayView<StridedData<lno_t, scalar_t> > ewgts;
187  int nEwgt = model->getNumWeightsPerEdge();
188  size_t nEdge = model->getEdgeList(adjgnos, offsets, ewgts);
189 
190  pm_idx_t *pm_ewgts = NULL;
191  if (nEwgt) {
192  pm_ewgts = new pm_idx_t[nEdge*nEwgt];
193  scale_weights(nEdge, ewgts, pm_ewgts);
194  }
195 
196  // Convert index types for edges, if needed
197  pm_idx_t *pm_offsets;
198  TPL_Traits<pm_idx_t,lno_t>::ASSIGN_TPL_T_ARRAY(&pm_offsets, offsets, env);
199  pm_idx_t *pm_adjs;
200  TPL_Traits<pm_idx_t,gno_t>::ASSIGN_TPL_T_ARRAY(&pm_adjs, adjgnos, env);
201 
202  // Build vtxdist
203  pm_idx_t *pm_vtxdist = new pm_idx_t[np+1];
204  pm_vtxdist[0] = 0;
205  Teuchos::gatherAll(*problemComm, 1, &pm_nVtx, np, &(pm_vtxdist[1]));
206  for (int i = 2; i <= np; i++)
207  pm_vtxdist[i] += pm_vtxdist[i-1];
208 
209  // Create array for ParMETIS to return results in.
210  // Note: ParMETIS does not like NULL arrays,
211  // so add 1 to always have non-null.
212  // See Zoltan bug 4299.
213  pm_idx_t *pm_partList = new pm_idx_t[nVtx+1];
214 
215  // Get target part sizes and imbalance tolerances
216 
217  pm_idx_t pm_nCon = (nVwgt == 0 ? 1 : pm_idx_t(nVwgt));
218  pm_real_t *pm_partsizes = new pm_real_t[numGlobalParts*pm_nCon];
219  for (pm_idx_t dim = 0; dim < pm_nCon; dim++) {
220  if (!solution->criteriaHasUniformPartSizes(dim))
221  for (size_t i=0; i<numGlobalParts; i++)
222  pm_partsizes[i*pm_nCon+dim] =
223  pm_real_t(solution->getCriteriaPartSize(dim,i));
224  else
225  for (size_t i=0; i<numGlobalParts; i++)
226  pm_partsizes[i*pm_nCon+dim] = pm_real_t(1.) / pm_real_t(numGlobalParts);
227  }
228  pm_real_t *pm_imbTols = new pm_real_t[pm_nCon];
229  for (pm_idx_t dim = 0; dim < pm_nCon; dim++)
230  pm_imbTols[dim] = 1.05; // TODO: GET THE PARAMETER
231 
232  std::string parmetis_method("PARTKWAY");
233  pm_idx_t pm_wgtflag = 2*(nVwgt > 0) + (nEwgt > 0);
234  pm_idx_t pm_numflag = 0;
235 
236  pm_idx_t pm_nPart;
237  TPL_Traits<pm_idx_t,size_t>::ASSIGN_TPL_T(pm_nPart, numGlobalParts, env);
238 
239  if (parmetis_method == "PARTKWAY") {
240 
241  pm_idx_t pm_edgecut = -1;
242  pm_idx_t pm_options[3];
243  pm_options[0] = 0; // Use default options
244  pm_options[1] = 0; // Debug level (ignored if pm_options[0] == 0)
245  pm_options[2] = 0; // Seed (ignored if pm_options[0] == 0)
246 
247  ParMETIS_V3_PartKway(pm_vtxdist, pm_offsets, pm_adjs, pm_vwgts, pm_ewgts,
248  &pm_wgtflag, &pm_numflag, &pm_nCon, &pm_nPart,
249  pm_partsizes, pm_imbTols, pm_options,
250  &pm_edgecut, pm_partList, &mpicomm);
251  }
252  else if (parmetis_method == "ADAPTIVE_REPART") {
253  // Get object sizes
254  std::cout << "NOT READY FOR ADAPTIVE_REPART YET" << std::endl;
255  exit(-1);
256  }
257  else if (parmetis_method == "PART_GEOM") {
258  // Get coordinate info, too.
259  std::cout << "NOT READY FOR PART_GEOM YET" << std::endl;
260  exit(-1);
261  }
262 
263  // Clean up
264  delete [] pm_vtxdist;
265  delete [] pm_partsizes;
266  delete [] pm_imbTols;
267 
268  // Load answer into the solution.
269 
270  ArrayRCP<part_t> partList;
272  partList = ArrayRCP<part_t>((part_t *)pm_partList, 0, nVtx, true);
273  }
274  else {
275  // TODO Probably should have a TPL_Traits function to do the following
276  partList = ArrayRCP<part_t>(new part_t[nVtx], 0, nVtx, true);
277  for (size_t i = 0; i < nVtx; i++) {
278  partList[i] = part_t(pm_partList[i]);
279  }
280  delete [] pm_partList;
281  }
282 
283  solution->setParts(partList);
284 
285  env->memory("Zoltan2-ParMETIS: After creating solution");
286 
287  // Clean up copies made due to differing data sizes.
290 
291  if (nVwgt) delete [] pm_vwgts;
292  if (nEwgt) delete [] pm_ewgts;
293 }
294 
296 // Scale and round scalar_t weights (typically float or double) to
297 // ParMETIS' idx_t (typically int or long).
298 // subject to sum(weights) <= max_wgt_sum.
299 // Scale only if deemed necessary.
300 //
301 // Note that we use ceil() instead of round() to avoid
302 // rounding to zero weights.
303 // Based on Zoltan's scale_round_weights, mode 1
304 
305 template <typename Adapter>
307  size_t n,
308  ArrayView<StridedData<typename Adapter::lno_t,
309  typename Adapter::scalar_t> > &fwgts,
310  pm_idx_t *iwgts
311 )
312 {
313  const double INT_EPSILON = 1e-5;
314  const int nWgt = fwgts.size();
315 
316  int *nonint_local = new int[nWgt+nWgt];
317  int *nonint = nonint_local + nWgt;
318 
319  double *sum_wgt_local = new double[nWgt*4];
320  double *max_wgt_local = sum_wgt_local + nWgt;
321  double *sum_wgt = max_wgt_local + nWgt;
322  double *max_wgt = sum_wgt + nWgt;
323 
324  for (int i = 0; i < nWgt; i++) {
325  nonint_local[i] = 0;
326  sum_wgt_local[i] = 0.;
327  max_wgt_local[i] = 0;
328  }
329 
330  // Compute local sums of the weights
331  // Check whether all weights are integers
332  for (int j = 0; j < nWgt; j++) {
333  for (size_t i = 0; i < n; i++) {
334  double fw = double(fwgts[j][i]);
335  if (!nonint_local[j]) {
336  pm_idx_t tmp = (pm_idx_t) floor(fw + .5); /* Nearest int */
337  if (fabs((double)tmp-fw) > INT_EPSILON) {
338  nonint_local[j] = 1;
339  }
340  }
341  sum_wgt_local[j] += fw;
342  if (fw > max_wgt_local[j]) max_wgt_local[j] = fw;
343  }
344  }
345 
346  Teuchos::reduceAll<int,int>(*problemComm, Teuchos::REDUCE_MAX, nWgt,
347  nonint_local, nonint);
348  Teuchos::reduceAll<int,double>(*problemComm, Teuchos::REDUCE_SUM, nWgt,
349  sum_wgt_local, sum_wgt);
350  Teuchos::reduceAll<int,double>(*problemComm, Teuchos::REDUCE_MAX, nWgt,
351  max_wgt_local, max_wgt);
352 
353  const double max_wgt_sum = double(std::numeric_limits<pm_idx_t>::max()/8);
354  for (int j = 0; j < nWgt; j++) {
355  double scale = 1.;
356 
357  // Scaling needed if weights are not integers or weights'
358  // range is not sufficient
359  if (nonint[j] || (max_wgt[j]<=INT_EPSILON) || (sum_wgt[j]>max_wgt_sum)) {
360  /* Calculate scale factor */
361  if (sum_wgt[j] != 0.) scale = max_wgt_sum/sum_wgt[j];
362  }
363 
364  /* Convert weights to positive integers using the computed scale factor */
365  for (size_t i = 0; i < n; i++)
366  iwgts[i*nWgt+j] = (pm_idx_t) ceil(double(fwgts[j][i])*scale);
367  }
368  delete [] nonint_local;
369  delete [] sum_wgt_local;
370 }
371 
372 } // namespace Zoltan2
373 
374 #endif // PARMETIS VERSION 4 OR HIGHER CHECK
375 
376 #endif // HAVE_ZOLTAN2_MPI
377 
378 #endif // HAVE_ZOLTAN2_PARMETIS
379 
380 #endif
#define HELLO
static void DELETE_TPL_T_ARRAY(tpl_t **a)
Defines the PartitioningSolution class.
static void ASSIGN_TPL_T(tpl_t &a, zno_t b, const RCP< const Environment > &env)
AlgParMETIS(const RCP< const Environment > &env, const RCP< const Comm< int > > &problemComm, const RCP< GraphModel< typename Adapter::base_adapter_t > > &model)
Adapter::scalar_t scalar_t
A PartitioningSolution is a solution to a partitioning problem.
Adapter::part_t part_t
The StridedData class manages lists of weights or coordinates.
Algorithm defines the base class for all algorithms.
static void ASSIGN_TPL_T_ARRAY(tpl_t **a, ArrayView< const zno_t > &b, const RCP< const Environment > &env)
virtual void partition(const RCP< PartitioningSolution< Adapter > > &solution)
Partitioning method.
GraphModel defines the interface required for graph models.
Defines the GraphModel interface.
A gathering of useful namespace methods.