|
Zoltan2
|
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
1.7.6.1