|
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 00050 #ifndef _ZOLTAN2_ALGRCB_HPP_ 00051 #define _ZOLTAN2_ALGRCB_HPP_ 00052 00053 #include <Zoltan2_AlgRCB_methods.hpp> 00054 #include <Zoltan2_CoordinateModel.hpp> 00055 #include <Zoltan2_Exceptions.hpp> 00056 00057 #include <Teuchos_ParameterList.hpp> 00058 00059 namespace Zoltan2{ 00060 00082 template <typename Adapter> 00083 class AlgRCB : public Algorithm<Adapter> 00084 { 00085 public: 00086 typedef CoordinateModel<typename Adapter::base_adapter_t> coordModel_t; 00087 typedef typename Adapter::node_t node_t; 00088 typedef typename Adapter::lno_t lno_t; 00089 typedef typename Adapter::gno_t gno_t; 00090 typedef typename Adapter::part_t part_t; 00091 typedef typename Adapter::scalar_t scalar_t; 00092 00093 00094 // TODO Minimal constructor for now; make it smarter later. 00095 AlgRCB(const RCP<const Environment> &env__, 00096 const RCP<Comm<int> > &problemComm__, 00097 const RCP<const coordModel_t> &coords__) : 00098 env(env__), problemComm(problemComm__), coords(coords__) 00099 { 00100 #ifndef INCLUDE_ZOLTAN2_EXPERIMENTAL 00101 Z2_THROW_EXPERIMENTAL("Zoltan2 RCB is strictly experimental software " 00102 "due to performance problems in its use of Tpetra.") 00103 #endif 00104 } 00105 00106 void partition(const RCP<PartitioningSolution<Adapter> > &solution); 00107 00108 private: 00109 const RCP<const Environment> env; 00110 const RCP<Comm<int> > problemComm; 00111 const RCP<const coordModel_t> coords; 00112 // TODO Functions in Zoltan2_AlgRCB_Methods should be here. 00113 }; 00114 00115 template <typename Adapter> 00116 void AlgRCB<Adapter>::partition( 00117 const RCP<PartitioningSolution<Adapter> > &solution 00118 ) 00119 { 00120 #ifndef INCLUDE_ZOLTAN2_EXPERIMENTAL 00121 00122 Z2_THROW_EXPERIMENTAL("Zoltan2 RCB is strictly experimental software " 00123 "due to performance problems in its use of Tpetra.") 00124 00125 #else // INCLUDE_ZOLTAN2_EXPERIMENTAL 00126 00127 // Make a copy of communicator because 00128 // we subdivide the communicator during the algorithm. 00129 00130 RCP<Comm<int> > comm = problemComm->duplicate(); 00131 00132 std::bitset<NUM_RCB_PARAMS> params; 00133 00134 env->debug(DETAILED_STATUS, "Entering AlgPartRCB"); 00135 00136 const Teuchos::ParameterList &pl = env->getParameters(); 00137 00138 env->timerStart(BOTH_TIMERS, "RCB set up"); 00139 00141 // Partitioning problem parameters of interest: 00142 // objective 00143 // imbalance_tolerance 00144 00145 env->debug(DETAILED_STATUS, "Accessing parameters"); 00146 multiCriteriaNorm mcnorm = normBalanceTotalMaximum; 00147 std::string obj; 00148 00149 const Teuchos::ParameterEntry *pe = pl.getEntryPtr("partitioning_objective"); 00150 if (pe) 00151 obj = pe->getValue(&obj); 00152 00153 if (!pe){ 00154 params.set(rcb_balanceWeight); 00155 mcnorm = normBalanceTotalMaximum; 00156 } 00157 else if (obj == std::string("balance_object_count")){ 00158 params.set(rcb_balanceCount); 00159 } 00160 else if (obj == std::string("multicriteria_minimize_total_weight")){ 00161 params.set(rcb_minTotalWeight); 00162 mcnorm = normMinimizeTotalWeight; 00163 } 00164 else if (obj == std::string("multicriteria_minimize_maximum_weight")){ 00165 params.set(rcb_minMaximumWeight); 00166 mcnorm = normMinimizeMaximumWeight; 00167 } 00168 else if (obj == std::string("multicriteria_balance_total_maximum")){ 00169 params.set(rcb_balanceTotalMaximum); 00170 mcnorm = normBalanceTotalMaximum; 00171 } 00172 else{ 00173 params.set(rcb_balanceWeight); 00174 mcnorm = normBalanceTotalMaximum; 00175 } 00176 00177 scalar_t imbalanceTolerance = .1; 00178 pe = pl.getEntryPtr("imbalance_tolerance"); 00179 if (pe){ 00180 double tol; 00181 tol = pe->getValue(&tol); 00182 imbalanceTolerance = tol - 1.0; 00183 } 00184 00185 if (imbalanceTolerance <= 0) 00186 imbalanceTolerance = 10e-4; // TODO - what's a good choice 00187 00189 // Geometric partitioning problem parameters of interest: 00190 // average_cuts 00191 // rectilinear 00192 // bisection_num_test_cuts (experimental) 00193 00194 int val = 0; 00195 pe = pl.getEntryPtr("average_cuts"); 00196 if (pe) 00197 val = pe->getValue(&val); 00198 00199 if (val == 1) 00200 params.set(rcb_averageCuts); 00201 00202 val = 0; 00203 pe = pl.getEntryPtr("rectilinear"); 00204 if (pe) 00205 val = pe->getValue(&val); 00206 00207 if (val == 1) 00208 params.set(rcb_rectilinear); 00209 00210 int numTestCuts = 1; 00211 pe = pl.getEntryPtr("bisection_num_test_cuts"); 00212 if (pe) 00213 numTestCuts = pe->getValue(&numTestCuts); 00214 00216 // From the CoordinateModel we need: 00217 // coordinate values 00218 // coordinate weights, if any 00219 // coordinate global Ids 00220 00221 env->debug(DETAILED_STATUS, "Accessing coordinate model"); 00222 typedef StridedData<lno_t, scalar_t> input_t; 00223 00224 bool ignoreWeights = params.test(rcb_balanceCount); 00225 00226 int coordDim = coords->getCoordinateDim(); 00227 00228 int nWeightsPerCoord = 0; 00229 if (!ignoreWeights) nWeightsPerCoord = coords->getNumWeightsPerCoordinate(); 00230 00231 size_t numLocalCoords = coords->getLocalNumCoordinates(); 00232 global_size_t numGlobalCoords = coords->getGlobalNumCoordinates(); 00233 00234 ArrayView<const gno_t> gnos; 00235 ArrayView<input_t> xyz; 00236 ArrayView<input_t> wgts; 00237 00238 coords->getCoordinates(gnos, xyz, wgts); 00239 00240 Array<ArrayRCP<const scalar_t> > values(coordDim); 00241 for (int dim=0; dim < coordDim; dim++){ 00242 ArrayRCP<const scalar_t> ar; 00243 xyz[dim].getInputArray(ar); 00244 values[dim] = ar; 00245 } 00246 00247 env->debug(DETAILED_STATUS, "Storing weights"); 00248 00249 Array<ArrayRCP<const scalar_t> > weights(nWeightsPerCoord); 00250 for (int widx = 0; widx < nWeightsPerCoord; widx++){ 00251 ArrayRCP<const scalar_t> ar; 00252 wgts[widx].getInputArray(ar); 00253 weights[widx] = ar; 00254 } 00255 00256 if (env->doStatus() && (numGlobalCoords < 500)){ 00257 std::ostringstream oss; 00258 oss << "Problem: "; 00259 for (size_t i=0; i < numLocalCoords; i++){ 00260 oss << gnos[i] << " ("; 00261 for (int dim=0; dim < coordDim; dim++) 00262 oss << (xyz[dim])[i] << " "; 00263 oss << ") "; 00264 } 00265 00266 env->debug(VERBOSE_DETAILED_STATUS, oss.str()); 00267 } 00268 00270 // From the Solution we get part information. 00271 // If the part sizes for a given criteria are not uniform, 00272 // then they are values that sum to 1.0. 00273 env->debug(DETAILED_STATUS, "Getting part info"); 00274 00275 size_t numGlobalParts = solution->getTargetGlobalNumberOfParts(); 00276 00277 int nSizesPerPart = (nWeightsPerCoord ? nWeightsPerCoord : 1); 00278 00279 Array<bool> uniformParts(nSizesPerPart); 00280 Array<ArrayRCP<scalar_t> > partSizes(nSizesPerPart); 00281 00282 for (int widx = 0; widx < nSizesPerPart; widx++){ 00283 if (solution->criteriaHasUniformPartSizes(widx)){ 00284 uniformParts[widx] = true; 00285 } 00286 else{ 00287 scalar_t *tmp = new scalar_t [numGlobalParts]; 00288 env->localMemoryAssertion(__FILE__, __LINE__, numGlobalParts, tmp) ; 00289 00290 for (size_t i=0; i < numGlobalParts; i++){ 00291 tmp[i] = solution->getCriteriaPartSize(widx, i); 00292 } 00293 00294 partSizes[widx] = arcp(tmp, 0, numGlobalParts); 00295 } 00296 } 00297 00298 // It may not be possible to solve the partitioning problem 00299 // if we have multiple weights with part size 00300 // arrays that differ. So let's be aware of this possibility. 00301 00302 bool multiplePartSizeSpecs = false; 00303 00304 if (nSizesPerPart > 1){ 00305 for (int widx1 = 0; widx1 < nSizesPerPart; widx1++) 00306 for (int widx2 = widx1+1; widx2 < nSizesPerPart; widx2++) 00307 if (!solution->criteriaHaveSamePartSizes(widx1, widx2)){ 00308 multiplePartSizeSpecs = true; 00309 break; 00310 } 00311 } 00312 00313 if (multiplePartSizeSpecs) 00314 params.set(rcb_multiplePartSizeSpecs); 00315 00317 // Create the distributed data for the algorithm. 00318 // 00319 // It is a multivector containing one vector for each coordinate 00320 // dimension, plus a vector for each weight. 00321 00322 env->debug(DETAILED_STATUS, "Creating multivec"); 00323 typedef Tpetra::Map<lno_t, gno_t, node_t> map_t; 00324 typedef Tpetra::MultiVector<scalar_t, lno_t, gno_t, node_t> mvector_t; 00325 00326 int multiVectorDim = coordDim + nWeightsPerCoord; 00327 00328 gno_t gnoMin, gnoMax; 00329 coords->getIdentifierMap()->getGnoRange(gnoMin, gnoMax); 00330 00331 RCP<map_t> map; 00332 try{ 00333 map = rcp(new map_t(numGlobalCoords, gnos, gnoMin, comm)); 00334 } 00335 Z2_THROW_OUTSIDE_ERROR(*env) 00336 00337 typedef ArrayView<const scalar_t> coordList_t; 00338 00339 coordList_t *avList = new coordList_t [multiVectorDim]; 00340 00341 for (int dim=0; dim < coordDim; dim++) 00342 avList[dim] = values[dim].view(0, numLocalCoords); 00343 00344 for (int widx=0, idx=coordDim; widx < nWeightsPerCoord; widx++) 00345 avList[idx++] = weights[widx].view(0, numLocalCoords); 00346 00347 ArrayRCP<const ArrayView<const scalar_t> > vectors = 00348 arcp(avList, 0, multiVectorDim); 00349 00350 RCP<mvector_t> mvector; 00351 00352 try{ 00353 mvector = rcp(new mvector_t( 00354 map, vectors.view(0, multiVectorDim), multiVectorDim)); 00355 } 00356 Z2_THROW_OUTSIDE_ERROR(*env) 00357 00358 env->timerStop(BOTH_TIMERS, "RCB set up"); 00359 00362 // The algorithm 00365 00366 env->debug(DETAILED_STATUS, "Beginning algorithm"); 00367 part_t part0 = 0; 00368 part_t part1 = numGlobalParts-1; 00369 int sanityCheck = numGlobalParts; 00370 int groupSize = comm->getSize(); 00371 int rank = comm->getRank(); 00372 00373 long imbalanceReductionFactor(1); 00374 long nparts = numGlobalParts; 00375 while ((nparts >>= 1) != 0) imbalanceReductionFactor++; 00376 00377 imbalanceTolerance /= imbalanceReductionFactor; 00378 00379 int iteration = 1; 00380 00381 env->memory("RCB algorithm set up"); 00382 00383 env->timerStart(MACRO_TIMERS, "Parallel RCB"); 00384 00385 while (part1>part0 && groupSize>1 && numGlobalCoords>0 && sanityCheck--){ 00386 00388 // Which coordinates are left and which are right? 00389 00390 Array<unsigned char> lrflags(numLocalCoords); 00391 scalar_t cutValue=0; // TODO eventually save this for user 00392 int cutDimension=0; 00393 scalar_t imbalance=0, weightLeft=0, weightRight=0; 00394 part_t leftHalfNumParts=0; 00395 00396 env->timerStart(MICRO_TIMERS, "Find cut", iteration, 2); 00397 00398 try{ 00399 determineCut<mvector_t, Adapter>(env, comm, 00400 params, numTestCuts, imbalanceTolerance, 00401 coordDim, nWeightsPerCoord, mvector, mcnorm, solution, part0, part1, 00402 lrflags.view(0, numLocalCoords), 00403 cutDimension, cutValue, imbalance, leftHalfNumParts, 00404 weightLeft, weightRight); 00405 } 00406 Z2_FORWARD_EXCEPTIONS 00407 00408 env->timerStop(MICRO_TIMERS, "Find cut", iteration, 2); 00409 00410 // Do we have empty left or right halves? 00411 00412 bool skipLeft = (weightLeft == 0); 00413 bool skipRight = (weightRight == 0); 00414 00416 // Migrate the multivector of data. 00417 00418 int leftHalfNumProcs=0; 00419 00420 env->timerStart(MICRO_TIMERS, "Migrate", iteration, 2); 00421 00422 try{ // on return mvector has my new data 00423 00424 migrateData<mvector_t>( env, comm, lrflags.view(0,numLocalCoords), 00425 mvector, leftHalfNumProcs); 00426 } 00427 Z2_FORWARD_EXCEPTIONS 00428 00429 env->timerStop(MICRO_TIMERS, "Migrate", iteration, 2); 00430 00431 env->localBugAssertion(__FILE__, __LINE__, "num procs in half", 00432 leftHalfNumProcs > 0 && leftHalfNumProcs < groupSize, 00433 BASIC_ASSERTION); 00434 00435 bool inLeftHalf = (rank < leftHalfNumProcs); 00436 00437 if ((inLeftHalf && skipLeft) || (!inLeftHalf && skipRight)){ 00438 groupSize = 1; 00439 numLocalCoords = 0; 00440 continue; 00441 } 00442 00444 // Divide into two subgroups. 00445 00446 env->timerStart(MICRO_TIMERS, "Create sub group, sub data", iteration, 2); 00447 00448 int *ids = NULL; 00449 00450 if (rank < leftHalfNumProcs){ 00451 groupSize = leftHalfNumProcs; 00452 ids = new int [groupSize]; 00453 env->localMemoryAssertion(__FILE__, __LINE__, groupSize, ids); 00454 for (int i=0; i < groupSize; i++) 00455 ids[i] = i; 00456 part1 = part0 + leftHalfNumParts - 1; 00457 } 00458 else { 00459 groupSize = comm->getSize() - leftHalfNumProcs; 00460 rank -= leftHalfNumProcs; 00461 ids = new int [groupSize]; 00462 env->localMemoryAssertion(__FILE__, __LINE__, groupSize, ids); 00463 for (int i=0; i < groupSize; i++) 00464 ids[i] = i + leftHalfNumProcs; 00465 part0 += leftHalfNumParts; 00466 } 00467 00468 ArrayView<const int> idView(ids, groupSize); 00469 comm = comm->createSubcommunicator(idView); 00470 00471 delete [] ids; 00472 00474 // Create a new multivector for my smaller group. 00475 00476 ArrayView<const gno_t> gnoList = mvector->getMap()->getNodeElementList(); 00477 size_t localSize = mvector->getLocalLength(); 00478 00479 // Tpetra will calculate the globalSize. 00480 size_t globalSize = Teuchos::OrdinalTraits<size_t>::invalid(); 00481 00482 RCP<map_t> subMap; 00483 try{ 00484 subMap= rcp(new map_t(globalSize, gnoList, 0, comm)); 00485 } 00486 Z2_THROW_OUTSIDE_ERROR(*env) 00487 00488 coordList_t *avSubList = new coordList_t [multiVectorDim]; 00489 00490 for (int dim=0; dim < multiVectorDim; dim++) 00491 avSubList[dim] = mvector->getData(dim).view(0, localSize); 00492 00493 ArrayRCP<const ArrayView<const scalar_t> > subVectors = 00494 arcp(avSubList, 0, multiVectorDim); 00495 00496 RCP<mvector_t> subMvector; 00497 00498 try{ 00499 subMvector = rcp(new mvector_t( 00500 subMap, subVectors.view(0, multiVectorDim), multiVectorDim)); 00501 } 00502 Z2_THROW_OUTSIDE_ERROR(*env) 00503 00504 env->timerStop(MICRO_TIMERS, "Create sub group, sub data", iteration, 2); 00505 00506 mvector = subMvector; 00507 00508 numLocalCoords = mvector->getLocalLength(); 00509 numGlobalCoords = mvector->getGlobalLength(); 00510 00511 iteration++; 00512 00513 env->memory("New subgroup data created"); 00514 } 00515 00516 env->timerStop(MACRO_TIMERS, "Parallel RCB"); 00517 00518 env->localBugAssertion(__FILE__, __LINE__, "partitioning failure", 00519 sanityCheck, BASIC_ASSERTION); 00520 00521 ArrayRCP<part_t> partId; 00522 00523 if (numLocalCoords > 0){ 00524 part_t *tmp = new part_t [numLocalCoords]; 00525 env->localMemoryAssertion(__FILE__, __LINE__, numLocalCoords, tmp); 00526 partId = arcp(tmp, 0, numLocalCoords, true); 00527 } 00528 00529 env->memory("Solution array created"); 00530 00531 if ((part1 > part0) && (numLocalCoords > 0)){ // Serial partitioning 00532 00533 // scalar_t cutValue; TODO 00534 // int cutDimension; 00535 // scalar_t imbalance; 00536 00537 env->timerStart(MACRO_TIMERS, "Serial RCB"); 00538 00539 try{ 00540 ArrayView<lno_t> emptyIndex; 00541 00542 serialRCB<mvector_t, Adapter>(env, 1, params, 00543 numTestCuts, imbalanceTolerance, 00544 coordDim, nWeightsPerCoord, mvector, emptyIndex, solution, 00545 part0, part1, partId.view(0,numLocalCoords)); 00546 } 00547 Z2_FORWARD_EXCEPTIONS 00548 00549 env->timerStop(MACRO_TIMERS, "Serial RCB"); 00550 00551 } 00552 else{ 00553 for (lno_t i=0; i < partId.size(); i++) 00554 partId[i] = part0; 00555 } 00556 00558 // Done: update the solution 00559 00560 ArrayRCP<const gno_t> gnoList = 00561 arcpFromArrayView(mvector->getMap()->getNodeElementList()); 00562 00563 if (env->getDebugLevel() >= VERBOSE_DETAILED_STATUS && 00564 (numGlobalCoords < 500)){ 00565 std::ostringstream oss; 00566 oss << "Solution: "; 00567 for (typename ArrayRCP<const gno_t>::size_type i=0; i < gnoList.size(); i++) 00568 oss << gnoList[i] << " (" << partId[i] << ") "; 00569 00570 env->debug(VERBOSE_DETAILED_STATUS, oss.str()); 00571 } 00572 00573 solution->setParts(gnoList, partId, false); 00574 #endif // INCLUDE_ZOLTAN2_EXPERIMENTAL 00575 } 00576 00577 } // namespace Zoltan2 00578 00579 #endif
1.7.6.1