Zoltan2
Zoltan2_AlgRCB.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 
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