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