AnasaziBlockDavidsonSolMgr.hpp

Go to the documentation of this file.
00001 // @HEADER
00002 // ***********************************************************************
00003 //
00004 //                 Anasazi: Block Eigensolvers Package
00005 //                 Copyright (2004) Sandia Corporation
00006 //
00007 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
00008 // license for use of this work by or on behalf of the U.S. Government.
00009 //
00010 // This library is free software; you can redistribute it and/or modify
00011 // it under the terms of the GNU Lesser General Public License as
00012 // published by the Free Software Foundation; either version 2.1 of the
00013 // License, or (at your option) any later version.
00014 //
00015 // This library is distributed in the hope that it will be useful, but
00016 // WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 // Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public
00021 // License along with this library; if not, write to the Free Software
00022 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00023 // USA
00024 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
00025 //
00026 // ***********************************************************************
00027 // @HEADER
00028 
00029 #ifndef ANASAZI_BLOCKDAVIDSON_SOLMGR_HPP
00030 #define ANASAZI_BLOCKDAVIDSON_SOLMGR_HPP
00031 
00036 #include "AnasaziConfigDefs.hpp"
00037 #include "AnasaziTypes.hpp"
00038 
00039 #include "AnasaziEigenproblem.hpp"
00040 #include "AnasaziSolverManager.hpp"
00041 #include "AnasaziSolverUtils.hpp"
00042 
00043 #include "AnasaziBlockDavidson.hpp"
00044 #include "AnasaziBasicSort.hpp"
00045 #include "AnasaziSVQBOrthoManager.hpp"
00046 #include "AnasaziStatusTestMaxIters.hpp"
00047 #include "AnasaziStatusTestResNorm.hpp"
00048 #include "AnasaziStatusTestOrderedResNorm.hpp"
00049 #include "AnasaziStatusTestCombo.hpp"
00050 #include "AnasaziStatusTestOutput.hpp"
00051 #include "AnasaziBasicOutputManager.hpp"
00052 #include "Teuchos_BLAS.hpp"
00053 #include "Teuchos_LAPACK.hpp"
00054 
00055 
00076 namespace Anasazi {
00077 
00078 template<class ScalarType, class MV, class OP>
00079 class BlockDavidsonSolMgr : public SolverManager<ScalarType,MV,OP> {
00080 
00081   private:
00082     typedef MultiVecTraits<ScalarType,MV> MVT;
00083     typedef OperatorTraits<ScalarType,MV,OP> OPT;
00084     typedef Teuchos::ScalarTraits<ScalarType> SCT;
00085     typedef typename Teuchos::ScalarTraits<ScalarType>::magnitudeType MagnitudeType;
00086     typedef Teuchos::ScalarTraits<MagnitudeType> MT;
00087     
00088   public:
00089 
00091 
00092 
00110   BlockDavidsonSolMgr( const Teuchos::RCP<Eigenproblem<ScalarType,MV,OP> > &problem,
00111                              Teuchos::ParameterList &pl );
00112 
00114   virtual ~BlockDavidsonSolMgr() {};
00116   
00118 
00119 
00120   const Eigenproblem<ScalarType,MV,OP>& getProblem() const {
00121     return *problem_;
00122   }
00123 
00125 
00127 
00128     
00150   ReturnType solve();
00152 
00153   private:
00154   Teuchos::RCP<Eigenproblem<ScalarType,MV,OP> > problem_;
00155 
00156   std::string whch_; 
00157 
00158   MagnitudeType convtol_, locktol_;
00159   int maxRestarts_;
00160   bool useLocking_;
00161   bool relconvtol_, rellocktol_;
00162   int blockSize_, numBlocks_;
00163   int maxLocked_;
00164   int verbosity_;
00165   int lockQuorum_;
00166   bool inSituRestart_;
00167   int numRestartBlocks_;
00168 };
00169 
00170 
00171 // Constructor
00172 template<class ScalarType, class MV, class OP>
00173 BlockDavidsonSolMgr<ScalarType,MV,OP>::BlockDavidsonSolMgr( 
00174         const Teuchos::RCP<Eigenproblem<ScalarType,MV,OP> > &problem,
00175         Teuchos::ParameterList &pl ) : 
00176   problem_(problem),
00177   whch_("SR"),
00178   convtol_(0),
00179   locktol_(0),
00180   maxRestarts_(20),
00181   useLocking_(false),
00182   relconvtol_(true),
00183   rellocktol_(true),
00184   blockSize_(0),
00185   numBlocks_(0),
00186   maxLocked_(0),
00187   verbosity_(Anasazi::Errors),
00188   lockQuorum_(1),
00189   inSituRestart_(false),
00190   numRestartBlocks_(1)
00191 {
00192   TEST_FOR_EXCEPTION(problem_ == Teuchos::null,               std::invalid_argument, "Problem not given to solver manager.");
00193   TEST_FOR_EXCEPTION(!problem_->isProblemSet(),               std::invalid_argument, "Problem not set.");
00194   TEST_FOR_EXCEPTION(!problem_->isHermitian(),                std::invalid_argument, "Problem not symmetric.");
00195   TEST_FOR_EXCEPTION(problem_->getInitVec() == Teuchos::null, std::invalid_argument, "Problem does not contain initial vectors to clone from.");
00196 
00197   // which values to solve for
00198   whch_ = pl.get("Which",whch_);
00199   TEST_FOR_EXCEPTION(whch_ != "SM" && whch_ != "LM" && whch_ != "SR" && whch_ != "LR",std::invalid_argument, "Invalid sorting string.");
00200 
00201   // convergence tolerance
00202   convtol_ = pl.get("Convergence Tolerance",MT::prec());
00203   relconvtol_ = pl.get("Relative Convergence Tolerance",relconvtol_);
00204   
00205   // locking tolerance
00206   useLocking_ = pl.get("Use Locking",useLocking_);
00207   rellocktol_ = pl.get("Relative Locking Tolerance",rellocktol_);
00208   locktol_ = pl.get("Locking Tolerance",convtol_/10.0);
00209 
00210   // maximum number of restarts
00211   maxRestarts_ = pl.get("Maximum Restarts",maxRestarts_);
00212 
00213   // block size: default is nev()
00214   blockSize_ = pl.get("Block Size",problem_->getNEV());
00215   TEST_FOR_EXCEPTION(blockSize_ <= 0, std::invalid_argument,
00216                      "Anasazi::BlockDavidsonSolMgr: \"Block Size\" must be strictly positive.");
00217   numBlocks_ = pl.get("Num Blocks",2);
00218   TEST_FOR_EXCEPTION(numBlocks_ <= 1, std::invalid_argument,
00219                      "Anasazi::BlockDavidsonSolMgr: \"Num Blocks\" must be >= 1.");
00220 
00221   // max locked: default is nev(), must satisfy maxLocked_ + blockSize_ >= nev
00222   if (useLocking_) {
00223     maxLocked_ = pl.get("Max Locked",problem_->getNEV());
00224   }
00225   else {
00226     maxLocked_ = 0;
00227   }
00228   if (maxLocked_ == 0) {
00229     useLocking_ = false;
00230   }
00231   TEST_FOR_EXCEPTION(maxLocked_ < 0, std::invalid_argument,
00232                      "Anasazi::BlockDavidsonSolMgr: \"Max Locked\" must be positive.");
00233   TEST_FOR_EXCEPTION(maxLocked_ + blockSize_ < problem_->getNEV(), 
00234                      std::invalid_argument,
00235                      "Anasazi::BlockDavidsonSolMgr: Not enough storage space for requested number of eigenpairs.");
00236   TEST_FOR_EXCEPTION(numBlocks_*blockSize_ + maxLocked_ > MVT::GetVecLength(*problem_->getInitVec()),
00237                      std::invalid_argument,
00238                      "Anasazi::BlockDavidsonSolMgr: Potentially impossible orthogonality requests. Reduce basis size or locking size.");
00239 
00240   if (useLocking_) {
00241     lockQuorum_ = pl.get("Locking Quorum",lockQuorum_);
00242     TEST_FOR_EXCEPTION(lockQuorum_ <= 0,
00243                        std::invalid_argument,
00244                        "Anasazi::BlockDavidsonSolMgr: \"Locking Quorum\" must be strictly positive.");
00245   }
00246 
00247   // verbosity level
00248   if (pl.isParameter("Verbosity")) {
00249     if (Teuchos::isParameterType<int>(pl,"Verbosity")) {
00250       verbosity_ = pl.get("Verbosity", verbosity_);
00251     } else {
00252       verbosity_ = (int)Teuchos::getParameter<Anasazi::MsgType>(pl,"Verbosity");
00253     }
00254   }
00255 
00256   // restart size
00257   numRestartBlocks_ = pl.get("Num Restart Blocks",numRestartBlocks_);
00258   TEST_FOR_EXCEPTION(numRestartBlocks_ <= 0, std::invalid_argument,
00259                      "Anasazi::BlockDavidsonSolMgr: \"Num Restart Blocks\" must be strictly positive.");
00260   TEST_FOR_EXCEPTION(numRestartBlocks_ >= numBlocks_, std::invalid_argument,
00261                      "Anasazi::BlockDavidsonSolMgr: \"Num Restart Blocks\" must be strictly less than \"Num Blocks\".");
00262 
00263   // restarting technique: V*Q or applyHouse(V,H,tau)
00264   if (pl.isParameter("In Situ Restarting")) {
00265     if (Teuchos::isParameterType<bool>(pl,"In Situ Restarting")) {
00266       inSituRestart_ = pl.get("In Situ Restarting",inSituRestart_);
00267     } else {
00268       inSituRestart_ = (bool)Teuchos::getParameter<int>(pl,"In Situ Restarting");
00269     }
00270   }
00271 }
00272 
00273 
00274 // solve()
00275 template<class ScalarType, class MV, class OP>
00276 ReturnType 
00277 BlockDavidsonSolMgr<ScalarType,MV,OP>::solve() {
00278 
00279   typedef SolverUtils<ScalarType,MV,OP> msutils;
00280 
00281   const int nev = problem_->getNEV();
00282 
00284   // Sort manager
00285   Teuchos::RCP<BasicSort<ScalarType,MV,OP> > sorter = Teuchos::rcp( new BasicSort<ScalarType,MV,OP>(whch_) );
00286 
00288   // Output manager
00289   Teuchos::RCP<BasicOutputManager<ScalarType> > printer = Teuchos::rcp( new BasicOutputManager<ScalarType>(verbosity_) );
00290 
00292   // Status tests
00293   //
00294   // convergence
00295   Teuchos::RCP<StatusTestOrderedResNorm<ScalarType,MV,OP> > convtest 
00296       = Teuchos::rcp( new StatusTestOrderedResNorm<ScalarType,MV,OP>(sorter,convtol_,nev,StatusTestOrderedResNorm<ScalarType,MV,OP>::RES_ORTH,relconvtol_) );
00297   // locking
00298   Teuchos::RCP<StatusTestResNorm<ScalarType,MV,OP> > locktest;
00299   if (useLocking_) {
00300     locktest = Teuchos::rcp( new StatusTestResNorm<ScalarType,MV,OP>(locktol_,lockQuorum_,StatusTestResNorm<ScalarType,MV,OP>::RES_ORTH,rellocktol_) );
00301   }
00302   // combo class
00303   Teuchos::Array<Teuchos::RCP<StatusTest<ScalarType,MV,OP> > > alltests;
00304   // for an OR test, the order doesn't matter
00305   alltests.push_back(convtest);
00306   if (locktest != Teuchos::null)   alltests.push_back(locktest);
00307   // combo: convergence || locking 
00308   Teuchos::RCP<StatusTestCombo<ScalarType,MV,OP> > combotest
00309     = Teuchos::rcp( new StatusTestCombo<ScalarType,MV,OP>( StatusTestCombo<ScalarType,MV,OP>::OR, alltests) );
00310   // printing StatusTest
00311   Teuchos::RCP<StatusTestOutput<ScalarType,MV,OP> > outputtest
00312     = Teuchos::rcp( new StatusTestOutput<ScalarType,MV,OP>( printer,combotest,1,Passed ) );
00313 
00315   // Orthomanager
00316   Teuchos::RCP<SVQBOrthoManager<ScalarType,MV,OP> > ortho 
00317     = Teuchos::rcp( new SVQBOrthoManager<ScalarType,MV,OP>(problem_->getM()) );
00318 
00320   // Parameter list
00321   Teuchos::ParameterList plist;
00322   plist.set("Block Size",blockSize_);
00323   plist.set("Num Blocks",numBlocks_);
00324 
00326   // BlockDavidson solver
00327   Teuchos::RCP<BlockDavidson<ScalarType,MV,OP> > bd_solver 
00328     = Teuchos::rcp( new BlockDavidson<ScalarType,MV,OP>(problem_,sorter,printer,outputtest,ortho,plist) );
00329   // set any auxiliary vectors defined in the problem
00330   Teuchos::RCP< const MV > probauxvecs = problem_->getAuxVecs();
00331   if (probauxvecs != Teuchos::null) {
00332     bd_solver->setAuxVecs( Teuchos::tuple< Teuchos::RCP<const MV> >(probauxvecs) );
00333   }
00334 
00336   // Storage
00337   // for locked vectors
00338   int curNumLocked = 0;
00339   Teuchos::RCP<MV> lockvecs;
00340   // lockvecs is used to hold the locked eigenvectors, as well as for temporary storage when locking.
00341   // when locking, we will lock some number of vectors numnew, where numnew <= maxlocked - curlocked
00342   // we will produce numnew random vectors, which will go into the space with the new basis.
00343   // we will also need numnew storage for the image of these random vectors under A and M; 
00344   // columns [curlocked+1,curlocked+numnew] will be used for this storage
00345   if (maxLocked_ > 0) {
00346     lockvecs = MVT::Clone(*problem_->getInitVec(),maxLocked_);
00347   }
00348   std::vector<MagnitudeType> lockvals;
00349   //
00350   // Restarting occurs under two scenarios: when the basis is full and after locking.
00351   //
00352   // For the former, a new basis of size blockSize*numRestartBlocks is generated using the current basis
00353   // and the most significant primitive Ritz vectors (projected eigenvectors).
00354   //     [S,L] = eig(KK)
00355   //     S = [Sr St]   // some for "r"estarting, some are "t"runcated
00356   //     newV = V*Sr
00357   //     KK_new = newV'*K*newV = Sr'*V'*K*V*Sr = Sr'*KK*Sr
00358   //  Therefore, the only multivector operation needed is for the generation of newV.
00359   //
00360   //  * If the multiplication is explicit, it requires a workspace of blockSize*numRestartBlocks vectors. 
00361   //    This space must be specifically allocated for that task, as we don't have any space of that size.
00362   //    It (workMV) will be allocated at the beginning of solve()
00363   //  * Optionally, the multiplication can be performed implicitly, via a Householder QR factorization of 
00364   //    Sr. This can be done in situ, using the basis multivector contained in the solver. This requires
00365   //    that we cast away the const on the multivector returned from getState(). Workspace for this approach
00366   //    is a single vector. the solver's internal storage must be preserved (X,MX,KX,R), requiring us to 
00367   //    allocate this vector.
00368   //
00369   // For the latter (restarting after locking), the new basis is the same size as existing basis. If numnew
00370   // vectors are locked, they are deflated from the current basis and replaced with randomly generated 
00371   // vectors.
00372   //     [S,L] = eig(KK)
00373   //     S = [Sl Su]  // partitioned: "l"ocked and "u"nlocked
00374   //     newL = V*Sl = X(locked)
00375   //     defV = V*Su
00376   //     augV = rand(numnew)  // orthogonal to oldL,newL,defV,auxvecs
00377   //     newV = [defV augV]
00378   //     Kknew = newV'*K*newV = [Su'*KK*Su    defV'*K*augV]
00379   //                            [augV'*K*defV augV'*K*augV]
00380   //     locked = [oldL newL]
00381   // Clearly, this operation is more complicated than the previous.
00382   // Here is a list of the significant computations that need to be performed:
00383   // - newL will be put into space in lockvecs, but will be copied from getState().X at the end
00384   // - defV,augV will be stored in workspace the size of the current basis.
00385   //   - If inSituRestart==true, we compute defV in situ in bd_solver::V_ and
00386   //     put augV at the end of bd_solver::V_
00387   //   - If inSituRestart==false, we must have curDim vectors available for 
00388   //     defV and augV; we will allocate a multivector (workMV) at the beginning of solve()
00389   //     for this purpose.
00390   // - M*augV and K*augV are needed; they will be stored in lockvecs. As a result, newL will
00391   //   not be put into lockvecs until the end.
00392   //
00393   // Therefore, we must allocate workMV when ((maxRestarts_ > 0) || (useLocking_ == true)) && inSituRestart == false
00394   // It will be allocated to size (numBlocks-1)*blockSize
00395   //
00396   Teuchos::RCP<MV> workMV;
00397   if (inSituRestart_ == false) {
00398     // we need storage space to restart, either if we may lock or if may restart after a full basis
00399     if (useLocking_==true || maxRestarts_ > 0) {
00400       workMV = MVT::Clone(*problem_->getInitVec(),(numBlocks_-1)*blockSize_);
00401     }
00402     else {
00403       // we will never need to restart.
00404       workMV = Teuchos::null;
00405     }
00406   }
00407   else { // inSituRestart_ == true 
00408     // we will restart in situ, if we need to restart
00409     // three situation remain: 
00410     // - never restart                                       => no space needed
00411     // - only restart for locking (i.e., never restart full) => no space needed
00412     // - restart for full basis                              => need one vector
00413     if (maxRestarts_ > 0) {
00414       workMV = MVT::Clone(*problem_->getInitVec(),1);
00415     }
00416     else {
00417       workMV = Teuchos::null;
00418     }
00419   }
00420 
00421   // some consts and utils
00422   const ScalarType ONE = SCT::one();
00423   const ScalarType ZERO = SCT::zero();
00424   Teuchos::LAPACK<int,ScalarType> lapack;
00425   Teuchos::BLAS<int,ScalarType> blas;
00426 
00427   // go ahead and initialize the solution to nothing in case we throw an exception
00428   Eigensolution<ScalarType,MV> sol;
00429   sol.numVecs = 0;
00430   problem_->setSolution(sol);
00431 
00432   int numRestarts = 0;
00433 
00434   // tell bd_solver to iterate
00435   while (1) {
00436     try {
00437       bd_solver->iterate();
00438 
00440       //
00441       // check convergence first
00442       //
00444       if (convtest->getStatus() == Passed ) {
00445         // we have convergence
00446         // convtest->whichVecs() tells us which vectors from lockvecs and solver state are the ones we want
00447         // convtest->howMany() will tell us how many
00448         break;
00449       }
00451       //
00452       // check for restarting before locking: if we need to lock, it will happen after the restart
00453       //
00455       else if ( bd_solver->getCurSubspaceDim() == bd_solver->getMaxSubspaceDim() ) {
00456 
00457         if ( numRestarts >= maxRestarts_ ) {
00458           break; // break from while(1){bd_solver->iterate()}
00459         }
00460         numRestarts++;
00461 
00462         printer->stream(IterationDetails) << " Performing restart number " << numRestarts << " of " << maxRestarts_ << std::endl << std::endl;
00463 
00464         BlockDavidsonState<ScalarType,MV> state = bd_solver->getState();
00465         int curdim = state.curDim;
00466         int newdim = numRestartBlocks_*blockSize_;
00467 
00468         //
00469         // compute eigenvectors of the projected problem
00470         Teuchos::SerialDenseMatrix<int,ScalarType> S(curdim,curdim);
00471         std::vector<MagnitudeType> theta(curdim);
00472         int rank = curdim;
00473         int info = msutils::directSolver(curdim,*state.KK,Teuchos::null,S,theta,rank,10);
00474         TEST_FOR_EXCEPTION(info != 0     ,std::logic_error,
00475             "Anasazi::BlockDavidsonSolMgr::solve(): error calling SolverUtils::directSolver.");       // this should never happen
00476         TEST_FOR_EXCEPTION(rank != curdim,std::logic_error,
00477             "Anasazi::BlockDavidsonSolMgr::solve(): direct solve did not compute all eigenvectors."); // this should never happen
00478 
00479         // 
00480         // sort the eigenvalues (so that we can order the eigenvectors)
00481         {
00482           std::vector<int> order(curdim);
00483           sorter->sort(bd_solver.get(),curdim,theta,&order);
00484           //
00485           // apply the same ordering to the primitive ritz vectors
00486           msutils::permuteVectors(order,S);
00487         }
00488         //
00489         // select the significant primitive ritz vectors
00490         Teuchos::SerialDenseMatrix<int,ScalarType> Sr(Teuchos::View,S,curdim,newdim);
00491         //
00492         // generate newKK = Sr'*KKold*Sr
00493         Teuchos::SerialDenseMatrix<int,ScalarType> newKK(newdim,newdim);
00494         {
00495           Teuchos::SerialDenseMatrix<int,ScalarType> KKtmp(curdim,newdim), 
00496                                                      KKold(Teuchos::View,*state.KK,curdim,curdim);
00497           int teuchosRet;
00498           // KKtmp = KKold*Sr
00499           teuchosRet = KKtmp.multiply(Teuchos::NO_TRANS,Teuchos::NO_TRANS,ONE,KKold,Sr,ZERO);
00500           TEST_FOR_EXCEPTION(teuchosRet != 0,std::logic_error,
00501                              "Anasazi::BlockDavidsonSolMgr::solve(): Logic error calling SerialDenseMatrix::multiply.");
00502           // newKK = Sr'*KKtmp = Sr'*KKold*Sr
00503           teuchosRet = newKK.multiply(Teuchos::CONJ_TRANS,Teuchos::NO_TRANS,ONE,Sr,KKtmp,ZERO);
00504           TEST_FOR_EXCEPTION(teuchosRet != 0,std::logic_error,
00505                              "Anasazi::BlockDavidsonSolMgr::solve(): Logic error calling SerialDenseMatrix::multiply.");
00506           // make it Hermitian in memory
00507           for (int j=0; j<newdim; ++j) {
00508             for (int i=j+1; i<newdim; ++i) {
00509               newKK(i,j) = SCT::conjugate(newKK(j,i));
00510             }
00511           }
00512         }
00513 
00514         // prepare new state
00515         BlockDavidsonState<ScalarType,MV> rstate;
00516         rstate.curDim = newdim;
00517         rstate.KK = Teuchos::rcp( &newKK, false );
00518         // 
00519         // we know that newX = newV*Sr(:,1:bS) = oldV*S(:1:bS) = oldX
00520         // the restarting preserves the Ritz vectors and residual
00521         // for the Ritz values, we want all of the values associated with newV. 
00522         // these have already been placed at the beginning of theta
00523         rstate.X  = state.X;
00524         rstate.KX = state.KX;
00525         rstate.MX = state.MX;
00526         rstate.R  = state.R;
00527         rstate.T  = Teuchos::rcp( new std::vector<MagnitudeType>(&theta[0],&theta[newdim]) );
00528 
00529         if (inSituRestart_ == true) {
00530           //
00531           // get non-const pointer to solver's basis so we can work in situ
00532           Teuchos::RCP<MV> solverbasis = Teuchos::rcp_const_cast<MV>(state.V);
00533           // 
00534           // perform Householder QR of Sr = Q [D;0], where D is unit diag.
00535           // WARNING: this will overwrite Sr; however, we do not need Sr anymore after this
00536           std::vector<ScalarType> tau(newdim), work(newdim);
00537           int info;
00538           lapack.GEQRF(curdim,newdim,Sr.values(),Sr.stride(),&tau[0],&work[0],work.size(),&info);
00539           TEST_FOR_EXCEPTION(info != 0,std::logic_error,
00540                              "Anasazi::BlockDavidsonSolMgr::solve(): error calling GEQRF during restarting.");
00541           if (printer->isVerbosity(Debug)) {
00542             Teuchos::SerialDenseMatrix<int,ScalarType> R(Teuchos::Copy,Sr,newdim,newdim);
00543             for (int j=0; j<newdim; j++) {
00544               R(j,j) = SCT::magnitude(R(j,j)) - 1.0;
00545               for (int i=j+1; i<newdim; i++) {
00546                 R(i,j) = ZERO;
00547               }
00548             }
00549             printer->stream(Debug) << "||Triangular factor of Sr - I||: " << R.normFrobenius() << std::endl;
00550           }
00551           // 
00552           // perform implicit oldV*Sr
00553           // this actually performs oldV*[Sr Su*M] = [newV truncV], for some unitary M
00554           // we are actually interested in only the first newdim vectors of the result
00555           {
00556             std::vector<int> curind(curdim);
00557             for (int i=0; i<curdim; i++) curind[i] = i;
00558             Teuchos::RCP<MV> oldV = MVT::CloneView(*solverbasis,curind);
00559             msutils::applyHouse(newdim,*oldV,Sr,tau,workMV);
00560           }
00561           // 
00562           // put the new basis into the state for initialize()
00563           // the new basis is contained in the the first newdim columns of solverbasis
00564           // initialize() will recognize that pointer bd_solver.V_ == pointer rstate.V, and will neglect the copy.
00565           rstate.V = solverbasis;
00566         }
00567         else { // inSituRestart == false)
00568           // newV = oldV*Sr, explicitly. workspace is in workMV
00569           std::vector<int> curind(curdim), newind(newdim);
00570           for (int i=0; i<curdim; i++) curind[i] = i;
00571           for (int i=0; i<newdim; i++) newind[i] = i;
00572           Teuchos::RCP<const MV> oldV = MVT::CloneView(*state.V,curind);
00573           Teuchos::RCP<MV>       newV = MVT::CloneView(*workMV ,newind);
00574 
00575           MVT::MvTimesMatAddMv(ONE,*oldV,Sr,ZERO,*newV);
00576           // 
00577           // put the new basis into the state for initialize()
00578           rstate.V = newV;
00579         }
00580 
00581         //
00582         // send the new state to the solver
00583         bd_solver->initialize(rstate);
00584       } // end of restarting
00586       //
00587       // check locking if we didn't converge or restart
00588       //
00590       else if (locktest != Teuchos::null && locktest->getStatus() == Passed) {
00591 
00592         // 
00593         // get current state
00594         BlockDavidsonState<ScalarType,MV> state = bd_solver->getState();
00595         const int curdim = state.curDim;
00596 
00597         //
00598         // get number,indices of vectors to be locked
00599         TEST_FOR_EXCEPTION(locktest->howMany() <= 0,std::logic_error,
00600             "Anasazi::BlockDavidsonSolMgr::solve(): status test mistake.");
00601         TEST_FOR_EXCEPTION(locktest->howMany() != (int)locktest->whichVecs().size(),std::logic_error,
00602             "Anasazi::BlockDavidsonSolMgr::solve(): status test mistake.");
00603         //
00604         // don't lock more than maxLocked_; we didn't allocate enough space.
00605         std::vector<int> tmp_vector_int;
00606         if (curNumLocked + locktest->howMany() > maxLocked_) {
00607           // just use the first of them
00608           tmp_vector_int.insert(tmp_vector_int.begin(),locktest->whichVecs().begin(),locktest->whichVecs().begin()+maxLocked_-curNumLocked);
00609         }
00610         else {
00611           tmp_vector_int = locktest->whichVecs();
00612         }
00613         const std::vector<int> lockind(tmp_vector_int);
00614         const int numNewLocked = lockind.size();
00615         //
00616         // generate indices of vectors left unlocked
00617         // curind = [0,...,curdim-1] = UNION( lockind, unlockind )
00618         const int numUnlocked = curdim-numNewLocked;
00619         tmp_vector_int.resize(curdim);
00620         for (int i=0; i<curdim; i++) tmp_vector_int[i] = i;
00621         const std::vector<int> curind(tmp_vector_int);       // curind = [0 ... curdim-1]
00622         tmp_vector_int.resize(numUnlocked); 
00623         set_difference(curind.begin(),curind.end(),lockind.begin(),lockind.end(),tmp_vector_int.begin());
00624         const std::vector<int> unlockind(tmp_vector_int);    // unlockind = [0 ... curdim-1] - lockind
00625         tmp_vector_int.clear();
00626 
00627         //
00628         // debug printing
00629         if (printer->isVerbosity(Debug)) {
00630           printer->print(Debug,"Locking vectors: ");
00631           for (unsigned int i=0; i<lockind.size(); i++) {printer->stream(Debug) << " " << lockind[i];}
00632           printer->print(Debug,"\n");
00633         }
00634 
00635         //
00636         // we need primitive ritz vectors/values:
00637         // [S,L] = eig(oldKK)
00638         //
00639         // this will be partitioned as follows:
00640         //   locked: Sl = S(lockind)      // we won't actually need Sl
00641         // unlocked: Su = S(unlockind)
00642         //
00643         Teuchos::SerialDenseMatrix<int,ScalarType> S(curdim,curdim);
00644         std::vector<MagnitudeType> theta(curdim);
00645         {
00646           int rank = curdim;
00647           int info = msutils::directSolver(curdim,*state.KK,Teuchos::null,S,theta,rank,10);
00648           TEST_FOR_EXCEPTION(info != 0     ,std::logic_error,
00649               "Anasazi::BlockDavidsonSolMgr::solve(): error calling SolverUtils::directSolver.");       // this should never happen
00650           TEST_FOR_EXCEPTION(rank != curdim,std::logic_error,
00651               "Anasazi::BlockDavidsonSolMgr::solve(): direct solve did not compute all eigenvectors."); // this should never happen
00652           // 
00653           // sort the eigenvalues (so that we can order the eigenvectors)
00654           std::vector<int> order(curdim);
00655           sorter->sort(bd_solver.get(),curdim,theta,&order);
00656           //
00657           // apply the same ordering to the primitive ritz vectors
00658           msutils::permuteVectors(order,S);
00659         }
00660         //
00661         // select the unlocked ritz vectors
00662         // the indexing in unlockind is relative to the ordered primitive ritz vectors
00663         // (this is why we ordered theta,S above)
00664         Teuchos::SerialDenseMatrix<int,ScalarType> Su(curdim,numUnlocked);
00665         for (int i=0; i<numUnlocked; i++) {
00666           blas.COPY(curdim, S[unlockind[i]], 1, Su[i], 1);
00667         }
00668 
00669 
00670         //
00671         // newV has the following form:
00672         // newV = [defV augV]
00673         // - defV will be of size curdim - numNewLocked, and contain the generated basis: defV = oldV*Su
00674         // - augV will be of size numNewLocked, and contain random directions to make up for the lost space
00675         //
00676         // we will need a pointer to defV below to generate the off-diagonal block of newKK
00677         // go ahead and setup pointer to augV
00678         //
00679         Teuchos::RCP<MV> defV, augV;
00680         if (inSituRestart_ == true) {
00681           //
00682           // get non-const pointer to solver's basis so we can work in situ
00683           Teuchos::RCP<MV> solverbasis = Teuchos::rcp_const_cast<MV>(state.V);
00684           // 
00685           // perform Householder QR of Su = Q [D;0], where D is unit diag.
00686           // work on a copy of Su, since we need Su below to build newKK
00687           Teuchos::SerialDenseMatrix<int,ScalarType> copySu(Su);
00688           std::vector<ScalarType> tau(numUnlocked), work(numUnlocked);
00689           int info;
00690           lapack.GEQRF(curdim,numUnlocked,copySu.values(),copySu.stride(),&tau[0],&work[0],work.size(),&info);
00691           TEST_FOR_EXCEPTION(info != 0,std::logic_error,
00692                              "Anasazi::BlockDavidsonSolMgr::solve(): error calling GEQRF during restarting.");
00693           if (printer->isVerbosity(Debug)) {
00694             Teuchos::SerialDenseMatrix<int,ScalarType> R(Teuchos::Copy,copySu,numUnlocked,numUnlocked);
00695             for (int j=0; j<numUnlocked; j++) {
00696               R(j,j) = SCT::magnitude(R(j,j)) - 1.0;
00697               for (int i=j+1; i<numUnlocked; i++) {
00698                 R(i,j) = ZERO;
00699               }
00700             }
00701             printer->stream(Debug) << "||Triangular factor of Su - I||: " << R.normFrobenius() << std::endl;
00702           }
00703           // 
00704           // perform implicit oldV*Su
00705           // this actually performs oldV*[Su Sl*M] = [defV lockV], for some unitary M
00706           // we are actually interested in only the first numUnlocked vectors of the result
00707           {
00708             Teuchos::RCP<MV> oldV = MVT::CloneView(*solverbasis,curind);
00709             msutils::applyHouse(numUnlocked,*oldV,copySu,tau,workMV);
00710           }
00711           std::vector<int> defind(numUnlocked), augind(numNewLocked);
00712           for (int i=0; i<numUnlocked ; i++) defind[i] = i;
00713           for (int i=0; i<numNewLocked; i++) augind[i] = numUnlocked+i;
00714           defV = MVT::CloneView(*solverbasis,defind);
00715           augV = MVT::CloneView(*solverbasis,augind);
00716         }
00717         else { // inSituRestart == false)
00718           // defV = oldV*Su, explicitly. workspace is in workMV
00719           std::vector<int> defind(numUnlocked), augind(numNewLocked);
00720           for (int i=0; i<numUnlocked ; i++) defind[i] = i;
00721           for (int i=0; i<numNewLocked; i++) augind[i] = numUnlocked+i;
00722           Teuchos::RCP<const MV> oldV = MVT::CloneView(*state.V,curind);
00723           defV = MVT::CloneView(*workMV,defind);
00724           augV = MVT::CloneView(*workMV,augind);
00725           
00726           MVT::MvTimesMatAddMv(ONE,*oldV,Su,ZERO,*defV);
00727         }
00728 
00729         //
00730         // lockvecs will be partitioned as follows:
00731         // lockvecs = [curlocked augTmp ...]
00732         // - augTmp will be used for the storage of M*augV and K*augV
00733         //   later, the locked vectors (stored in state.X and referenced via const MV view newLocked) 
00734         //   will be moved into lockvecs on top of augTmp when it is no longer needed as workspace.
00735         // - curlocked will be used in orthogonalization of augV
00736         //
00737         // newL is the new locked vectors; newL = oldV*Sl = RitzVectors(lockind)
00738         // we will not produce them, but instead retrieve them from RitzVectors
00739         //
00740         Teuchos::RCP<const MV> curlocked, newLocked;
00741         Teuchos::RCP<MV> augTmp;
00742         {
00743           // setup curlocked
00744           if (curNumLocked > 0) {
00745             std::vector<int> curlockind(curNumLocked);
00746             for (int i=0; i<curNumLocked; i++) curlockind[i] = i;
00747             curlocked = MVT::CloneView(*lockvecs,curlockind);
00748           }
00749           else {
00750             curlocked = Teuchos::null;
00751           }
00752           // setup augTmp
00753           std::vector<int> augtmpind(numNewLocked); 
00754           for (int i=0; i<numNewLocked; i++) augtmpind[i] = curNumLocked+i;
00755           augTmp = MVT::CloneView(*lockvecs,augtmpind);
00756           // setup newLocked
00757           newLocked = MVT::CloneView(*bd_solver->getRitzVectors(),lockind);
00758         }
00759 
00760         // 
00761         // generate augV and perform orthogonalization
00762         //
00763         MVT::MvRandom(*augV);
00764         // 
00765         // orthogonalize it against auxvecs, defV, and all locked vectors (new and current)
00766         // use augTmp as storage for M*augV, if hasM
00767         {
00768           Teuchos::Array<Teuchos::RCP<const MV> > against;
00769           Teuchos::Array<Teuchos::RCP<Teuchos::SerialDenseMatrix<int,ScalarType> > > dummy;
00770           if (probauxvecs != Teuchos::null) against.push_back(probauxvecs);
00771           if (curlocked != Teuchos::null)   against.push_back(curlocked);
00772           against.push_back(newLocked);
00773           against.push_back(defV);
00774           if (problem_->getM() != Teuchos::null) {
00775             OPT::Apply(*problem_->getM(),*augV,*augTmp);
00776           }
00777           ortho->projectAndNormalizeMat(*augV,augTmp,dummy,Teuchos::null,against);
00778         }
00779 
00780         //
00781         // form newKK
00782         //
00783         // newKK = newV'*K*newV = [Su'*KK*Su    defV'*K*augV]
00784         //                        [augV'*K*defV augV'*K*augV]
00785         //
00786         // first, generate the principal submatrix, the projection of K onto the unlocked portion of oldV
00787         //
00788         Teuchos::SerialDenseMatrix<int,ScalarType> newKK(curdim,curdim);
00789         {
00790           Teuchos::SerialDenseMatrix<int,ScalarType> KKtmp(curdim,numUnlocked), 
00791                                                      KKold(Teuchos::View,*state.KK,curdim,curdim),
00792                                                      KK11(Teuchos::View,newKK,numUnlocked,numUnlocked);
00793           int teuchosRet;
00794           // KKtmp = KKold*Su
00795           teuchosRet = KKtmp.multiply(Teuchos::NO_TRANS,Teuchos::NO_TRANS,ONE,KKold,Su,ZERO);
00796           TEST_FOR_EXCEPTION(teuchosRet != 0,std::logic_error,
00797                              "Anasazi::BlockDavidsonSolMgr::solve(): Logic error calling SerialDenseMatrix::multiply.");
00798           // KK11 = Su'*KKtmp = Su'*KKold*Su
00799           teuchosRet = KK11.multiply(Teuchos::CONJ_TRANS,Teuchos::NO_TRANS,ONE,Su,KKtmp,ZERO);
00800           TEST_FOR_EXCEPTION(teuchosRet != 0,std::logic_error,
00801                              "Anasazi::BlockDavidsonSolMgr::solve(): Logic error calling SerialDenseMatrix::multiply.");
00802         }
00803         //
00804         // project the stiffness matrix on augV
00805         {
00806           OPT::Apply(*problem_->getOperator(),*augV,*augTmp);
00807           Teuchos::SerialDenseMatrix<int,ScalarType> KK12(Teuchos::View,newKK,numUnlocked,numNewLocked,0,numUnlocked),
00808                                                      KK22(Teuchos::View,newKK,numNewLocked,numNewLocked,numUnlocked,numUnlocked);
00809           MVT::MvTransMv(ONE,*defV,*augTmp,KK12);
00810           MVT::MvTransMv(ONE,*augV,*augTmp,KK22);
00811         }
00812         // 
00813         // done with defV,augV
00814         defV = Teuchos::null;
00815         augV = Teuchos::null;
00816         //
00817         // make it hermitian in memory (fill in KK21)
00818         for (int j=0; j<curdim; ++j) {
00819           for (int i=j+1; i<curdim; ++i) {
00820             newKK(i,j) = SCT::conjugate(newKK(j,i));
00821           }
00822         }
00823         //
00824         // we are done using augTmp as storage
00825         // put newLocked into lockvecs, new values into lockvals
00826         augTmp = Teuchos::null;
00827         {
00828           std::vector<Value<ScalarType> > allvals = bd_solver->getRitzValues();
00829           for (int i=0; i<numNewLocked; i++) {
00830             lockvals.push_back(allvals[lockind[i]].realpart);
00831           }
00832 
00833           std::vector<int> indlock(numNewLocked);
00834           for (int i=0; i<numNewLocked; i++) indlock[i] = curNumLocked+i;
00835           MVT::SetBlock(*newLocked,indlock,*lockvecs);
00836           newLocked = Teuchos::null;
00837 
00838           curNumLocked += numNewLocked;
00839           std::vector<int> curlockind(curNumLocked);
00840           for (int i=0; i<curNumLocked; i++) curlockind[i] = i;
00841           curlocked = MVT::CloneView(*lockvecs,curlockind);
00842         }
00843         // add locked vecs as aux vecs, along with aux vecs from problem
00844         // add lockvals to convtest
00845         // disable locktest if curNumLocked == maxLocked
00846         {
00847           convtest->setAuxVals(lockvals);
00848 
00849           Teuchos::Array< Teuchos::RCP<const MV> > aux;
00850           if (probauxvecs != Teuchos::null) aux.push_back(probauxvecs);
00851           aux.push_back(curlocked);
00852           bd_solver->setAuxVecs(aux);
00853 
00854           if (curNumLocked == maxLocked_) {
00855             // disabled locking now by setting quorum to unreachable number
00856             locktest->setQuorum(blockSize_+1);
00857           }
00858         }
00859 
00860         //
00861         // prepare new state
00862         BlockDavidsonState<ScalarType,MV> rstate;
00863         rstate.curDim = curdim;
00864         if (inSituRestart_) {
00865           // data is already in the solver's memory
00866           rstate.V = state.V;
00867         }
00868         else {
00869           // data is in workspace and will be copied to solver memory
00870           rstate.V = workMV;
00871         }
00872         rstate.KK = Teuchos::rcp( &newKK, false );
00873         //
00874         // pass new state to the solver
00875         bd_solver->initialize(rstate);
00876       } // end of locking
00878       //
00879       // we returned from iterate(), but none of our status tests Passed.
00880       // something is wrong, and it is probably our fault.
00881       //
00883       else {
00884         TEST_FOR_EXCEPTION(true,std::logic_error,"Anasazi::BlockDavidsonSolMgr::solve(): Invalid return from bd_solver::iterate().");
00885       }
00886     }
00887     catch (std::exception e) {
00888       printer->stream(Errors) << "Error! Caught exception in BlockDavidson::iterate() at iteration " << bd_solver->getNumIters() << std::endl 
00889                               << e.what() << std::endl;
00890       throw;
00891     }
00892   }
00893 
00894   // clear temp space
00895   workMV = Teuchos::null;
00896 
00897   sol.numVecs = convtest->howMany();
00898   if (sol.numVecs > 0) {
00899     sol.Evecs = MVT::Clone(*problem_->getInitVec(),sol.numVecs);
00900     sol.Espace = sol.Evecs;
00901     sol.Evals.resize(sol.numVecs);
00902     std::vector<MagnitudeType> vals(sol.numVecs);
00903 
00904     // copy them into the solution
00905     std::vector<int> which = convtest->whichVecs();
00906     // indices between [0,blockSize) refer to vectors/values in the solver
00907     // indices between [blockSize,blocksize+curNumLocked) refer to locked vectors/values
00908     // everything has already been ordered by the solver; we just have to partition the two references
00909     std::vector<int> inlocked(0), insolver(0);
00910     for (unsigned int i=0; i<which.size(); i++) {
00911       if (which[i] < blockSize_) {
00912         insolver.push_back(which[i]);
00913       }
00914       else {
00915         // sanity check
00916         TEST_FOR_EXCEPTION(which[i] >= curNumLocked+blockSize_,std::logic_error,"Anasazi::BlockDavidsonSolMgr::solve(): indexing mistake.");
00917         inlocked.push_back(which[i] - blockSize_);
00918       }
00919     }
00920 
00921     TEST_FOR_EXCEPTION(insolver.size() + inlocked.size() != (unsigned int)sol.numVecs,std::logic_error,"Anasazi::BlockDavidsonSolMgr::solve(): indexing mistake.");
00922 
00923     // set the vecs,vals in the solution
00924     if (insolver.size() > 0) {
00925       // set vecs
00926       int lclnum = insolver.size();
00927       std::vector<int> tosol(lclnum);
00928       for (int i=0; i<lclnum; i++) tosol[i] = i;
00929       Teuchos::RCP<const MV> v = MVT::CloneView(*bd_solver->getRitzVectors(),insolver);
00930       MVT::SetBlock(*v,tosol,*sol.Evecs);
00931       // set vals
00932       std::vector<Value<ScalarType> > fromsolver = bd_solver->getRitzValues();
00933       for (unsigned int i=0; i<insolver.size(); i++) {
00934         vals[i] = fromsolver[insolver[i]].realpart;
00935       }
00936     }
00937 
00938     // get the vecs,vals from locked storage
00939     if (inlocked.size() > 0) {
00940       int solnum = insolver.size();
00941       // set vecs
00942       int lclnum = inlocked.size();
00943       std::vector<int> tosol(lclnum);
00944       for (int i=0; i<lclnum; i++) tosol[i] = solnum + i;
00945       Teuchos::RCP<const MV> v = MVT::CloneView(*lockvecs,inlocked);
00946       MVT::SetBlock(*v,tosol,*sol.Evecs);
00947       // set vals
00948       for (unsigned int i=0; i<inlocked.size(); i++) {
00949         vals[i+solnum] = lockvals[inlocked[i]];
00950       }
00951     }
00952 
00953     // sort the eigenvalues and permute the eigenvectors appropriately
00954     {
00955       std::vector<int> order(sol.numVecs);
00956       sorter->sort(bd_solver.get(), sol.numVecs, vals, &order );
00957       // store the values in the Eigensolution
00958       for (int i=0; i<sol.numVecs; i++) {
00959         sol.Evals[i].realpart = vals[i];
00960         sol.Evals[i].imagpart = MT::zero();
00961       }
00962       // now permute the eigenvectors according to order
00963       msutils::permuteVectors(sol.numVecs,order,*sol.Evecs);
00964     }
00965 
00966     // setup sol.index, remembering that all eigenvalues are real so that index = {0,...,0}
00967     sol.index.resize(sol.numVecs,0);
00968   }
00969 
00970   // print final summary
00971   bd_solver->currentStatus(printer->stream(FinalSummary));
00972 
00973   // print timing information
00974   Teuchos::TimeMonitor::summarize(printer->stream(TimingDetails));
00975 
00976   problem_->setSolution(sol);
00977   printer->stream(Debug) << "Returning " << sol.numVecs << " eigenpairs to eigenproblem." << std::endl;
00978 
00979   if (sol.numVecs < nev) {
00980     return Unconverged; // return from BlockDavidsonSolMgr::solve() 
00981   }
00982   return Converged; // return from BlockDavidsonSolMgr::solve() 
00983 }
00984 
00985 
00986 } // end Anasazi namespace
00987 
00988 #endif /* ANASAZI_BLOCKDAVIDSON_SOLMGR_HPP */

Generated on Tue Oct 20 12:45:18 2009 for Anasazi by doxygen 1.4.7