Zoltan2
Zoltan2_AlgScotch.hpp
Go to the documentation of this file.
00001 // @HEADER
00002 //
00003 // ***********************************************************************
00004 //
00005 //   Zoltan2: A package of combinatorial algorithms for scientific computing
00006 //                  Copyright 2012 Sandia Corporation
00007 //
00008 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
00009 // the U.S. Government retains certain rights in this software.
00010 //
00011 // Redistribution and use in source and binary forms, with or without
00012 // modification, are permitted provided that the following conditions are
00013 // met:
00014 //
00015 // 1. Redistributions of source code must retain the above copyright
00016 // notice, this list of conditions and the following disclaimer.
00017 //
00018 // 2. Redistributions in binary form must reproduce the above copyright
00019 // notice, this list of conditions and the following disclaimer in the
00020 // documentation and/or other materials provided with the distribution.
00021 //
00022 // 3. Neither the name of the Corporation nor the names of the
00023 // contributors may be used to endorse or promote products derived from
00024 // this software without specific prior written permission.
00025 //
00026 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
00027 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00028 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00029 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
00030 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00031 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00032 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00033 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00034 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00035 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00036 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00037 //
00038 // Questions? Contact Karen Devine      (kddevin@sandia.gov)
00039 //                    Erik Boman        (egboman@sandia.gov)
00040 //                    Siva Rajamanickam (srajama@sandia.gov)
00041 //
00042 // ***********************************************************************
00043 //
00044 // @HEADER
00045 #ifndef _ZOLTAN2_ALGSCOTCH_HPP_
00046 #define _ZOLTAN2_ALGSCOTCH_HPP_
00047 
00048 #include <Zoltan2_GraphModel.hpp>
00049 #include <Zoltan2_Algorithm.hpp>
00050 #include <Zoltan2_PartitioningSolution.hpp>
00051 #include <Zoltan2_Util.hpp>
00052 #include <Zoltan2_TPLTraits.hpp>
00053 
00057 
00059 #ifndef HAVE_ZOLTAN2_SCOTCH
00060 
00061 namespace Zoltan2 {
00062 // Error handling for when Scotch is requested
00063 // but Zoltan2 not built with Scotch.
00064 
00065 template <typename Adapter>
00066 class AlgPTScotch : public Algorithm<Adapter>
00067 {
00068 public:
00069   AlgPTScotch(const RCP<const Environment> &env,
00070               const RCP<const Comm<int> > &problemComm,
00071               const RCP<GraphModel<typename Adapter::base_adapter_t> > &model
00072   )
00073   {
00074     throw std::runtime_error(
00075           "BUILD ERROR:  Scotch requested but not compiled into Zoltan2.\n"
00076           "Please set CMake flag Zoltan2_ENABLE_Scotch:BOOL=ON.");
00077   }
00078 };
00079 }
00080 #endif
00081 
00084 
00085 #ifdef HAVE_ZOLTAN2_SCOTCH
00086 
00087 namespace Zoltan2 {
00088 
00089 // stdint.h for int64_t in scotch header
00090 #include <stdint.h>
00091 extern "C" {
00092 #ifndef HAVE_ZOLTAN2_MPI
00093 #include "scotch.h"
00094 #else
00095 #include "ptscotch.h"
00096 #endif
00097 }
00098 
00099 #ifdef SHOW_ZOLTAN2_SCOTCH_MEMORY
00100 //
00101 // Scotch keeps track of memory high water mark, but doesn't
00102 // provide a way to get that number.  So add this function:
00103 //   "size_t SCOTCH_getMemoryMax() { return memorymax;}"
00104 // to src/libscotch/common_memory.c
00105 //
00106 // and this macro:
00107 //   "#define HAVE_SCOTCH_GETMEMORYMAX
00108 // to include/ptscotch.h
00109 //
00110 // and compile scotch with -DCOMMON_MEMORY_TRACE
00111 //
00112 #ifdef HAVE_SCOTCH_GETMEMORYMAX
00113   extern "C"{
00114     extern size_t SCOTCH_getMemoryMax();
00115   }
00116 #else
00117 #error "Either turn off ZOLTAN2_ENABLE_SCOTCH_MEMORY_REPORT in cmake configure, or see SHOW_ZOLTAN2_SCOTCH_MEMORY comment in Zoltan2_AlgScotch.hpp"
00118 #endif  // HAVE_SCOTCH_GETMEMORYMAX
00119 #endif  // SHOW_ZOLTAN2_SCOTCH_MEMORY
00120 
00121 template <typename Adapter>
00122 class AlgPTScotch : public Algorithm<Adapter>
00123 {
00124 public:
00125 
00126   typedef GraphModel<typename Adapter::base_adapter_t> graphModel_t;
00127   typedef typename Adapter::lno_t lno_t;
00128   typedef typename Adapter::gno_t gno_t;
00129   typedef typename Adapter::scalar_t scalar_t;
00130   typedef typename Adapter::part_t part_t;
00131 
00142   AlgPTScotch(const RCP<const Environment> &env__,
00143               const RCP<const Comm<int> > &problemComm__,
00144               const RCP<graphModel_t> &model__) :
00145     env(env__), problemComm(problemComm__), 
00146 #ifdef HAVE_ZOLTAN2_MPI
00147     mpicomm(TeuchosConst2MPI(problemComm__)),
00148 #endif
00149     model(model__)
00150   { }
00151 
00152   void partition(const RCP<PartitioningSolution<Adapter> > &solution);
00153 
00154 private:
00155 
00156   const RCP<const Environment> env;
00157   const RCP<const Comm<int> > problemComm;
00158 #ifdef HAVE_ZOLTAN2_MPI
00159   const MPI_Comm mpicomm;
00160 #endif
00161   const RCP<GraphModel<typename Adapter::base_adapter_t> > model;
00162 
00163   void scale_weights(size_t n, StridedData<lno_t, scalar_t> &fwgts,
00164                      SCOTCH_Num *iwgts);
00165 };
00166 
00167 
00169 template <typename Adapter>
00170 void AlgPTScotch<Adapter>::partition(
00171   const RCP<PartitioningSolution<Adapter> > &solution
00172 )
00173 {
00174   HELLO;
00175 
00176   size_t numGlobalParts = solution->getTargetGlobalNumberOfParts();
00177 
00178   SCOTCH_Num partnbr=0;
00179   TPL_Traits<SCOTCH_Num, size_t>::ASSIGN_TPL_T(partnbr, numGlobalParts, env);
00180 
00181 #ifdef HAVE_ZOLTAN2_MPI
00182   int ierr = 0;
00183   int me = problemComm->getRank();
00184 
00185   const SCOTCH_Num  baseval = 0;  // Base value for array indexing.
00186                                   // GraphModel returns GNOs from base 0.
00187 
00188   SCOTCH_Strat stratstr;          // Strategy string
00189                                   // TODO:  Set from parameters
00190   SCOTCH_stratInit(&stratstr);
00191 
00192   // Allocate and initialize PTScotch Graph data structure.
00193   SCOTCH_Dgraph *gr = SCOTCH_dgraphAlloc();  // Scotch distributed graph
00194   ierr = SCOTCH_dgraphInit(gr, mpicomm);
00195 
00196   env->globalInputAssertion(__FILE__, __LINE__, "SCOTCH_dgraphInit", 
00197     !ierr, BASIC_ASSERTION, problemComm);
00198 
00199   // Get vertex info
00200   ArrayView<const gno_t> vtxID;
00201   ArrayView<StridedData<lno_t, scalar_t> > xyz;
00202   ArrayView<StridedData<lno_t, scalar_t> > vwgts;
00203   size_t nVtx = model->getVertexList(vtxID, xyz, vwgts);
00204   SCOTCH_Num vertlocnbr=0;
00205   TPL_Traits<SCOTCH_Num, size_t>::ASSIGN_TPL_T(vertlocnbr, nVtx, env);
00206   SCOTCH_Num vertlocmax = vertlocnbr; // Assumes no holes in global nums.
00207 
00208   // Get edge info
00209   ArrayView<const gno_t> edgeIds;
00210   ArrayView<const int>   procIds;
00211   ArrayView<const lno_t> offsets;
00212   ArrayView<StridedData<lno_t, scalar_t> > ewgts;
00213 
00214   size_t nEdge = model->getEdgeList(edgeIds, procIds, offsets, ewgts);
00215 
00216   SCOTCH_Num edgelocnbr=0;
00217   TPL_Traits<SCOTCH_Num, size_t>::ASSIGN_TPL_T(edgelocnbr, nEdge, env);
00218   const SCOTCH_Num edgelocsize = edgelocnbr;  // Assumes adj array is compact.
00219 
00220   SCOTCH_Num *vertloctab;  // starting adj/vtx
00221   TPL_Traits<SCOTCH_Num, lno_t>::ASSIGN_TPL_T_ARRAY(&vertloctab, offsets, env);
00222 
00223   SCOTCH_Num *edgeloctab;  // adjacencies
00224   TPL_Traits<SCOTCH_Num, gno_t>::ASSIGN_TPL_T_ARRAY(&edgeloctab, edgeIds, env);
00225 
00226   // We don't use these arrays, but we need them as arguments to Scotch.
00227   SCOTCH_Num *vendloctab = NULL;  // Assume consecutive storage for adj
00228   SCOTCH_Num *vlblloctab = NULL;  // Vertex label array
00229   SCOTCH_Num *edgegsttab = NULL;  // Array for ghost vertices
00230 
00231   // Get weight info.
00232   SCOTCH_Num *velotab = NULL;  // Vertex weights
00233   SCOTCH_Num *edlotab = NULL;  // Edge weights
00234 
00235   int nVwgts = model->getNumWeightsPerVertex();
00236   int nEwgts = model->getNumWeightsPerEdge();
00237   if (nVwgts > 1 && me == 0) {
00238     std::cerr << "Warning:  NumWeightsPerVertex is " << nVwgts 
00239               << " but Scotch allows only one weight. "
00240               << " Zoltan2 will use only the first weight per vertex."
00241               << std::endl;
00242   }
00243   if (nEwgts > 1 && me == 0) {
00244     std::cerr << "Warning:  NumWeightsPerEdge is " << nEwgts 
00245               << " but Scotch allows only one weight. "
00246               << " Zoltan2 will use only the first weight per edge."
00247               << std::endl;
00248   }
00249 
00250   if (nVwgts) {
00251     velotab = new SCOTCH_Num[nVtx+1];  // +1 since Scotch wants all procs 
00252                                        // to have non-NULL arrays
00253     scale_weights(nVtx, vwgts[0], velotab);
00254   }
00255 
00256   if (nEwgts) {
00257     edlotab = new SCOTCH_Num[nEdge+1];  // +1 since Scotch wants all procs 
00258                                          // to have non-NULL arrays
00259     scale_weights(nEdge, ewgts[0], edlotab);
00260   }
00261 
00262   // Build PTScotch distributed data structure
00263   ierr = SCOTCH_dgraphBuild(gr, baseval, vertlocnbr, vertlocmax,
00264                             vertloctab, vendloctab, velotab, vlblloctab,
00265                             edgelocnbr, edgelocsize,
00266                             edgeloctab, edgegsttab, edlotab);
00267 
00268   env->globalInputAssertion(__FILE__, __LINE__, "SCOTCH_dgraphBuild", 
00269     !ierr, BASIC_ASSERTION, problemComm);
00270 
00271   // Create array for Scotch to return results in.
00272   ArrayRCP<part_t> partList(new part_t[nVtx], 0, nVtx,true);
00273   SCOTCH_Num *partloctab = NULL;
00274   if (nVtx && (sizeof(SCOTCH_Num) == sizeof(part_t))) {
00275     // Can write directly into the solution's memory
00276     partloctab = (SCOTCH_Num *) partList.getRawPtr();
00277   }
00278   else {
00279     // Can't use solution memory directly; will have to copy later.
00280     // Note:  Scotch does not like NULL arrays, so add 1 to always have non-null.
00281     //        ParMETIS has this same "feature."  See Zoltan bug 4299.
00282     partloctab = new SCOTCH_Num[nVtx+1];
00283   }
00284 
00285   // Get target part sizes
00286   float *partsizes = new float[numGlobalParts];
00287   if (!solution->criteriaHasUniformPartSizes(0))
00288     for (size_t i=0; i<numGlobalParts; i++)
00289       partsizes[i] = solution->getCriteriaPartSize(0, i);
00290   else
00291     for (size_t i=0; i<numGlobalParts; i++)
00292       partsizes[i] = 1.0 / float(numGlobalParts);
00293 
00294   // Allocate and initialize PTScotch target architecture data structure
00295   SCOTCH_Arch archdat;
00296   SCOTCH_archInit(&archdat);
00297 
00298   SCOTCH_Num velosum = 0;
00299   SCOTCH_dgraphSize (gr, &velosum, NULL, NULL, NULL);
00300   SCOTCH_Num *goalsizes = new SCOTCH_Num[partnbr];
00301   // TODO: The goalsizes are set as in Zoltan; not sure it is correct there 
00302   // or here.
00303   // It appears velosum is global NUMBER of vertices, not global total 
00304   // vertex weight.  I think we should use the latter.
00305   // Fix this when we add vertex weights.
00306   for (SCOTCH_Num i = 0; i < partnbr; i++)
00307     goalsizes[i] = SCOTCH_Num(ceil(velosum * partsizes[i]));
00308   delete [] partsizes;
00309 
00310   SCOTCH_archCmpltw(&archdat, partnbr, goalsizes);
00311 
00312   // Call partitioning; result returned in partloctab.
00313   ierr = SCOTCH_dgraphMap(gr, &archdat, &stratstr, partloctab);
00314 
00315   env->globalInputAssertion(__FILE__, __LINE__, "SCOTCH_dgraphMap", 
00316     !ierr, BASIC_ASSERTION, problemComm);
00317 
00318   SCOTCH_archExit(&archdat);
00319   delete [] goalsizes;
00320 
00321   // TODO - metrics
00322 
00323 #ifdef SHOW_ZOLTAN2_SCOTCH_MEMORY
00324   int me = env->comm_->getRank();
00325 #endif
00326 
00327 #ifdef HAVE_SCOTCH_ZOLTAN2_GETMEMORYMAX
00328   if (me == 0){
00329     size_t scotchBytes = SCOTCH_getMemoryMax();
00330     std::cout << "Rank " << me << ": Maximum bytes used by Scotch: ";
00331     std::cout << scotchBytes << std::endl;
00332   }
00333 #endif
00334 
00335   // Clean up PTScotch
00336   SCOTCH_dgraphExit(gr);
00337   free(gr);
00338   SCOTCH_stratExit(&stratstr);
00339 
00340   // Load answer into the solution.
00341 
00342   if ((sizeof(SCOTCH_Num) != sizeof(part_t)) || (nVtx == 0)) {
00343     for (size_t i = 0; i < nVtx; i++) partList[i] = partloctab[i];
00344     delete [] partloctab;
00345   }
00346 
00347   ArrayRCP<const gno_t> gnos = arcpFromArrayView(vtxID);
00348 
00349   solution->setParts(gnos, partList, true);
00350 
00351   env->memory("Zoltan2-Scotch: After creating solution");
00352 
00353   // Clean up copies made due to differing data sizes.
00354   TPL_Traits<SCOTCH_Num, lno_t>::DELETE_TPL_T_ARRAY(&vertloctab);
00355   TPL_Traits<SCOTCH_Num, gno_t>::DELETE_TPL_T_ARRAY(&edgeloctab);
00356 
00357   if (nVwgts) delete [] velotab;
00358   if (nEwgts) delete [] edlotab;
00359 
00360 #else // DO NOT HAVE_MPI
00361 
00362   // TODO:  Handle serial case with calls to Scotch.
00363   // TODO:  For now, assign everything to rank 0 and assume only one part.
00364   // TODO:  Can probably use the code above for loading solution,
00365   // TODO:  instead of duplicating it here.
00366   // TODO
00367   // TODO:  Actual logic should call Scotch when number of processes == 1.
00368   ArrayView<const gno_t> vtxID;
00369   ArrayView<StridedData<lno_t, scalar_t> > xyz;
00370   ArrayView<StridedData<lno_t, scalar_t> > vwgts;
00371   size_t nVtx = model->getVertexList(vtxID, xyz, vwgts);
00372 
00373   ArrayRCP<part_t> partList(new part_t[nVtx], 0, nVtx, true);
00374   for (size_t i = 0; i < nVtx; i++) partList[i] = 0;
00375 
00376   ArrayRCP<const gno_t> gnos = arcpFromArrayView(vtxID);
00377   solution->setParts(gnos, partList, true);
00378 
00379 #endif // DO NOT HAVE_MPI
00380 }
00381 
00383 // Scale and round scalar_t weights (typically float or double) to 
00384 // SCOTCH_Num (typically int or long).
00385 // subject to sum(weights) <= max_wgt_sum.
00386 // Only scale if deemed necessary.
00387 //
00388 // Note that we use ceil() instead of round() to avoid
00389 // rounding to zero weights.
00390 // Based on Zoltan's scale_round_weights, mode 1.
00391 
00392 template <typename Adapter>
00393 void AlgPTScotch<Adapter>::scale_weights(
00394   size_t n,
00395   StridedData<typename Adapter::lno_t, typename Adapter::scalar_t> &fwgts,
00396   SCOTCH_Num *iwgts
00397 )
00398 {
00399   const double INT_EPSILON = 1e-5;
00400 
00401   SCOTCH_Num nonint, nonint_local = 0;
00402   double sum_wgt, sum_wgt_local = 0.;
00403   double max_wgt, max_wgt_local = 0.;
00404 
00405   // Compute local sums of the weights 
00406   // Check whether all weights are integers
00407   for (size_t i = 0; i < n; i++) {
00408     double fw = double(fwgts[i]);
00409     if (!nonint_local){
00410       SCOTCH_Num tmp = (SCOTCH_Num) floor(fw + .5); /* Nearest int */
00411       if (fabs((double)tmp-fw) > INT_EPSILON) {
00412         nonint_local = 1;
00413       }
00414     }
00415     sum_wgt_local += fw;
00416     if (fw > max_wgt_local) max_wgt_local = fw;
00417   }
00418 
00419   Teuchos::reduceAll<int,SCOTCH_Num>(*problemComm, Teuchos::REDUCE_MAX, 1,
00420                               &nonint_local,  &nonint);
00421   Teuchos::reduceAll<int,double>(*problemComm, Teuchos::REDUCE_SUM, 1,
00422                                  &sum_wgt_local, &sum_wgt);
00423   Teuchos::reduceAll<int,double>(*problemComm, Teuchos::REDUCE_MAX, 1,
00424                                  &max_wgt_local, &max_wgt);
00425 
00426   double scale = 1.;
00427   const double max_wgt_sum = double(SCOTCH_NUMMAX/8);
00428 
00429   // Scaling needed if weights are not integers or weights' 
00430   // range is not sufficient
00431   if (nonint || (max_wgt <= INT_EPSILON) || (sum_wgt > max_wgt_sum)) {
00432     /* Calculate scale factor */
00433     if (sum_wgt != 0.) scale = max_wgt_sum/sum_wgt;
00434   }
00435 
00436   /* Convert weights to positive integers using the computed scale factor */
00437   for (size_t i = 0; i < n; i++)
00438     iwgts[i] = (SCOTCH_Num) ceil(double(fwgts[i])*scale);
00439 
00440 }
00441 
00442 } // namespace Zoltan2
00443 
00444 #endif // HAVE_ZOLTAN2_SCOTCH
00445 
00447 
00448 
00449 #endif