Zoltan2
Zoltan2_AlgScotch.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_ALGSCOTCH_HPP_
46 #define _ZOLTAN2_ALGSCOTCH_HPP_
47 
48 #include <Zoltan2_GraphModel.hpp>
49 #include <Zoltan2_Algorithm.hpp>
51 #include <Zoltan2_OrderingSolution.hpp> // BDD: needed by ordering method
52 #include <Zoltan2_Util.hpp>
53 #include <Zoltan2_TPLTraits.hpp>
54 
58 
60 
61 namespace Zoltan2 {
62 
63 // this function called by the two scotch types below
64 static inline void getScotchParameters(Teuchos::ParameterList & pl)
65 {
66  // bool parameter
67  pl.set("scotch_verbose", false, "verbose output",
69 
70  RCP<Teuchos::EnhancedNumberValidator<int>> scotch_level_Validator =
71  Teuchos::rcp( new Teuchos::EnhancedNumberValidator<int>(0, 1000, 1, 0) );
72  pl.set("scotch_level", 0, "scotch ordering - Level of the subgraph in the "
73  "separators tree for the initial graph at the root of the tree",
74  scotch_level_Validator);
75 
76  pl.set("scotch_imbalance_ratio", 0.2, "scotch ordering - Dissection "
77  "imbalance ratio", Environment::getAnyDoubleValidator());
78 
79  // bool parameter
80  pl.set("scotch_ordering_default", true, "use default scotch ordering "
81  "strategy", Environment::getBoolValidator());
82 
83  pl.set("scotch_ordering_strategy", "", "scotch ordering - Dissection "
84  "imbalance ratio");
85 }
86 
87 } // Zoltan2 namespace
88 
89 #ifndef HAVE_ZOLTAN2_SCOTCH
90 
91 namespace Zoltan2 {
92 
93 // Error handling for when Scotch is requested
94 // but Zoltan2 not built with Scotch.
95 
96 template <typename Adapter>
97 class AlgPTScotch : public Algorithm<Adapter>
98 {
99 public:
101  //AlgPTScotch(const RCP<const Environment> &env,
102  // const RCP<const Comm<int> > &problemComm,
103  // const RCP<GraphModel<typename Adapter::base_adapter_t> > &model
104  //) BDD: old inteface for reference
105  AlgPTScotch(const RCP<const Environment> &env,
106  const RCP<const Comm<int> > &problemComm,
107  const RCP<const base_adapter_t> &adapter
108  )
109  {
110  throw std::runtime_error(
111  "BUILD ERROR: Scotch requested but not compiled into Zoltan2.\n"
112  "Please set CMake flag Zoltan2_ENABLE_Scotch:BOOL=ON.");
113  }
114 
117  static void getValidParameters(ParameterList & pl)
118  {
120  }
121 };
122 }
123 #endif
124 
127 
128 #ifdef HAVE_ZOLTAN2_SCOTCH
129 
130 namespace Zoltan2 {
131 
132 // stdint.h for int64_t in scotch header
133 #include <stdint.h>
134 extern "C" {
135 #ifndef HAVE_ZOLTAN2_MPI
136 #include "scotch.h"
137 #else
138 #include "ptscotch.h"
139 #endif
140 }
141 
142 #ifdef SHOW_ZOLTAN2_SCOTCH_MEMORY
143 //
144 // Scotch keeps track of memory high water mark, but doesn't
145 // provide a way to get that number. So add this function:
146 // "size_t SCOTCH_getMemoryMax() { return memorymax;}"
147 // to src/libscotch/common_memory.c
148 //
149 // and this macro:
150 // "#define HAVE_SCOTCH_GETMEMORYMAX
151 // to include/ptscotch.h
152 //
153 // and compile scotch with -DCOMMON_MEMORY_TRACE
154 //
155 #ifdef HAVE_SCOTCH_GETMEMORYMAX
156  extern "C"{
157  extern size_t SCOTCH_getMemoryMax();
158  }
159 #else
160 #error "Either turn off ZOLTAN2_ENABLE_SCOTCH_MEMORY_REPORT in cmake configure, or see SHOW_ZOLTAN2_SCOTCH_MEMORY comment in Zoltan2_AlgScotch.hpp"
161 #endif // HAVE_SCOTCH_GETMEMORYMAX
162 #endif // SHOW_ZOLTAN2_SCOTCH_MEMORY
163 
164 template <typename Adapter>
165 class AlgPTScotch : public Algorithm<Adapter>
166 {
167 public:
168 
169  typedef typename Adapter::base_adapter_t base_adapter_t;
171  typedef typename Adapter::lno_t lno_t;
172  typedef typename Adapter::gno_t gno_t;
173  typedef typename Adapter::scalar_t scalar_t;
174  typedef typename Adapter::part_t part_t;
175  typedef typename Adapter::user_t user_t;
176  typedef typename Adapter::userCoord_t userCoord_t;
177 
178 // /*! Scotch constructor
179 // * \param env parameters for the problem and library configuration
180 // * \param problemComm the communicator for the problem
181 // * \param model a graph
182 // *
183 // * Preconditions: The parameters in the environment have been processed.
184 // * TODO: THIS IS A MINIMAL CONSTRUCTOR FOR NOW.
185 // * TODO: WHEN ADD SCOTCH ORDERING OR MAPPING, MOVE SCOTCH GRAPH CONSTRUCTION
186 // * TODO: TO THE CONSTRUCTOR SO THAT CODE MAY BE SHARED.
187 // */
188 // AlgPTScotch(const RCP<const Environment> &env__,
189 // const RCP<const Comm<int> > &problemComm__,
190 // const RCP<graphModel_t> &model__) :
191 // env(env__), problemComm(problemComm__),
192 //#ifdef HAVE_ZOLTAN2_MPI
193 // mpicomm(Teuchos::getRawMpiComm(*problemComm__)),
194 //#endif
195 // model(model__) BDD: olde interface for reference
196 
207  AlgPTScotch(const RCP<const Environment> &env__,
208  const RCP<const Comm<int> > &problemComm__,
209  const RCP<const IdentifierAdapter<user_t> > &adapter__) :
210  env(env__), problemComm(problemComm__),
211 #ifdef HAVE_ZOLTAN2_MPI
212  mpicomm(Teuchos::getRawMpiComm(*problemComm__)),
213 #endif
214  adapter(adapter__)
215  {
216  std::string errStr = "cannot build GraphModel from IdentifierAdapter, ";
217  errStr += "Scotch requires Graph, Matrix, or Mesh Adapter";
218  throw std::runtime_error(errStr);
219  }
220 
221  AlgPTScotch(const RCP<const Environment> &env__,
222  const RCP<const Comm<int> > &problemComm__,
223  const RCP<const VectorAdapter<user_t> > &adapter__) :
224  env(env__), problemComm(problemComm__),
225 #ifdef HAVE_ZOLTAN2_MPI
226  mpicomm(Teuchos::getRawMpiComm(*problemComm__)),
227 #endif
228  adapter(adapter__)
229  {
230  std::string errStr = "cannot build GraphModel from IdentifierAdapter, ";
231  errStr += "Scotch requires Graph, Matrix, or Mesh Adapter";
232  throw std::runtime_error(errStr);
233  }
234 
235  AlgPTScotch(const RCP<const Environment> &env__,
236  const RCP<const Comm<int> > &problemComm__,
237  const RCP<const GraphAdapter<user_t, userCoord_t> > &adapter__) :
238  env(env__), problemComm(problemComm__),
239 #ifdef HAVE_ZOLTAN2_MPI
240  mpicomm(Teuchos::getRawMpiComm(*problemComm__)),
241 #endif
242  adapter(adapter__), model_flags()
243  {
244  this->model_flags.reset();
245  }
246 
247  AlgPTScotch(const RCP<const Environment> &env__,
248  const RCP<const Comm<int> > &problemComm__,
249  const RCP<const MatrixAdapter<user_t, userCoord_t> > &adapter__) :
250  env(env__), problemComm(problemComm__),
251 #ifdef HAVE_ZOLTAN2_MPI
252  mpicomm(Teuchos::getRawMpiComm(*problemComm__)),
253 #endif
254  adapter(adapter__), model_flags()
255  {
256  this->model_flags.reset();
257 
258  const ParameterList &pl = env->getParameters();
259  const Teuchos::ParameterEntry *pe;
260 
261  std::string defString("default");
262  std::string objectOfInterest(defString);
263  pe = pl.getEntryPtr("objects_to_partition");
264  if (pe)
265  objectOfInterest = pe->getValue<std::string>(&objectOfInterest);
266 
267  if (objectOfInterest == defString ||
268  objectOfInterest == std::string("matrix_rows") )
269  this->model_flags.set(VERTICES_ARE_MATRIX_ROWS);
270  else if (objectOfInterest == std::string("matrix_columns"))
271  this->model_flags.set(VERTICES_ARE_MATRIX_COLUMNS);
272  else if (objectOfInterest == std::string("matrix_nonzeros"))
273  this->model_flags.set(VERTICES_ARE_MATRIX_NONZEROS);
274  }
275 
276  AlgPTScotch(const RCP<const Environment> &env__,
277  const RCP<const Comm<int> > &problemComm__,
278  const RCP<const MeshAdapter<user_t> > &adapter__) :
279  env(env__), problemComm(problemComm__),
280 #ifdef HAVE_ZOLTAN2_MPI
281  mpicomm(Teuchos::getRawMpiComm(*problemComm__)),
282 #endif
283  adapter(adapter__), model_flags()
284  {
285  this->model_flags.reset();
286 
287  const ParameterList &pl = env->getParameters();
288  const Teuchos::ParameterEntry *pe;
289 
290  std::string defString("default");
291  std::string objectOfInterest(defString);
292  pe = pl.getEntryPtr("objects_to_partition");
293  if (pe)
294  objectOfInterest = pe->getValue<std::string>(&objectOfInterest);
295 
296  if (objectOfInterest == defString ||
297  objectOfInterest == std::string("mesh_nodes") )
298  this->model_flags.set(VERTICES_ARE_MESH_NODES);
299  else if (objectOfInterest == std::string("mesh_elements"))
300  this->model_flags.set(VERTICES_ARE_MESH_ELEMENTS);
301  }
302 
305  static void getValidParameters(ParameterList & pl)
306  {
308  }
309 
310  void partition(const RCP<PartitioningSolution<Adapter> > &solution);
311 
312  int localOrder(const RCP<LocalOrderingSolution<lno_t> > &solution);
313  int globalOrder(const RCP<GlobalOrderingSolution<gno_t> > &solution);
314 
315 private:
316 
317  const RCP<const Environment> env;
318  const RCP<const Comm<int> > problemComm;
319 #ifdef HAVE_ZOLTAN2_MPI
320  const MPI_Comm mpicomm;
321 #endif
322  const RCP<const base_adapter_t> adapter;
323  modelFlag_t model_flags;
324  RCP<graphModel_t > model; // BDD:to be constructed
325 
326  void buildModel(bool isLocal);
327  void scale_weights(size_t n, StridedData<lno_t, scalar_t> &fwgts,
328  SCOTCH_Num *iwgts);
329  static int setStrategy(SCOTCH_Strat * c_strat_ptr, const ParameterList &pList, int procRank);
330 };
331 
333 template <typename Adapter>
334 void AlgPTScotch<Adapter>::buildModel(bool isLocal) {
335  HELLO;
336  const ParameterList &pl = env->getParameters();
337  const Teuchos::ParameterEntry *pe;
338 
339  std::string defString("default");
340  std::string symParameter(defString);
341  pe = pl.getEntryPtr("symmetrize_graph");
342  if (pe){
343  symParameter = pe->getValue<std::string>(&symParameter);
344  if (symParameter == std::string("transpose"))
345  this->model_flags.set(SYMMETRIZE_INPUT_TRANSPOSE);
346  else if (symParameter == std::string("bipartite"))
347  this->model_flags.set(SYMMETRIZE_INPUT_BIPARTITE); }
348 
349  bool sgParameter = false;
350  pe = pl.getEntryPtr("subset_graph");
351  if (pe)
352  sgParameter = pe->getValue(&sgParameter);
353  if (sgParameter)
354  this->model_flags.set(BUILD_SUBSET_GRAPH);
355 
356  this->model_flags.set(REMOVE_SELF_EDGES);
357  this->model_flags.set(GENERATE_CONSECUTIVE_IDS);
358  if (isLocal) {
359  HELLO; // only for ordering!
360  this->model_flags.set(BUILD_LOCAL_GRAPH);
361  }
362 
363  this->env->debug(DETAILED_STATUS, " building graph model");
364  this->model = rcp(new graphModel_t(this->adapter,
365  this->env,
366  this->problemComm,
367  this->model_flags));
368  this->env->debug(DETAILED_STATUS, " graph model built");
369 }
370 
371 template <typename Adapter>
373  const RCP<PartitioningSolution<Adapter> > &solution
374 )
375 {
376  HELLO;
377  this->buildModel(false);
378 
379  size_t numGlobalParts = solution->getTargetGlobalNumberOfParts();
380 
381  SCOTCH_Num partnbr=0;
382  TPL_Traits<SCOTCH_Num, size_t>::ASSIGN(partnbr, numGlobalParts);
383 
384 #ifdef HAVE_ZOLTAN2_MPI
385  int ierr = 0;
386  int me = problemComm->getRank();
387 
388  const SCOTCH_Num baseval = 0; // Base value for array indexing.
389  // GraphModel returns GNOs from base 0.
390 
391  SCOTCH_Strat stratstr; // Strategy string
392  // TODO: Set from parameters
393  SCOTCH_stratInit(&stratstr);
394 
395  // Allocate and initialize PTScotch Graph data structure.
396  SCOTCH_Dgraph *gr = SCOTCH_dgraphAlloc(); // Scotch distributed graph
397  ierr = SCOTCH_dgraphInit(gr, mpicomm);
398 
399  env->globalInputAssertion(__FILE__, __LINE__, "SCOTCH_dgraphInit",
400  !ierr, BASIC_ASSERTION, problemComm);
401 
402  // Get vertex info
403  ArrayView<const gno_t> vtxID;
404  ArrayView<StridedData<lno_t, scalar_t> > vwgts;
405  size_t nVtx = model->getVertexList(vtxID, vwgts);
406  SCOTCH_Num vertlocnbr=0;
407  TPL_Traits<SCOTCH_Num, size_t>::ASSIGN(vertlocnbr, nVtx);
408  SCOTCH_Num vertlocmax = vertlocnbr; // Assumes no holes in global nums.
409 
410  // Get edge info
411  ArrayView<const gno_t> edgeIds;
412  ArrayView<const lno_t> offsets;
413  ArrayView<StridedData<lno_t, scalar_t> > ewgts;
414 
415  size_t nEdge = model->getEdgeList(edgeIds, offsets, ewgts);
416 
417  SCOTCH_Num edgelocnbr=0;
418  TPL_Traits<SCOTCH_Num, size_t>::ASSIGN(edgelocnbr, nEdge);
419  const SCOTCH_Num edgelocsize = edgelocnbr; // Assumes adj array is compact.
420 
421  SCOTCH_Num *vertloctab; // starting adj/vtx
423 
424  SCOTCH_Num *edgeloctab; // adjacencies
426 
427  // We don't use these arrays, but we need them as arguments to Scotch.
428  SCOTCH_Num *vendloctab = NULL; // Assume consecutive storage for adj
429  SCOTCH_Num *vlblloctab = NULL; // Vertex label array
430  SCOTCH_Num *edgegsttab = NULL; // Array for ghost vertices
431 
432  // Get weight info.
433  SCOTCH_Num *velotab = NULL; // Vertex weights
434  SCOTCH_Num *edlotab = NULL; // Edge weights
435 
436  int nVwgts = model->getNumWeightsPerVertex();
437  int nEwgts = model->getNumWeightsPerEdge();
438  if (nVwgts > 1 && me == 0) {
439  std::cerr << "Warning: NumWeightsPerVertex is " << nVwgts
440  << " but Scotch allows only one weight. "
441  << " Zoltan2 will use only the first weight per vertex."
442  << std::endl;
443  }
444  if (nEwgts > 1 && me == 0) {
445  std::cerr << "Warning: NumWeightsPerEdge is " << nEwgts
446  << " but Scotch allows only one weight. "
447  << " Zoltan2 will use only the first weight per edge."
448  << std::endl;
449  }
450 
451  if (nVwgts) {
452  velotab = new SCOTCH_Num[nVtx+1]; // +1 since Scotch wants all procs
453  // to have non-NULL arrays
454  scale_weights(nVtx, vwgts[0], velotab);
455  }
456 
457  if (nEwgts) {
458  edlotab = new SCOTCH_Num[nEdge+1]; // +1 since Scotch wants all procs
459  // to have non-NULL arrays
460  scale_weights(nEdge, ewgts[0], edlotab);
461  }
462 
463  // Build PTScotch distributed data structure
464  ierr = SCOTCH_dgraphBuild(gr, baseval, vertlocnbr, vertlocmax,
465  vertloctab, vendloctab, velotab, vlblloctab,
466  edgelocnbr, edgelocsize,
467  edgeloctab, edgegsttab, edlotab);
468 
469  env->globalInputAssertion(__FILE__, __LINE__, "SCOTCH_dgraphBuild",
470  !ierr, BASIC_ASSERTION, problemComm);
471 
472  // Create array for Scotch to return results in.
473  SCOTCH_Num *partloctab = new SCOTCH_Num[nVtx+1];
474  // Note: Scotch does not like NULL arrays, so add 1 to always have non-null.
475  // ParMETIS has this same "feature." See Zoltan bug 4299.
476 
477  // Get target part sizes
478  float *partsizes = new float[numGlobalParts];
479  if (!solution->criteriaHasUniformPartSizes(0))
480  for (size_t i=0; i<numGlobalParts; i++)
481  partsizes[i] = solution->getCriteriaPartSize(0, i);
482  else
483  for (size_t i=0; i<numGlobalParts; i++)
484  partsizes[i] = 1.0 / float(numGlobalParts);
485 
486  // Allocate and initialize PTScotch target architecture data structure
487  SCOTCH_Arch archdat;
488  SCOTCH_archInit(&archdat);
489 
490  SCOTCH_Num velosum = 0;
491  SCOTCH_dgraphSize (gr, &velosum, NULL, NULL, NULL);
492  SCOTCH_Num *goalsizes = new SCOTCH_Num[partnbr];
493  // TODO: The goalsizes are set as in Zoltan; not sure it is correct there
494  // or here.
495  // It appears velosum is global NUMBER of vertices, not global total
496  // vertex weight. I think we should use the latter.
497  // Fix this when we add vertex weights.
498  for (SCOTCH_Num i = 0; i < partnbr; i++)
499  goalsizes[i] = SCOTCH_Num(ceil(velosum * partsizes[i]));
500  delete [] partsizes;
501 
502  SCOTCH_archCmpltw(&archdat, partnbr, goalsizes);
503 
504  // Call partitioning; result returned in partloctab.
505  ierr = SCOTCH_dgraphMap(gr, &archdat, &stratstr, partloctab);
506 
507  env->globalInputAssertion(__FILE__, __LINE__, "SCOTCH_dgraphMap",
508  !ierr, BASIC_ASSERTION, problemComm);
509 
510  SCOTCH_archExit(&archdat);
511  delete [] goalsizes;
512 
513  // TODO - metrics
514 
515 #ifdef SHOW_ZOLTAN2_SCOTCH_MEMORY
516  int me = env->comm_->getRank();
517 #endif
518 
519 #ifdef HAVE_SCOTCH_ZOLTAN2_GETMEMORYMAX
520  if (me == 0){
521  size_t scotchBytes = SCOTCH_getMemoryMax();
522  std::cout << "Rank " << me << ": Maximum bytes used by Scotch: ";
523  std::cout << scotchBytes << std::endl;
524  }
525 #endif
526 
527  // Clean up PTScotch
528  SCOTCH_dgraphExit(gr);
529  free(gr);
530  SCOTCH_stratExit(&stratstr);
531 
532  // Load answer into the solution.
533 
534  ArrayRCP<part_t> partList;
535  if (nVtx > 0)
536  TPL_Traits<part_t, SCOTCH_Num>::SAVE_ARRAYRCP(&partList, partloctab, nVtx);
538 
539  solution->setParts(partList);
540 
541  env->memory("Zoltan2-Scotch: After creating solution");
542 
543  // Clean up copies made due to differing data sizes.
546 
547  if (nVwgts) delete [] velotab;
548  if (nEwgts) delete [] edlotab;
549 
550 #else // DO NOT HAVE MPI
551 
552  // TODO: Handle serial case with calls to Scotch.
553  // TODO: For now, assign everything to rank 0 and assume only one part.
554  // TODO: Can probably use the code above for loading solution,
555  // TODO: instead of duplicating it here.
556  // TODO
557  // TODO: Actual logic should call Scotch when number of processes == 1.
558  ArrayView<const gno_t> vtxID;
559  ArrayView<StridedData<lno_t, scalar_t> > vwgts;
560  size_t nVtx = model->getVertexList(vtxID, vwgts);
561 
562  ArrayRCP<part_t> partList(new part_t[nVtx], 0, nVtx, true);
563  for (size_t i = 0; i < nVtx; i++) partList[i] = 0;
564 
565  solution->setParts(partList);
566 
567 #endif // DO NOT HAVE MPI
568 }
569 
571 // Scale and round scalar_t weights (typically float or double) to
572 // SCOTCH_Num (typically int or long).
573 // subject to sum(weights) <= max_wgt_sum.
574 // Only scale if deemed necessary.
575 //
576 // Note that we use ceil() instead of round() to avoid
577 // rounding to zero weights.
578 // Based on Zoltan's scale_round_weights, mode 1.
579 
580 template <typename Adapter>
582  size_t n,
584  SCOTCH_Num *iwgts
585 )
586 {
587  const double INT_EPSILON = 1e-5;
588 
589  SCOTCH_Num nonint, nonint_local = 0;
590  double sum_wgt, sum_wgt_local = 0.;
591  double max_wgt, max_wgt_local = 0.;
592 
593  // Compute local sums of the weights
594  // Check whether all weights are integers
595  for (size_t i = 0; i < n; i++) {
596  double fw = double(fwgts[i]);
597  if (!nonint_local){
598  SCOTCH_Num tmp = (SCOTCH_Num) floor(fw + .5); /* Nearest int */
599  if (fabs((double)tmp-fw) > INT_EPSILON) {
600  nonint_local = 1;
601  }
602  }
603  sum_wgt_local += fw;
604  if (fw > max_wgt_local) max_wgt_local = fw;
605  }
606 
607  Teuchos::reduceAll<int,SCOTCH_Num>(*problemComm, Teuchos::REDUCE_MAX, 1,
608  &nonint_local, &nonint);
609  Teuchos::reduceAll<int,double>(*problemComm, Teuchos::REDUCE_SUM, 1,
610  &sum_wgt_local, &sum_wgt);
611  Teuchos::reduceAll<int,double>(*problemComm, Teuchos::REDUCE_MAX, 1,
612  &max_wgt_local, &max_wgt);
613 
614  double scale = 1.;
615  const double max_wgt_sum = double(SCOTCH_NUMMAX/8);
616 
617  // Scaling needed if weights are not integers or weights'
618  // range is not sufficient
619  if (nonint || (max_wgt <= INT_EPSILON) || (sum_wgt > max_wgt_sum)) {
620  /* Calculate scale factor */
621  if (sum_wgt != 0.) scale = max_wgt_sum/sum_wgt;
622  }
623 
624  /* Convert weights to positive integers using the computed scale factor */
625  for (size_t i = 0; i < n; i++)
626  iwgts[i] = (SCOTCH_Num) ceil(double(fwgts[i])*scale);
627 
628 }
629 
630 template <typename Adapter>
631 int AlgPTScotch<Adapter>::setStrategy(SCOTCH_Strat * c_strat_ptr, const ParameterList &pList, int procRank) {
632  // get ordering parameters from parameter list
633  bool bPrintOutput = false; // will be true if rank 0 and verbose is true
634  const Teuchos::ParameterEntry *pe;
635 
636  if (procRank == 0) {
637  pe = pList.getEntryPtr("scotch_verbose");
638  if (pe) {
639  bPrintOutput = pe->getValue(&bPrintOutput);
640  }
641  }
642 
643  // get parameter setting for ordering default true/false
644  bool scotch_ordering_default = true;
645  pe = pList.getEntryPtr("scotch_ordering_default");
646  if (pe) {
647  scotch_ordering_default = pe->getValue(&scotch_ordering_default);
648  }
649  if (bPrintOutput) {
650  std::cout <<
651  "Scotch: Ordering default setting (parameter: scotch_ordering_default): "
652  << (scotch_ordering_default ? "True" : "False" ) << std::endl;
653  }
654 
655  // set up error code for return
656  int ierr = 1; // will be set 0 if successful
657 
658  if (scotch_ordering_default) {
659  // get parameter scotch_level
660  int scotch_level = 0;
661  pe = pList.getEntryPtr("scotch_level");
662  if (pe) {
663  scotch_level = pe->getValue(&scotch_level);
664  }
665  if (bPrintOutput) {
666  std::cout << "Scotch: Ordering level (parameter: scotch_level): " <<
667  scotch_level << std::endl;
668  }
669 
670  // get parameter scotch_imbalance_ratio
671  double scotch_imbalance_ratio = 0.2;
672  pe = pList.getEntryPtr("scotch_imbalance_ratio");
673  if (pe) {
674  scotch_imbalance_ratio = pe->getValue(&scotch_imbalance_ratio);
675  }
676  if (bPrintOutput) {
677  std::cout << "Scotch: Ordering dissection imbalance ratio "
678  "(parameter: scotch_imbalance_ratio): "
679  << scotch_imbalance_ratio << std::endl;
680  }
681 
682  SCOTCH_stratInit(c_strat_ptr);
683 
684  ierr = SCOTCH_stratGraphOrderBuild( c_strat_ptr,
685  SCOTCH_STRATLEVELMAX | SCOTCH_STRATLEVELMIN |
686  SCOTCH_STRATLEAFSIMPLE | SCOTCH_STRATSEPASIMPLE,
687  scotch_level, scotch_imbalance_ratio); // based on Siva's example
688  }
689  else {
690  // get parameter scotch_ordering_strategy
691  std::string scotch_ordering_strategy_string("");
692  pe = pList.getEntryPtr("scotch_ordering_strategy");
693  if (pe) {
694  scotch_ordering_strategy_string =
695  pe->getValue(&scotch_ordering_strategy_string);
696  }
697  if (bPrintOutput) {
698  std::cout << "Scotch ordering strategy"
699  " (parameter: scotch_ordering_strategy): " <<
700  scotch_ordering_strategy_string << std::endl;
701  }
702 
703  SCOTCH_stratInit(c_strat_ptr);
704  ierr = SCOTCH_stratGraphOrder( c_strat_ptr,
705  scotch_ordering_strategy_string.c_str());
706  }
707 
708  return ierr;
709 }
710 
711 template <typename Adapter>
713  const RCP<GlobalOrderingSolution<gno_t> > &solution) {
714  throw std::logic_error("AlgPTScotch does not yet support global ordering.");
715 }
716 
717 template <typename Adapter>
719  const RCP<LocalOrderingSolution<lno_t> > &solution) {
720  // TODO: translate teuchos sublist parameters to scotch strategy string
721  // TODO: construct local graph model
722  // TODO: solve with scotch
723  // TODO: set solution fields
724 
725  HELLO; // say hi so that we know we have called this method
726  int me = problemComm->getRank();
727  const ParameterList &pl = env->getParameters();
728  const Teuchos::ParameterEntry *pe;
729 
730  bool isVerbose = false;
731  pe = pl.getEntryPtr("scotch_verbose");
732  if (pe)
733  isVerbose = pe->getValue(&isVerbose);
734 
735  // build a local graph model
736  this->buildModel(true);
737  if (isVerbose && me==0) {
738  std::cout << "Built local graph model." << std::endl;
739  }
740 
741  // based off of Siva's example
742  SCOTCH_Strat c_strat_ptr; // strategy
743  SCOTCH_Graph c_graph_ptr; // pointer to scotch graph
744  int ierr;
745 
746  ierr = SCOTCH_graphInit( &c_graph_ptr);
747  if ( ierr != 0) {
748  throw std::runtime_error("Failed to initialize Scotch graph!");
749  } else if (isVerbose && me == 0) {
750  std::cout << "Initialized Scotch graph." << std::endl;
751  }
752 
753  // Get vertex info
754  ArrayView<const gno_t> vtxID;
755  ArrayView<StridedData<lno_t, scalar_t> > vwgts;
756  size_t nVtx = model->getVertexList(vtxID, vwgts);
757  SCOTCH_Num vertnbr=0;
759 
760  // Get edge info
761  ArrayView<const gno_t> edgeIds;
762  ArrayView<const lno_t> offsets;
763  ArrayView<StridedData<lno_t, scalar_t> > ewgts;
764 
765  size_t nEdge = model->getEdgeList(edgeIds, offsets, ewgts);
766  SCOTCH_Num edgenbr=0;
768 
769  SCOTCH_Num *verttab; // starting adj/vtx
771 
772  SCOTCH_Num *edgetab; // adjacencies
774 
775  // We don't use these arrays, but we need them as arguments to Scotch.
776  SCOTCH_Num *vendtab = NULL; // Assume consecutive storage for adj
777  //SCOTCH_Num *vendtab = verttab+1; // Assume consecutive storage for adj
778  // Get weight info.
779  SCOTCH_Num *velotab = NULL; // Vertex weights
780  SCOTCH_Num *vlbltab = NULL; // vertes labels
781  SCOTCH_Num *edlotab = NULL; // Edge weights
782 
783  int nVwgts = model->getNumWeightsPerVertex();
784  int nEwgts = model->getNumWeightsPerEdge();
785  if (nVwgts > 1 && me == 0) {
786  std::cerr << "Warning: NumWeightsPerVertex is " << nVwgts
787  << " but Scotch allows only one weight. "
788  << " Zoltan2 will use only the first weight per vertex."
789  << std::endl;
790  }
791  if (nEwgts > 1 && me == 0) {
792  std::cerr << "Warning: NumWeightsPerEdge is " << nEwgts
793  << " but Scotch allows only one weight. "
794  << " Zoltan2 will use only the first weight per edge."
795  << std::endl;
796  }
797 
798  if (nVwgts) {
799  velotab = new SCOTCH_Num[nVtx+1]; // +1 since Scotch wants all procs
800  // to have non-NULL arrays
801  scale_weights(nVtx, vwgts[0], velotab);
802  }
803 
804  if (nEwgts) {
805  edlotab = new SCOTCH_Num[nEdge+1]; // +1 since Scotch wants all procs
806  // to have non-NULL arrays
807  scale_weights(nEdge, ewgts[0], edlotab);
808  }
809 
810  // build scotch graph
811  int baseval = 0;
812  ierr = 1;
813  ierr = SCOTCH_graphBuild( &c_graph_ptr, baseval,
814  vertnbr, verttab, vendtab, velotab, vlbltab,
815  edgenbr, edgetab, edlotab);
816  if (ierr != 0) {
817  throw std::runtime_error("Failed to build Scotch graph!");
818  } else if (isVerbose && me == 0) {
819  std::cout << "Built Scotch graph." << std::endl;
820  }
821 
822  ierr = SCOTCH_graphCheck(&c_graph_ptr);
823  if (ierr != 0) {
824  throw std::runtime_error("Graph is inconsistent!");
825  } else if (isVerbose && me == 0) {
826  std::cout << "Graph is consistent." << std::endl;
827  }
828 
829  // set the strategy
830  ierr = AlgPTScotch<Adapter>::setStrategy(&c_strat_ptr, pl, me);
831 
832  if (ierr != 0) {
833  throw std::runtime_error("Can't build strategy!");
834  }else if (isVerbose && me == 0) {
835  std::cout << "Graphing strategy built." << std::endl;
836  }
837 
838  // Allocate results
839  SCOTCH_Num cblk = 0;
840  SCOTCH_Num *permtab; // permutation array
841  SCOTCH_Num *peritab; // inverse permutation array
842  SCOTCH_Num *rangetab; // separator range array
843  SCOTCH_Num *treetab; // separator tree
844 
846  permtab = reinterpret_cast<SCOTCH_Num*>(solution->getPermutationView(false));
847  peritab = reinterpret_cast<SCOTCH_Num*>(solution->getPermutationView(true));
848  rangetab = reinterpret_cast<SCOTCH_Num*>(solution->getSeparatorRangeView());
849  treetab = reinterpret_cast<SCOTCH_Num*>(solution->getSeparatorTreeView());
850  }
851  else {
852  permtab = new SCOTCH_Num[nVtx];
853  peritab = new SCOTCH_Num[nVtx];
854  rangetab = new SCOTCH_Num[nVtx+1];
855  treetab = new SCOTCH_Num[nVtx];
856  }
857 
858  ierr = SCOTCH_graphOrder(&c_graph_ptr, &c_strat_ptr, permtab, peritab,
859  &cblk, rangetab, treetab);
860  if (ierr != 0) {
861  throw std::runtime_error("Could not compute ordering!!");
862  } else if(isVerbose && me == 0) {
863  std::cout << "Ordering computed." << std::endl;
864  }
865 
866  lno_t nSepBlocks;
867  TPL_Traits<lno_t, SCOTCH_Num>::ASSIGN(nSepBlocks, cblk);
868  solution->setNumSeparatorBlocks(nSepBlocks);
869 
871 
872  const ArrayRCP<lno_t> arv_perm = solution->getPermutationRCP(false);
873  for (size_t i = 0; i < nVtx; i++)
874  TPL_Traits<lno_t, SCOTCH_Num>::ASSIGN(arv_perm[i], permtab[i]);
875  delete [] permtab;
876 
877  const ArrayRCP<lno_t> arv_peri = solution->getPermutationRCP(true);
878  for (size_t i = 0; i < nVtx; i++)
879  TPL_Traits<lno_t, SCOTCH_Num>::ASSIGN(arv_peri[i], peritab[i]);
880  delete [] peritab;
881 
882  const ArrayRCP<lno_t> arv_range = solution->getSeparatorRangeRCP();
883  for (size_t i = 0; i <= nVtx; i++)
884  TPL_Traits<lno_t, SCOTCH_Num>::ASSIGN(arv_range[i], rangetab[i]);
885  delete [] rangetab;
886 
887  const ArrayRCP<lno_t> arv_tree = solution->getSeparatorTreeRCP();
888  for (size_t i = 0; i < nVtx; i++)
889  TPL_Traits<lno_t, SCOTCH_Num>::ASSIGN(arv_tree[i], treetab[i]);
890  delete [] treetab;
891  }
892 
893  solution->setHaveSeparator(true);
894 
895  // reclaim memory
896  // Clean up copies made due to differing data sizes.
899 
900  if (nVwgts) delete [] velotab;
901  if (nEwgts) delete [] edlotab;
902 
903  SCOTCH_stratExit(&c_strat_ptr);
904  SCOTCH_graphFree(&c_graph_ptr);
905 
906  if (isVerbose && problemComm->getRank() == 0) {
907  std::cout << "Freed Scotch graph!" << std::endl;
908  }
909  return 0;
910 }
911 
912 } // namespace Zoltan2
913 
914 #endif // HAVE_ZOLTAN2_SCOTCH
915 
917 
918 #endif
use columns as graph vertices
#define HELLO
Zoltan2::BaseAdapter< userTypes_t > base_adapter_t
Adapter::base_adapter_t base_adapter_t
IdentifierAdapter defines the interface for identifiers.
ignore invalid neighbors
use mesh nodes as vertices
virtual int localOrder(const RCP< LocalOrderingSolution< lno_t > > &solution)
Ordering method.
fast typical checks for valid arguments
MatrixAdapter defines the adapter interface for matrices.
GraphAdapter defines the interface for graph-based user data.
static RCP< Teuchos::BoolParameterEntryValidator > getBoolValidator()
Exists to make setting up validators less cluttered.
algorithm requires consecutive ids
MeshAdapter defines the interface for mesh input.
std::bitset< NUM_MODEL_FLAGS > modelFlag_t
model must symmetrize input
Defines the OrderingSolution class.
model must symmetrize input
virtual int globalOrder(const RCP< GlobalOrderingSolution< gno_t > > &solution)
Ordering method.
Defines the PartitioningSolution class.
static RCP< Teuchos::AnyNumberParameterEntryValidator > getAnyDoubleValidator()
Exists to make setting up validators less cluttered.
static void SAVE_ARRAYRCP(ArrayRCP< first_t > *a, second_t *b, size_t size)
sub-steps, each method&#39;s entry and exit
SparseMatrixAdapter_t::part_t part_t
use matrix rows as graph vertices
algorithm requires no self edges
static void getScotchParameters(Teuchos::ParameterList &pl)
Adapter::scalar_t scalar_t
A PartitioningSolution is a solution to a partitioning problem.
use nonzeros as graph vertices
Adapter::part_t part_t
VectorAdapter defines the interface for vector input.
The StridedData class manages lists of weights or coordinates.
Algorithm defines the base class for all algorithms.
static void ASSIGN(first_t &a, second_t b)
static void getValidParameters(ParameterList &pl)
Set up validators specific to this algorithm.
virtual void partition(const RCP< PartitioningSolution< Adapter > > &solution)
Partitioning method.
Traits class to handle conversions between gno_t/lno_t and TPL data types (e.g., ParMETIS&#39;s idx_t...
use mesh elements as vertices
GraphModel defines the interface required for graph models.
static void ASSIGN_ARRAY(first_t **a, ArrayView< second_t > &b)
AlgPTScotch(const RCP< const Environment > &env, const RCP< const Comm< int > > &problemComm, const RCP< const base_adapter_t > &adapter)
Defines the GraphModel interface.
A gathering of useful namespace methods.
static void DELETE_ARRAY(first_t **a)
model represents graph within only one rank