CheMPS2
ConjugateGradient.cpp
1 /*
2  CheMPS2: a spin-adapted implementation of DMRG for ab initio quantum chemistry
3  Copyright (C) 2013-2016 Sebastian Wouters
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License along
16  with this program; if not, write to the Free Software Foundation, Inc.,
17  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #include <math.h>
21 #include <stdlib.h>
22 #include <iostream>
23 
24 #include "ConjugateGradient.h"
25 
26 using std::cout;
27 using std::endl;
28 
29 CheMPS2::ConjugateGradient::ConjugateGradient( const int veclength_in, const double RTOL_in, const double DIAG_CUTOFF_in, const bool print_in ){
30 
31  veclength = veclength_in;
32  RTOL = RTOL_in;
33  DIAG_CUTOFF = DIAG_CUTOFF_in;
34  print = print_in;
35 
36  state = 'I';
37  num_matvec = 0;
38 
39  XVEC = new double[ veclength ];
40  PRECON = new double[ veclength ];
41  RHS = new double[ veclength ];
42  WORK = new double[ veclength ];
43  RESID = new double[ veclength ];
44  PVEC = new double[ veclength ];
45  OPVEC = new double[ veclength ];
46 
47 }
48 
50 
51  delete [] XVEC;
52  delete [] PRECON;
53  delete [] RHS;
54  delete [] WORK;
55  delete [] RESID;
56  delete [] PVEC;
57  delete [] OPVEC;
58 
59 }
60 
61 int CheMPS2::ConjugateGradient::get_num_matvec() const{ return num_matvec; }
62 
63 char CheMPS2::ConjugateGradient::step( double ** pointers ){
64 
65  /*
66  Possible states:
67  - I : just created the class
68  - G : the guess has been set in XVEC, the diagonal in PRECON, and the right-hand side in RHS
69  - H : the PRECON, RESID, and XVEC have been set to start the iterations of ( PRECON * operator * PRECON ) * XVEC = RESID = PRECON * RHS
70  - J : at start-up OPVEC contains operator * PRECON * XVEC
71  - K : OPVEC, RESID, PVEC have just been set, as well as rnorm and rkT_rk
72  - L : OPVEC contains operator * PRECON * x_k
73  - Y : XVEC contains x = operator^{-1} * rhs, and OPVEC contains operator * XVEC
74  - Z : the converged signal has been given to the user, nothing remains to be done
75 
76  Possible instructions:
77  - A : copy the guess to pointers[0], the diagonal of the operator to pointers[1], and the right-hand side of the problem to pointers[2]
78  - B : perform pointers[1] = operator * pointers[0]
79  - C : pointers[0] contains the solution; pointers[1][0] the residual norm
80  - D : there was an error
81  */
82 
83  if ( state == 'I' ){
84  pointers[0] = XVEC;
85  pointers[1] = PRECON;
86  pointers[2] = RHS;
87  state = 'G';
88  return 'A';
89  }
90 
91  if ( state == 'G' ){
92  stepG2H();
93  state = 'H';
94  }
95 
96  if ( state == 'H' ){
97  apply_precon( XVEC, WORK );
98  pointers[0] = WORK;
99  pointers[1] = OPVEC;
100  state = 'J';
101  num_matvec++;
102  return 'B';
103  }
104 
105  if ( state == 'J' ){
106  stepJ2K();
107  state = 'K';
108  }
109 
110  if ( state == 'L' ){
111  stepL2K();
112  state = 'K';
113  }
114 
115  if ( state == 'K' ){
116  if ( rnorm >= RTOL ){
117  apply_precon( PVEC, WORK ); // WORK = PRECON * PVEC
118  pointers[0] = WORK;
119  pointers[1] = OPVEC;
120  state = 'L';
121  } else {
122  apply_precon( XVEC );
123  pointers[0] = XVEC;
124  pointers[1] = OPVEC;
125  state = 'Y';
126  }
127  num_matvec++;
128  return 'B';
129  }
130 
131  if ( state == 'Y' ){
132  stepY2Z();
133  pointers[0] = XVEC;
134  pointers[1] = WORK;
135  pointers[1][0] = rnorm;
136  state = 'Z';
137  return 'C';
138  }
139 
140  return 'D';
141 
142 }
143 
144 void CheMPS2::ConjugateGradient::stepL2K(){
145 
146  apply_precon( OPVEC ); // OPVEC_old = ( PRECON * operator * PRECON ) * PVEC_old
147  const double alpha = rdotr / inprod( PVEC, OPVEC ); // alpha = RESID_old^T * RESID_old / ( PVEC_old^T * ( PRECON * operator * PRECON ) * PVEC_old )
148  for ( int elem = 0; elem < veclength; elem++ ){
149  XVEC[ elem ] = XVEC[ elem ] + alpha * PVEC[ elem ]; // XVEC_new <-- XVEC_old + alpha * PVEC_old
150  }
151  for ( int elem = 0; elem < veclength; elem++ ){
152  RESID[ elem ] = RESID[ elem ] - alpha * OPVEC[ elem ]; // RESID_new <-- RESID_old - alpha * ( PRECON * operator * PRECON ) * PVEC_old
153  }
154  const double new_rdotr = inprod( RESID );
155  const double beta = new_rdotr / rdotr; // beta = RESID_new^T * RESID_new / ( RESID_old^T * RESID_old )
156  for ( int elem = 0; elem < veclength; elem++ ){
157  PVEC[ elem ] = RESID[ elem ] + beta * PVEC[ elem ]; // PVEC_new = RESID_new + beta * PVEC_old
158  }
159  rdotr = new_rdotr;
160  rnorm = sqrt( rdotr );
161  if ( print ){ cout << "ConjugateGradient : After " << num_matvec << " matrix-vector products, the residual of p*O*p * x = p*RHS is " << rnorm << endl; }
162 
163 }
164 
165 void CheMPS2::ConjugateGradient::stepY2Z(){
166 
167  rnorm = 0.0;
168  for ( int elem = 0; elem < veclength; elem++ ){
169  const double diff = OPVEC[ elem ] - RHS[ elem ];
170  rnorm += diff * diff;
171  }
172  rnorm = sqrt( rnorm );
173  if ( print ){ cout << "ConjugateGradient : At convergence the residual of O * x = RHS is " << rnorm << endl; }
174 
175 }
176 
177 void CheMPS2::ConjugateGradient::stepJ2K(){
178 
179  apply_precon( OPVEC ); // OPVEC = ( PRECON * operator * PRECON ) * XVEC
180  for ( int elem = 0; elem < veclength; elem++ ){
181  RESID[ elem ] = RESID[ elem ] - OPVEC[ elem ]; // RESID = ( precon * RHS ) - ( precon * operator * precon ) * XVEC
182  }
183  for ( int elem = 0; elem < veclength; elem++ ){
184  PVEC[ elem ] = RESID[ elem ]; // PVEC = RESID
185  }
186  rdotr = inprod( RESID );
187  rnorm = sqrt( rdotr );
188 
189 }
190 
191 void CheMPS2::ConjugateGradient::stepG2H(){
192 
193  // PRECON = 1 / sqrt( diag ( operator ) )
194  for ( int elem = 0; elem < veclength; elem++ ){
195  if ( PRECON[ elem ] < DIAG_CUTOFF ){ PRECON[ elem ] = DIAG_CUTOFF; }
196  PRECON[ elem ] = 1.0 / sqrt( PRECON[ elem ] );
197  }
198 
199  // RESID = PRECON * RHS
200  apply_precon( RHS, RESID );
201 
202  // XVEC = guess / PRECON
203  for ( int elem = 0; elem < veclength; elem++ ){
204  XVEC[ elem ] = XVEC[ elem ] / PRECON[ elem ];
205  }
206 
207 }
208 
209 double CheMPS2::ConjugateGradient::inprod( double * vector ){
210 
211  double inproduct = 0.0;
212  for ( int elem = 0; elem < veclength; elem++ ){
213  inproduct += vector[ elem ] * vector[ elem ];
214  }
215  return inproduct;
216 
217 }
218 
219 double CheMPS2::ConjugateGradient::inprod( double * vector, double * othervector ){
220 
221  double inproduct = 0.0;
222  for ( int elem = 0; elem < veclength; elem++ ){
223  inproduct += vector[ elem ] * othervector[ elem ];
224  }
225  return inproduct;
226 
227 }
228 
229 void CheMPS2::ConjugateGradient::apply_precon( double * vector ){
230 
231  for ( int elem = 0; elem < veclength; elem++ ){
232  vector[ elem ] = PRECON[ elem ] * vector[ elem ];
233  }
234 
235 }
236 
237 void CheMPS2::ConjugateGradient::apply_precon( double * vector, double * result ){
238 
239  for ( int elem = 0; elem < veclength; elem++ ){
240  result[ elem ] = PRECON[ elem ] * vector[ elem ];
241  }
242 
243 }
244 
char step(double **pointers)
The iterator to converge the ground state vector.
int get_num_matvec() const
Get the number of matrix vector multiplications which have been performed.
virtual ~ConjugateGradient()
Destructor.
ConjugateGradient(const int veclength_in, const double RTOL_in, const double DIAG_CUTOFF_in, const bool print_in)
Constructor.