eig_spec.cc

Go to the documentation of this file.
00001 // $Id: eig_spec.cc,v 3.1 2007/02/22 21:11:48 bjoo Exp $
00002 /*! \file
00003  *  \brief Compute low lying eigenvalues of the hermitian H
00004  */
00005 
00006 
00007 #include "chromabase.h"
00008 
00009 #include <sstream>
00010 
00011 #include "meas/eig/eig_spec.h"
00012 #include "meas/eig/ritz.h"
00013 #include "meas/eig/sn_jacob.h"
00014 
00015 namespace Chroma {
00016 
00017 //! Compute low lying eigenvalues of the hermitian H 
00018 /*!
00019  * \ingroup eig
00020  *
00021  *  Compute low lying eigenvalues of the hermitian H 
00022  *  using the Ritz functional minimization routine,
00023  *  if desired in the Kalkreuter-Simma algorithm
00024  *  H           The operator                    (Read)
00025  *  lambda_H    Eigenvalues                     (Modify)
00026  *  psi         Eigenvectors                    (Modify)
00027  *  N_eig       No of eigenvalues               (Read)
00028  *  N_min       Minimal CG iterations           (Read)
00029  *  N_max       Maximal CG iterations           (Read)
00030  *  Kalk_Sim    Use Kalkreuter-Simma criterion  (Read)
00031  *  n_renorm    Renormalize every n_renorm iter.        (Read)
00032  *  N_KS_max    Max number of Kalkreuter-Simma iterations       (Read)
00033  *  RsdR_r      (relative) residue              (Read)
00034  *  Cv_fact     "Convergence factor" required   (Read)
00035  *  NCG_tot     Total number of CG iter         (Write)
00036  *  n_KS        total number of Kalkreuter-Simma iterations     (Write)
00037  *  n_valid     number of valid eigenvalues     (Write)
00038  *  ProjApsiP   flag for projecting A.psi       (Read)
00039  */
00040 
00041 
00042 void EigSpecRitzCG(const LinearOperator<LatticeFermion>& M, // Herm pos def operator
00043                    multi1d<Real>& lambda_H,          // E-values
00044                    multi1d<LatticeFermion>& psi,     // E-vectors
00045                    int n_eig,                        // No of e-values to find
00046                    int n_renorm,                     // renorm frequency
00047                    int n_min,                        // minimum iters / e_value
00048                    int MaxCG,                        // Max no of CG iters
00049                    const Real& Rsd_r,                // relative residuum of each 
00050                                                      // e-value
00051                    const Real& Rsd_a,                // absolute residuum
00052                    const Real& zero_cutoff,          // if ev slips below this
00053                                                      // we consider it zero
00054                    const bool ProjApsiP,             // Project in Ritz?
00055                    
00056                    int& n_cg_tot,                    // Total no of CG iters
00057                    XMLWriter& xml_out          // Diagnostics
00058              )
00059 {
00060   START_CODE();
00061   
00062   const Subset& sub = M.subset(); // Subset over which M acts
00063   
00064   push(xml_out, "EigSpecRitzCG");
00065 
00066 
00067   n_cg_tot = 0;
00068   int n_count = 0;
00069   int n_max = MaxCG+1;
00070 
00071   int n, i;
00072   Real final_grad;
00073 
00074   multi1d<Real> resid_rel(n_eig);
00075   for(n = 1, i = 0; n <= n_eig;  n++, i++) {
00076 
00077     // Initialise lambda[i] = 1
00078     lambda_H[i] = Real(1);
00079     
00080     // Do the Ritz
00081     Ritz(M, lambda_H[i], psi, n, Rsd_r, Rsd_a, zero_cutoff, n_renorm, n_min, 
00082          n_max, MaxCG, ProjApsiP, n_count, final_grad, false, Real(1), 
00083          Real(1));
00084 
00085     // Add n_count
00086     n_cg_tot += n_count;
00087     
00088     // Check e-value
00089     ostringstream s;
00090     s << "eval" << n;
00091     push(xml_out, s.str());
00092     write(xml_out, "n_count", n_count);
00093     pop(xml_out);
00094 
00095     if( toBool( fabs(lambda_H[i]) < zero_cutoff ) ) 
00096     { 
00097       QDPIO::cout << "Evalue["<< n << "] = " << lambda_H[i] << " is considered zero" << endl;
00098     }
00099     else 
00100     {
00101       LatticeFermion   D_e;
00102       LatticeFermion  lambda_e;
00103       M(D_e, psi[i], PLUS);
00104       lambda_e[sub] = lambda_H[i]*psi[i];
00105       D_e[sub] -= lambda_e;
00106       Double r_norm = sqrt(norm2(D_e,sub));
00107       resid_rel[i] = Real(r_norm)/lambda_H[i];
00108       QDPIO::cout << "Evalue["<<n<<"]: eigen_norm = " << r_norm << " resid_rel = " << resid_rel[i] << endl << endl;
00109     }
00110   }
00111 
00112   // All EV-s done. Dump-em
00113   push(xml_out, "eValues");
00114   write(xml_out, "lambda", lambda_H);
00115   pop(xml_out);
00116   write(xml_out, "n_cg_tot", n_cg_tot);
00117 
00118   pop(xml_out); // EigSpecRitz
00119 
00120       
00121   END_CODE();
00122 }
00123 
00124 
00125 void EigSpecRitzKS(const LinearOperator<LatticeFermion>& M, // Herm pos def operator
00126                    multi1d<Real>& lambda_H,          // E-values
00127                    multi1d<LatticeFermion>& psi,     // E-vectors
00128                    int n_eig,                       // no of eig wanted
00129                    int n_dummy,                     // No of Dummy e-vals to use
00130 
00131                    int n_renorm,                    // renorm frequency
00132                    int n_min,                       // minimum iters / e_value      
00133                    int n_max,                       // max iters / e_value
00134                    int n_max_KS,                    // max KS cycles
00135                    const Real& gamma_factor,        // the KS gamma factor
00136 
00137                    int MaxCG,                       // Max no of CG iters
00138                    const Real& Rsd_r,               // relative residuum of each 
00139                                                   // e-value
00140                    const Real& Rsd_a,               // Absolute residuum
00141                    const Real& zero_cutoff,         // if e-value slips below this, we 
00142                                                     // consider it zero
00143                    const bool ProjApsiP,            // Project in Ritz?
00144                    
00145                    int& n_cg_tot,                   // Total no of CG iters
00146                    int& n_KS,                       // Total no of KS cycles
00147                    int& n_jacob_tot,
00148                    XMLWriter& xml_out         // Diagnostics
00149               )
00150 {
00151   START_CODE();
00152 
00153   const Subset& s = M.subset(); // Subset over which M acts
00154   
00155   // Sanity Checks: 
00156   // Make sure lambda_H is large enough
00157   if( lambda_H.size() < (n_eig+n_dummy) ) { 
00158     QDP_error_exit("lambda_H is too small to hold n_eig + n_dummy values\n");
00159   }
00160 
00161   // Make sure psi is large enough
00162   if( psi.size() < (n_eig + n_dummy) ) {
00163     QDP_error_exit("psi is too small to hold n_eig + n_dummy values\n");
00164   }
00165 
00166   if( n_eig <=0 ) { 
00167     QDP_error_exit("n_eig must be > 0. it is %d\n", n_eig);
00168   }
00169 
00170 
00171   if( n_eig ==1 ) { 
00172     // if n_eig is one, KS algorithm is not applicable. We revert to the 
00173     // Normal CG method
00174     EigSpecRitzCG(M, lambda_H, psi, n_eig, n_renorm, n_min, MaxCG, Rsd_r, 
00175                   Rsd_a, zero_cutoff, ProjApsiP, n_cg_tot, xml_out);
00176     return;
00177   }
00178 
00179   // Internal lambda's
00180   // lambda_H holds initial guesses. Copy these over.
00181   int n_working_eig = n_eig+n_dummy;
00182   multi1d<Real> lambda_intern(n_working_eig);
00183   multi1d<Real> lambda_previous(n_working_eig);
00184   multi1d<Real> delta_cycle(n_working_eig);
00185   int i;
00186   for(i = 0; i < n_working_eig; i++) { 
00187     lambda_intern[i] = lambda_H[i];
00188     lambda_previous[i] = Real(1);
00189     delta_cycle[i] = Real(1);
00190   }
00191 
00192   // Off diag elements of hermitian matrix to diagonalise
00193   multi1d<Complex> off_diag((n_working_eig)*(n_working_eig-1)/2);
00194 
00195   
00196   multi1d<Real> final_grad(n_working_eig);
00197 
00198   int n_count = 0;
00199   n_cg_tot = 0;
00200   n_KS = 0;
00201   n_jacob_tot = 0;
00202 
00203   int ev, j, ij;
00204   int n_jacob;
00205 
00206   push(xml_out, "EigSpecRitzKS");
00207   
00208   bool convP = false;
00209 
00210   for( int KS_iter = 0; KS_iter < n_max_KS; KS_iter++) {
00211     n_KS++;
00212 
00213     push(xml_out, "KS_iter");
00214 
00215     // KS Step 1: for each k=0, n-1 in succession compute
00216     // approximations to the eigenvectors of M by performing
00217     // only a certain number of CG iterations (governed by gamma_factor)
00218     for(ev = 1, i=0; ev <= n_working_eig; ev++, i++) {
00219 
00220       // Do the Ritz for all working eigenvalues
00221       Ritz(M, lambda_intern[i], psi, ev, Rsd_r, Rsd_a, zero_cutoff,  n_renorm, 
00222            n_min, n_max, MaxCG, ProjApsiP, n_count, final_grad[i], true, 
00223            Real(1), gamma_factor);
00224 
00225       // Count the CG cycles
00226       n_cg_tot += n_count;
00227       push(xml_out, "ev");
00228       write(xml_out, "n_count", n_count);
00229       write(xml_out, "final_grad", final_grad[i]);
00230       pop(xml_out);
00231 
00232     }
00233     
00234     // Construct the hermitian matrix M to diagonalise.
00235     // Note only the off diagonal elements are needed
00236     // the lambda_intern[i] are the diagonal elements
00237 
00238     // M_ij = (psi_j, M psi_i )
00239     //
00240     LatticeFermion tmp;
00241     for(i=0, ij=0; i < n_working_eig; i++) { 
00242       M(tmp, psi[i], PLUS);
00243       for(j=0; j < i; j++) { 
00244         off_diag[ij] = innerProduct(psi[j], tmp);
00245         ij++;
00246       }
00247     }
00248 
00249 
00250 
00251     // Now diagonalise it, rotate evecs, and sort
00252     // 
00253     // Jacobi at the moment works with the absolute error
00254     n_jacob = SN_Jacob(psi, n_working_eig, lambda_intern, off_diag, Rsd_a, 50, s);
00255     n_jacob_tot += n_jacob;
00256 
00257     write(xml_out, "n_jacob", n_jacob);
00258 
00259     
00260     // Now we should check convergence
00261     // Pessimistic but safe criterion || g ||/lambda  < omega
00262     // but can also be converged if we consider something to have a zero ev
00263     // we only check convergence of the wanted n_eig
00264     bool convP = true;
00265     for(i=0; i < n_eig; i++) {
00266       bool zeroReachedP = toBool( fabs(lambda_intern[i]) < zero_cutoff );
00267 
00268       // This ev is converged if either 
00269       //   i)  Pessimistic absolute error is reached 
00270       //  ii)  Pessimistic relative error is reached 
00271       // iii)  if the ev is below the zero cutoff
00272       // 
00273       convP &= ( toBool( final_grad[i] < Rsd_a ) 
00274                  || toBool( final_grad[i] < Rsd_r*fabs(lambda_intern[i]) ) 
00275                  || zeroReachedP );
00276     }
00277 
00278     pop(xml_out); // KS_iter
00279 
00280     // All the wanted e-values converged
00281     if( convP ) {
00282 
00283       // Do final Jacobi without the dummies.
00284       // Make the matrix
00285       for(i=0, ij=0; i < n_eig; i++) { 
00286         M(tmp, psi[i], PLUS);
00287         for(j=0; j < i; j++) { 
00288           off_diag[ij] = innerProduct(psi[j], tmp);
00289           ij++;
00290         }
00291       }
00292 
00293       // Diagonalise, rotate, sort
00294       n_jacob = SN_Jacob(psi, n_eig, lambda_intern, off_diag, Rsd_a, 50, s);
00295       write(xml_out, "final_n_jacob", n_jacob);
00296       write(xml_out, "n_cg_tot", n_cg_tot);
00297       write(xml_out, "n_KS", n_KS);
00298 
00299       
00300       // Copy lambda_intern back into lambda_H and return
00301       for(i=0; i < n_eig; i++) { 
00302         lambda_H[i] = lambda_intern[i];
00303       }
00304 
00305       write(xml_out, "lambda_H", lambda_H);
00306       pop(xml_out); // EigSpecRitzKS
00307       END_CODE();
00308       return;
00309     }
00310 
00311   }
00312 
00313   write(xml_out, "n_cg_tot", n_cg_tot);
00314   write(xml_out, "n_KS", n_KS);
00315   pop(xml_out); //EigSpecRitzKS
00316   
00317   // If we reached here then we have done more than n_max KS
00318   QDP_error_exit("n_max_KS reached with no convergence");
00319   END_CODE();
00320 }
00321 
00322 
00323 void fixMMev2Mev(const LinearOperator<LatticeFermion>& M,  // The Op to fix to
00324                  multi1d<Real>& lambda,       // The Evals of M^{dag}M on input
00325                                              // The Evals of M on output 
00326                  multi1d<LatticeFermion>& ev_psi,  // The Evecs 
00327                  const int n_eig,             // The no of evals/evecs 
00328                  const Real& Rsd_r,           // Relative error for validity
00329                  const Real& Rsd_a,           // Absolute error for validity
00330                  const Real& zero_cutoff,     // Zero cutoff
00331                  multi1d<bool>& valid_eig,    // Validity mask (Write)
00332                  int& n_valid,                // No of valids  (Write)
00333                  int& n_jacob                 // How many Jacobis were done
00334                  )
00335 {
00336   START_CODE();
00337 
00338   const Subset& s = M.subset(); // Subset over which M acts
00339   
00340    // Sanity checking
00341   if( n_eig > lambda.size() ) { 
00342     QDP_error_exit("n_eig greater than size of lambda array\n");
00343   }
00344 
00345   if( lambda.size() != ev_psi.size() ) { 
00346     QDP_error_exit("lambda and ev_psi arrays must have same size\n");
00347   }
00348 
00349   // Make the valid eig the right size
00350   valid_eig.resize(n_eig);
00351 
00352   // Temporaries 
00353   LatticeFermion tmp;
00354   Double lambda_H_sq;
00355   Double delta_lambda;
00356   bool zeroMatchedP;
00357   bool convP;
00358   // We are all set -- lets do it
00359 
00360   if( n_eig == 1 ) {                  // only one eigenvalue
00361     M(tmp, ev_psi[0], PLUS);
00362     Double lambda_fix_single = innerProductReal(ev_psi[0], tmp);
00363     
00364     // No diagonalisation needed -- only 1 eval
00365 
00366     // We square lambda_fix_single.
00367     lambda_H_sq = lambda_fix_single*lambda_fix_single;
00368 
00369 
00370     // Different validity criteria, depending on whether lambda_fix_single^2 
00371     // is less than our zero cutoff
00372     if( toBool( lambda_H_sq < zero_cutoff ) ) {
00373 
00374             
00375       // Yes, the square of our estimate for lambda is less than the cutoff
00376       // Check whether the original lambda was also less than the cutoff
00377       // if so, our ev is considered a valid zero e-value
00378       if( toBool( lambda[0] < zero_cutoff ) ) {
00379         valid_eig[0] = true;
00380         n_valid = 1;
00381       }
00382       else { 
00383         // No, the square of our estimate was not zero. Mismatch
00384         // BTW what else can we do here?
00385         valid_eig[0] = false;
00386         n_valid = 1;
00387       }
00388     }
00389     else { 
00390       // Check relative error or absolute error is satisfied
00391       delta_lambda = fabs( lambda_H_sq - Double( lambda[0] ) );
00392       
00393       convP = toBool( delta_lambda < Double(Rsd_a) )
00394         || toBool( delta_lambda < Double(Rsd_r)*fabs(Double(lambda[0])) );
00395       
00396       if ( convP ) { 
00397         // Condition is satisfied
00398         valid_eig[0] = true;
00399         n_valid = 1;
00400       } else {
00401         // Condition is not satisfied
00402         valid_eig[0] = false;
00403         n_valid = 0;
00404       }
00405     }
00406  
00407     // Whatever happened lambda_fix_single is our estimate for
00408     // lambda[0]
00409     lambda[0] = Real(lambda_fix_single);
00410     return;
00411   }  // end if (n_eig == 1)
00412 
00413     
00414   // Interesting case multiple ev-s
00415   // Construct new ev-s and off diagonal matrix
00416   multi1d<Complex> off_diag(n_eig*(n_eig-1)/2 );
00417   multi1d<Real>    lambda_fix(n_eig);
00418   for(int i = 0; i < n_eig; i++) { 
00419     valid_eig[i] = false;
00420   }
00421 
00422   for(int i=0, ij=0; i < n_eig; i++) { 
00423     M(tmp, ev_psi[i], PLUS); 
00424     lambda_fix[i] = innerProductReal(ev_psi[i], tmp);
00425     
00426     for(int j=0; j < i; j++) { 
00427       off_diag[ij] = innerProduct(ev_psi[j], tmp);
00428       ij++;
00429     }
00430   }
00431     
00432   // Diagonalise and sort according to size
00433   n_jacob = SN_Jacob(ev_psi, n_eig, lambda_fix, off_diag, Rsd_a, 50, s);
00434 
00435   // Now the tricky business of matching up with ev-s of H^2
00436   n_valid = 0;
00437     
00438   for(int i=0; i < n_eig; i++) { 
00439     
00440     lambda_H_sq = Double(lambda_fix[i])*Double(lambda_fix[i]);
00441 
00442 
00443     if( toBool(lambda_H_sq < zero_cutoff ) ) {
00444 
00445       // Now compare with all the invalid ev-s
00446       for(int j = n_valid; ( j < n_eig ) && ( valid_eig[i] == false ); j++ ) {
00447 
00448         // If we find an original zero lambda, we match it with this 
00449         // and consider this matched
00450         if( toBool( lambda[j] < zero_cutoff ) ) { 
00451           
00452           // lambda_fix[i] matches lambda[j] 
00453           valid_eig[i] = true;
00454           
00455           // swap lambda[j] with lambda[valid_eig];
00456           // so we don't search it again
00457           Real tmp = lambda[j];
00458           lambda[j] = lambda[n_valid];
00459           lambda[n_valid] = tmp;
00460           
00461           // Increase the validity count
00462           n_valid++;
00463         }
00464 
00465         // Else compare with next j
00466       }
00467       // At this point either valid_eig[i] is true, or we were unable to match
00468       // in which case valid_eig[i] is false from initialisation
00469       // in any case time for next_eig
00470 
00471     }
00472     else {
00473       // Now compare with all the invalid ev-s
00474       for(int j = n_valid; ( j < n_eig ) && ( valid_eig[i] == false ); j++ ) {
00475         delta_lambda = fabs( lambda_H_sq - Double(lambda[j]) );
00476         
00477         convP = toBool( delta_lambda < Double(Rsd_a) ) 
00478           || toBool( delta_lambda < Double(Rsd_r) * fabs( Double(lambda[j]) ) );
00479         if( convP ) {
00480           
00481           // lambda_fix[i] matches lambda[j] 
00482           valid_eig[i] = true;
00483           
00484           // swap lambda[j] with lambda[valid_eig];
00485           // so we don't search it again
00486           Real tmp = lambda[j];
00487           lambda[j] = lambda[n_valid];
00488           lambda[n_valid] = tmp;
00489 
00490           // Increase the validity count
00491           n_valid++;
00492         }
00493 
00494         // Else compare with next j
00495       }
00496       // At this point either valid_eig[i] is true, or we were unable to match
00497       // in which case valid_eig[i] is false from initialisation
00498       // in any case time for next_eig
00499     }
00500   } // We are done
00501 
00502   // Now we can overwrite lambda with lambda_fix
00503   // A desirable feature would be to move the invalid ev-s to the end
00504   // but I cannot be bothered
00505   for(int i = 0; i < n_eig; i++ ) { 
00506     lambda[i] = lambda_fix[i];
00507   }
00508   
00509   END_CODE();
00510   
00511   // we are done 
00512   return;
00513 }
00514 
00515 }  // end namespace Chroma

Generated on Sat Mar 13 04:29:17 2010 for CHROMA by  doxygen 1.4.7