//////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2008 The Regents of the University of California // // This file is part of Qbox // // Qbox is distributed under the terms of the GNU General Public License // as published by the Free Software Foundation, either version 2 of // the License, or (at your option) any later version. // See the file COPYING in the root directory of this distribution // or . // //////////////////////////////////////////////////////////////////////////////// // // PSDAWavefunctionStepper.C // //////////////////////////////////////////////////////////////////////////////// #include "PSDAWavefunctionStepper.h" #include "Wavefunction.h" #include "SlaterDet.h" #include "Preconditioner.h" #include using namespace std; //////////////////////////////////////////////////////////////////////////////// PSDAWavefunctionStepper::PSDAWavefunctionStepper(Wavefunction& wf, Preconditioner& prec, TimerMap& tmap) : prec_(prec), WavefunctionStepper(wf,tmap), wf_last_(wf), dwf_last_(wf), extrapolate_(false) {} //////////////////////////////////////////////////////////////////////////////// PSDAWavefunctionStepper::~PSDAWavefunctionStepper(void) {} //////////////////////////////////////////////////////////////////////////////// void PSDAWavefunctionStepper::update(Wavefunction& dwf) { tmap_["psda_residual"].start(); for ( int ispin = 0; ispin < wf_.nspin(); ispin++ ) { for ( int ikp = 0; ikp < wf_.nkp(); ikp++ ) { // compute A = V^T H V and descent direction HV - VA if ( wf_.sd(ispin,ikp)->basis().real() ) { // proxy real matrices c, cp DoubleMatrix c_proxy(wf_.sd(ispin,ikp)->c()); DoubleMatrix cp_proxy(dwf.sd(ispin,ikp)->c()); DoubleMatrix a(c_proxy.context(),c_proxy.n(),c_proxy.n(), c_proxy.nb(),c_proxy.nb()); // factor 2.0 in next line: G and -G a.gemm('t','n',2.0,c_proxy,cp_proxy,0.0); // rank-1 update correction a.ger(-1.0,c_proxy,0,cp_proxy,0); // cp = cp - c * a cp_proxy.gemm('n','n',-1.0,c_proxy,a,1.0); } else { // not implemented in the complex case cout << "PSDA is not implemented for complex wave functions" << endl; assert(false); } } } tmap_["psda_residual"].stop(); // dwf.sd->c() now contains the descent direction (HV-VA) (residual) // update the preconditioner prec_.update(wf_); for ( int ispin = 0; ispin < wf_.nspin(); ispin++ ) { for ( int ikp = 0; ikp < wf_.nkp(); ikp++ ) { tmap_["psda_prec"].start(); // Apply preconditioner K and store -K(HV-VA) in dwf double* c = (double*) wf_.sd(ispin,ikp)->c().valptr(); double* c_last = (double*) wf_last_.sd(ispin,ikp)->c().valptr(); double* dc = (double*) dwf.sd(ispin,ikp)->c().valptr(); double* dc_last = (double*) dwf_last_.sd(ispin,ikp)->c().valptr(); const int mloc = wf_.sd(ispin,ikp)->c().mloc(); const int ngwl = wf_.sd(ispin,ikp)->basis().localsize(); const int nloc = wf_.sd(ispin,ikp)->c().nloc(); for ( int n = 0; n < nloc; n++ ) { // note: double mloc length for complex indices double* dcn = &dc[2*mloc*n]; for ( int i = 0; i < ngwl; i++ ) { const double fac = prec_.diag(ispin,ikp,n,i); const double f0 = -fac * dcn[2*i]; const double f1 = -fac * dcn[2*i+1]; dcn[2*i] = f0; dcn[2*i+1] = f1; } } tmap_["psda_prec"].stop(); // dwf now contains the preconditioned descent // direction -K(HV-VA) tmap_["psda_update_wf"].start(); // Anderson extrapolation if ( extrapolate_ ) { double theta = 0.0; double a = 0.0, b = 0.0; for ( int i = 0; i < 2*mloc*nloc; i++ ) { const double f = dc[i]; const double delta_f = f - dc_last[i]; // accumulate partial sums of a and b // a = delta_F * F a += f * delta_f; b += delta_f * delta_f; } // correct for double counting of asum and bsum on first row // factor 2.0: G and -G a *= 2.0; b *= 2.0; if ( wf_.sdcontext()->myrow() == 0 ) { for ( int n = 0; n < nloc; n++ ) { const int i = 2*mloc*n; const double f0 = dc[i]; const double f1 = dc[i+1]; const double delta_f0 = f0 - dc_last[i]; const double delta_f1 = f1 - dc_last[i+1]; a -= f0 * delta_f0 + f1 * delta_f1; b -= delta_f0 * delta_f0 + delta_f1 * delta_f1; } } // a and b contain the partial sums of a and b double tmpvec[2] = { a, b }; wf_.sdcontext()->dsum(2,1,&tmpvec[0],2); a = tmpvec[0]; b = tmpvec[1]; // compute theta = - a / b if ( b != 0.0 ) theta = - a / b; if ( wf_.sdcontext()->onpe0() ) cout << " Anderson extrapolation: theta=" << theta; if ( theta < -1.0 ) { theta = 0.0; } theta = min(2.0,theta); if ( wf_.sdcontext()->onpe0() ) cout <<" (" << theta << ")" << endl; // extrapolation for ( int i = 0; i < 2*mloc*nloc; i++ ) { // x_bar = x_ + theta * ( x_ - xlast_ ) (store in x_) const double x = c[i]; const double xlast = c_last[i]; const double xbar = x + theta * ( x - xlast ); // f_bar = f + theta * ( f - flast ) (store in f) const double f = dc[i]; const double flast = dc_last[i]; const double fbar = f + theta * ( f - flast ); c[i] = xbar + fbar; c_last[i] = x; dc_last[i] = f; } } else { // no extrapolation for ( int i = 0; i < 2*mloc*nloc; i++ ) { // x_ = x_ + f_ const double x = c[i]; const double f = dc[i]; c[i] = x + f; c_last[i] = x; dc_last[i] = f; } } tmap_["psda_update_wf"].stop(); enum ortho_type { GRAM, LOWDIN, ORTHO_ALIGN, RICCATI }; //const ortho_type ortho = GRAM; //const ortho_type ortho = LOWDIN; const ortho_type ortho = ORTHO_ALIGN; switch ( ortho ) { case GRAM: tmap_["gram"].start(); wf_.sd(ispin,ikp)->gram(); tmap_["gram"].stop(); break; case LOWDIN: tmap_["lowdin"].start(); wf_.sd(ispin,ikp)->lowdin(); tmap_["lowdin"].stop(); break; case ORTHO_ALIGN: tmap_["ortho_align"].start(); wf_.sd(ispin,ikp)->ortho_align(*wf_last_.sd(ispin,ikp)); tmap_["ortho_align"].stop(); break; case RICCATI: tmap_["riccati"].start(); wf_.sd(ispin,ikp)->riccati(*wf_last_.sd(ispin,ikp)); tmap_["riccati"].stop(); break; } } // ikp } // ispin extrapolate_ = true; }