Zoltan2
Zoltan2_AlgParMETIS.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_ALGPARMETIS_HPP_
00046 #define _ZOLTAN2_ALGPARMETIS_HPP_
00047 
00048 #include <Zoltan2_GraphModel.hpp>
00049 #include <Zoltan2_Algorithm.hpp>
00050 #include <Zoltan2_PartitioningSolution.hpp>
00051 #include <Zoltan2_Util.hpp>
00052 
00057 
00058 #ifndef HAVE_ZOLTAN2_PARMETIS
00059 
00060 // Error handling for when ParMETIS is requested
00061 // but Zoltan2 not built with ParMETIS.
00062 
00063 namespace Zoltan2 {
00064 template <typename Adapter>
00065 class AlgParMETIS : public Algorithm<Adapter>
00066 {
00067 public:
00068   AlgParMETIS(const RCP<const Environment> &env,
00069               const RCP<const Comm<int> > &problemComm,
00070               const RCP<GraphModel<typename Adapter::base_adapter_t> > &model
00071   )
00072   {
00073     throw std::runtime_error(
00074           "BUILD ERROR:  ParMETIS requested but not compiled into Zoltan2.\n"
00075           "Please set CMake flag Zoltan2_ENABLE_ParMETIS:BOOL=ON.");
00076   }
00077 };
00078 }
00079 
00080 #endif
00081 
00084 
00085 #ifdef HAVE_ZOLTAN2_PARMETIS
00086 
00087 #ifndef HAVE_ZOLTAN2_MPI
00088 
00089 // ParMETIS requires compilation with MPI.  
00090 // If MPI is not available, make compilation fail.
00091 #error "TPL ParMETIS requires compilation with MPI.  Configure with -DTPL_ENABLE_MPI:BOOL=ON or -DZoltan2_ENABLE_ParMETIS:BOOL=OFF"
00092 
00093 #else
00094 
00095 extern "C" {
00096 #include "parmetis.h"
00097 }
00098 
00099 #if (PARMETIS_MAJOR_VERSION < 4)
00100 
00101 // Zoltan2 requires ParMETIS v4.x.  
00102 // Make compilation fail for earlier versions of ParMETIS.
00103 #error "Specified version of ParMETIS is not compatible with Zoltan2; upgrade to ParMETIS v4 or later, or build Zoltan2 without ParMETIS."
00104 
00105 #else
00106 
00107 // MPI and ParMETIS version requirements are met.  Proceed.
00108 
00109 namespace Zoltan2 {
00110 
00111 template <typename Adapter>
00112 class AlgParMETIS : public Algorithm<Adapter>
00113 {
00114 public:
00115 
00116   typedef GraphModel<typename Adapter::base_adapter_t> graphModel_t;
00117   typedef typename Adapter::lno_t lno_t;
00118   typedef typename Adapter::gno_t gno_t;
00119   typedef typename Adapter::scalar_t scalar_t;
00120   typedef typename Adapter::part_t part_t;
00121 
00122   typedef idx_t  pm_idx_t;
00123   typedef real_t pm_real_t;
00124 
00135   AlgParMETIS(const RCP<const Environment> &env__,
00136               const RCP<const Comm<int> > &problemComm__,
00137               const RCP<graphModel_t> &model__) :
00138     env(env__), problemComm(problemComm__), 
00139     mpicomm(TeuchosConst2MPI(problemComm__)),
00140     model(model__)
00141   { }
00142 
00143   void partition(const RCP<PartitioningSolution<Adapter> > &solution);
00144 
00145 private:
00146 
00147   const RCP<const Environment> env;
00148   const RCP<const Comm<int> > problemComm;
00149   MPI_Comm mpicomm;
00150   const RCP<GraphModel<typename Adapter::base_adapter_t> > model;
00151 
00152   void scale_weights(size_t n, ArrayView<StridedData<lno_t, scalar_t> > &fwgts,
00153                      pm_idx_t *iwgts);
00154 };
00155 
00156 
00158 template <typename Adapter>
00159 void AlgParMETIS<Adapter>::partition(
00160   const RCP<PartitioningSolution<Adapter> > &solution
00161 )
00162 {
00163   HELLO;
00164 
00165   size_t numGlobalParts = solution->getTargetGlobalNumberOfParts();
00166 
00167   int np = problemComm->getSize();
00168 
00169   // Get vertex info
00170   ArrayView<const gno_t> vtxgnos;
00171   ArrayView<StridedData<lno_t, scalar_t> > xyz;
00172   ArrayView<StridedData<lno_t, scalar_t> > vwgts;
00173   int nVwgt = model->getNumWeightsPerVertex();
00174   size_t nVtx = model->getVertexList(vtxgnos, xyz, vwgts);
00175   pm_idx_t pm_nVtx;
00176   TPL_Traits<pm_idx_t,size_t>::ASSIGN_TPL_T(pm_nVtx, nVtx, env);
00177 
00178   pm_idx_t *pm_vwgts = NULL;
00179   if (nVwgt) {
00180     pm_vwgts = new pm_idx_t[nVtx*nVwgt];
00181     scale_weights(nVtx, vwgts, pm_vwgts);
00182   }
00183 
00184   // Get edge info
00185   ArrayView<const gno_t> adjgnos;
00186   ArrayView<const int>   procs;
00187   ArrayView<const lno_t> offsets;
00188   ArrayView<StridedData<lno_t, scalar_t> > ewgts;
00189   int nEwgt = model->getNumWeightsPerEdge();
00190   size_t nEdge = model->getEdgeList(adjgnos, procs, offsets, ewgts);
00191 
00192   pm_idx_t *pm_ewgts = NULL;
00193   if (nEwgt) {
00194     pm_ewgts = new pm_idx_t[nEdge*nEwgt]; 
00195     scale_weights(nEdge, ewgts, pm_ewgts);
00196   }
00197 
00198   // Convert index types for edges, if needed
00199   pm_idx_t *pm_offsets;  
00200   TPL_Traits<pm_idx_t,lno_t>::ASSIGN_TPL_T_ARRAY(&pm_offsets, offsets, env);
00201   pm_idx_t *pm_adjs;  
00202   TPL_Traits<pm_idx_t,gno_t>::ASSIGN_TPL_T_ARRAY(&pm_adjs, adjgnos, env);
00203 
00204   // Build vtxdist
00205   pm_idx_t *pm_vtxdist = new pm_idx_t[np+1];
00206   pm_vtxdist[0] = 0;
00207   Teuchos::gatherAll(*problemComm, 1, &pm_nVtx, np, &(pm_vtxdist[1]));
00208   for (int i = 2; i <= np; i++)
00209     pm_vtxdist[i] += pm_vtxdist[i-1];
00210 
00211   // Create array for ParMETIS to return results in.
00212   // Note:  ParMETIS does not like NULL arrays,
00213   //        so add 1 to always have non-null.
00214   //        See Zoltan bug 4299.
00215   pm_idx_t *pm_partList = new pm_idx_t[nVtx+1];
00216 
00217   // Get target part sizes and imbalance tolerances
00218 
00219   pm_idx_t pm_nCon = (nVwgt == 0 ? 1 : pm_idx_t(nVwgt));
00220   pm_real_t *pm_partsizes = new pm_real_t[numGlobalParts*pm_nCon];
00221   for (pm_idx_t dim = 0; dim < pm_nCon; dim++) {
00222     if (!solution->criteriaHasUniformPartSizes(dim))
00223       for (size_t i=0; i<numGlobalParts; i++)
00224         pm_partsizes[i*pm_nCon+dim] = 
00225                      pm_real_t(solution->getCriteriaPartSize(dim,i));
00226     else
00227       for (size_t i=0; i<numGlobalParts; i++)
00228         pm_partsizes[i*pm_nCon+dim] = pm_real_t(1.) / pm_real_t(numGlobalParts);
00229   }
00230   pm_real_t *pm_imbTols = new pm_real_t[pm_nCon];
00231   for (pm_idx_t dim = 0; dim < pm_nCon; dim++)
00232     pm_imbTols[dim] = 1.05;  // TODO:  GET THE PARAMETER
00233 
00234   std::string parmetis_method("PARTKWAY");
00235   pm_idx_t pm_wgtflag = 2*(nVwgt > 0) + (nEwgt > 0);
00236   pm_idx_t pm_numflag = 0;
00237 
00238   pm_idx_t pm_nPart;
00239   TPL_Traits<pm_idx_t,size_t>::ASSIGN_TPL_T(pm_nPart, numGlobalParts, env);
00240 
00241   if (parmetis_method == "PARTKWAY") {
00242 
00243     pm_idx_t pm_edgecut = -1;
00244     pm_idx_t pm_options[3];
00245     pm_options[0] = 0;   // Use default options
00246     pm_options[1] = 0;   // Debug level (ignored if pm_options[0] == 0)
00247     pm_options[2] = 0;   // Seed (ignored if pm_options[0] == 0)
00248 
00249     ParMETIS_V3_PartKway(pm_vtxdist, pm_offsets, pm_adjs, pm_vwgts, pm_ewgts,
00250                          &pm_wgtflag, &pm_numflag, &pm_nCon, &pm_nPart,
00251                          pm_partsizes, pm_imbTols, pm_options,
00252                          &pm_edgecut, pm_partList, &mpicomm);
00253   }
00254   else if (parmetis_method == "ADAPTIVE_REPART") {
00255     // Get object sizes
00256     std::cout << "NOT READY FOR ADAPTIVE_REPART YET" << std::endl;
00257     exit(-1);
00258   }
00259   else if (parmetis_method == "PART_GEOM") {
00260     // Get coordinate info, too.
00261     std::cout << "NOT READY FOR PART_GEOM YET" << std::endl;
00262     exit(-1);
00263   }
00264 
00265   // Clean up 
00266   delete [] pm_vtxdist;
00267   delete [] pm_partsizes;
00268   delete [] pm_imbTols;
00269 
00270   // Load answer into the solution.
00271 
00272   ArrayRCP<part_t> partList;
00273   if (TPL_Traits<pm_idx_t, part_t>::OK_TO_CAST_TPL_T()) {
00274     partList = ArrayRCP<part_t>((part_t *)pm_partList, 0, nVtx, true);
00275   }
00276   else {
00277     // TODO Probably should have a TPL_Traits function to do the following
00278     partList = ArrayRCP<part_t>(new part_t[nVtx], 0, nVtx, true);
00279     for (size_t i = 0; i < nVtx; i++) {
00280       partList[i] = part_t(pm_partList[i]);
00281     }
00282     delete [] pm_partList;
00283   }
00284 
00285   ArrayRCP<const gno_t> gnos = arcpFromArrayView(vtxgnos);
00286 
00287   solution->setParts(gnos, partList, true);
00288 
00289   env->memory("Zoltan2-ParMETIS: After creating solution");
00290 
00291   // Clean up copies made due to differing data sizes.
00292   TPL_Traits<pm_idx_t,lno_t>::DELETE_TPL_T_ARRAY(&pm_offsets);
00293   TPL_Traits<pm_idx_t,gno_t>::DELETE_TPL_T_ARRAY(&pm_adjs);
00294 
00295   if (nVwgt) delete [] pm_vwgts;
00296   if (nEwgt) delete [] pm_ewgts;
00297 }
00298 
00300 // Scale and round scalar_t weights (typically float or double) to 
00301 // ParMETIS' idx_t (typically int or long).
00302 // subject to sum(weights) <= max_wgt_sum.
00303 // Scale only if deemed necessary.
00304 //
00305 // Note that we use ceil() instead of round() to avoid
00306 // rounding to zero weights.
00307 // Based on Zoltan's scale_round_weights, mode 1
00308 
00309 template <typename Adapter>
00310 void AlgParMETIS<Adapter>::scale_weights(
00311   size_t n, 
00312   ArrayView<StridedData<typename Adapter::lno_t,
00313                         typename Adapter::scalar_t> > &fwgts,
00314   pm_idx_t *iwgts
00315 )
00316 {
00317   const double INT_EPSILON = 1e-5;
00318   const int nWgt = fwgts.size();
00319 
00320   int *nonint_local = new int[nWgt+nWgt]; 
00321   int *nonint = nonint_local + nWgt;
00322 
00323   double *sum_wgt_local = new double[nWgt*4];
00324   double *max_wgt_local = sum_wgt_local + nWgt;
00325   double *sum_wgt = max_wgt_local + nWgt;
00326   double *max_wgt = sum_wgt + nWgt;
00327 
00328   for (int i = 0; i < nWgt; i++) {
00329     nonint_local[i] = 0;
00330     sum_wgt_local[i] = 0.;
00331     max_wgt_local[i] = 0; 
00332   }
00333 
00334   // Compute local sums of the weights 
00335   // Check whether all weights are integers
00336   for (int j = 0; j < nWgt; j++) {
00337     for (size_t i = 0; i < n; i++) {
00338       double fw = double(fwgts[j][i]);
00339       if (!nonint_local[j]) {
00340         pm_idx_t tmp = (pm_idx_t) floor(fw + .5); /* Nearest int */
00341         if (fabs((double)tmp-fw) > INT_EPSILON) {
00342           nonint_local[j] = 1;
00343         }
00344       }
00345       sum_wgt_local[j] += fw;
00346       if (fw > max_wgt_local[j]) max_wgt_local[j] = fw;
00347     }
00348   }
00349 
00350   Teuchos::reduceAll<int,int>(*problemComm, Teuchos::REDUCE_MAX, nWgt,
00351                               nonint_local,  nonint);
00352   Teuchos::reduceAll<int,double>(*problemComm, Teuchos::REDUCE_SUM, nWgt,
00353                                  sum_wgt_local, sum_wgt);
00354   Teuchos::reduceAll<int,double>(*problemComm, Teuchos::REDUCE_MAX, nWgt,
00355                                  max_wgt_local, max_wgt);
00356 
00357   const double max_wgt_sum = double(std::numeric_limits<pm_idx_t>::max()/8);
00358   for (int j = 0; j < nWgt; j++) {
00359     double scale = 1.;
00360 
00361     // Scaling needed if weights are not integers or weights' 
00362     // range is not sufficient
00363     if (nonint[j] || (max_wgt[j]<=INT_EPSILON) || (sum_wgt[j]>max_wgt_sum)) {
00364       /* Calculate scale factor */
00365       if (sum_wgt[j] != 0.) scale = max_wgt_sum/sum_wgt[j];
00366     }
00367 
00368     /* Convert weights to positive integers using the computed scale factor */
00369     for (size_t i = 0; i < n; i++)
00370       iwgts[i*nWgt+j] = (pm_idx_t) ceil(double(fwgts[j][i])*scale);
00371   }
00372   delete [] nonint_local;
00373   delete [] sum_wgt_local;
00374 }
00375 
00376 } // namespace Zoltan2
00377 
00378 #endif // PARMETIS VERSION 4 OR HIGHER CHECK
00379 
00380 #endif // HAVE_ZOLTAN2_MPI
00381 
00382 #endif // HAVE_ZOLTAN2_PARMETIS
00383 
00384 #endif