MPQC  3.0.0-alpha
curvy_steps.hpp
1 /*
2  * curvy_steps.hpp
3  *
4  * Created on: Aug 14, 2013
5  * Author: drewlewis
6  */
7 
8 #ifndef CURVY_STEPS_HPP_
9 #define CURVY_STEPS_HPP_
10 
11 #include "common.hpp"
12 #include "tiledarray_fock.hpp"
13 
14 namespace mpqc {
15 namespace tests {
16  using Array2 = TA::Array<double,2>;
17  using Array3 = TA::Array<double,3>;
18  using Array4 = TA::Array<double,4>;
19 
20 namespace curvy_steps {
21 
22  void
23  Purify(Array2 &R, const Array2 &S){
24 
25  // Purify an approximate density matrix so that it meets the requirements
26  // of a density matirx mainly that
27  // R = R^T
28  // Trace(S.R) = Number of electrons * 0.5
29  // All eigenvalues of the matrix are 1.
30  //lets force symmetry via averaging. R_{ij} = (R_{ij} + R_{ji}) / 2
31  R("i,j") = (R("i,j") + R("j,i")) * 0.5;
32 
33  // This routine is essentially R = 3*R^2 - 2 * R^3 it only converges if R is
34  // close enough to the correct answer.
35  double rdiff;
36  do {
37  const Array2 RS = R("i,k") * S("k,j");
38 
40  const Array2 RSR = RS("i,k") * R("k,j");
41  const Array2 Rnew = 3 * RSR("i,j") - 2 * RS("i,m") * RSR("m,j");
42  const Array2 Rnorm_temp = Rnew("i,j") - R("i,j");
43 
44  rdiff = TA::expressions::norminf(Rnorm_temp("i,j"));
45  R = Rnew;
46  } while(rdiff > 1e-8);
47  }
48 
49  Array2
50  Scom(const Array2 &R, const Array2 &X, const Array2 &S){
51 
52  //Compute the S commutator of 2 matrices [R,X]_s = R.S.X - X.S.R
53  Array2 RSX = R("i,k") * S("k,n") * X("n,j");
54 
55  // RSX_{ij} = - RSX_{ji}
56  return RSX("i,j") + RSX("j,i");
57  }
58 
59  void
60  RotateR(Array2 &R, const Array2 &X, const Array2 &S,
61  const size_t order = 4){
62 
63  // This routine approximates the rotation of the density matrix which
64  // looks like R(X) = e^{-XS} R e^{SX} via the BCH approximation
65  // R(X) = R + [R,X]_s + 1/(2!) [[R,X]_s,X]_s + 1/(3!)[[[R,X]_s,X]_s,X]_s . . .
66  Array2 LHS = R("i,j");
67 
68  //Avoid computing factorial pieces
69  double Fac[] = {1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800,
70  39916800};
71 
72  //Compute the first S comumator because we always want at least 1st order
73  Array2 steps = Scom(R, X, S);
74  for(std::size_t i = 0; i < order; ++i){
75  //add the next order approximation to R and then compute the one for the
76  //next round. This will always compute one more step than asked for right
77  //now, but it only returns the number of steps asked for.
78  LHS("i,j") = LHS("i,j") + 1/Fac[i] * steps("i,j");
79  steps = Scom(steps, X, S);
80  }
81  R = LHS;
82  }
83 
84  void
85  conjgradmat(const Array2 &F, Array2 &X, const Array2 &D,
86  const Array2 &Esymm, const Array2 &S,
87  const Array2 &Eanti, const std::size_t max_iter = 100) {
88  //Solve problem Eanti = F.X.D + D.X.F - 0.5( S.X.Esymm + Esymm.X.S)
89  //Using a modified conjugate gradient method.
90 
91  Array2 FXD = X; // = F("i,n") * X("n,m") * D("m,j") but X = 0
92  Array2 SXEs = X; // = S("i,n") * X("n,m") * Esymm("m,j") but X = 0
93  Array2 RHS = X; // (FXD("i,j") - FXD("j,i"))
94 
95  // Gradient = -Residual
96  Array2 R = Eanti; // (Eanti("i,j") - RHS("i,j")); RHS = 0
97 
98  // Search direction is the opposite of gradient
99  Array2 P_i = R;
100 
101  //Scalar product of the residual
102  double Rnorm2 = TA::expressions::norm2(R("i,j"));
103  double Rsold = Rnorm2 * Rnorm2;
104 
105 
106  Array2 d_old = P_i;
107 
108  std::size_t count = 0;
109  do{
110  F.world().gop.fence();
111  Array2 FPD = F("i,n") * P_i("n,m") * D("m,j");
112  Array2 SPEs = S("i,n") * P_i("n,m") * Esymm("m,j");
113 
114  //Precompute what traditioally is A * p_i in regular cg method
115  Array2 Ap_i = (FPD("i,j") - FPD("j,i") -
116  0.5 * (SPEs("i,j") - SPEs("j,i")) );
117 
118  //Forming alpha the optimized length to travel along p_i
119  double alpha = Rsold / TA::expressions::dot(P_i("i,j"), Ap_i("i,j"));
120 
121  // that the new step
122  Array2 d = alpha * P_i("i,j");
123 
124  //Updating the unknown
125  X("i,j") = X("i,j") + d("i,j");
126 
127  //Calculating the new residual
128  R("i,j") = R("i,j") - alpha * Ap_i("i,j");
129  double Rsnew = TA::expressions::norm2(R("i,j"));
130  Rsnew = Rsnew * Rsnew;
131 
132  //Finding a new search direction
133  P_i("i,j") = R("i,j") + (Rsnew/Rsold) * P_i("i,j");
134  Rsold = Rsnew;
135 
136  ++count;
137  }while( std::sqrt(Rsold) >= 1e-5 && count < max_iter);
138  if(count == max_iter){
139  std::cout << "Convergence Factor in Conjgradmat = "
140  << std::sqrt(Rsold) << std::endl;
141  //conjgradmat(F, X, D, Esymm, S, Eanti);
142  std::cout << "The maximum number of iterations was reached"
143  " in conjugate gradient" << std::endl;
144  }
145  F.world().gop.fence();
146  }
147 
148  void Density_Update(Array2 &R, const Array2 &S,
149  const Array2 &F, const std::size_t iter,
150  const std::size_t rotation_order = 2){
151  // Optimize the one electron density matrix to solve for the Hartree Fock
152  // Energy using the second order approach to modifying R (The denisty matrix)
153  // The equations being solved are
154  // F.Rn.S - S.Rn.F = F.X.S.Rn.S + S.Rn.S.X.F - 0.5( S.X.Esymm + Esymm.X.S)
155  // Esymm = F.Rn.S + S.Rn.F
156  //
157  // For more through explination see P.477 of Molecular Electronic-Structure Theory
158 
161  Array2 FRS = F("i,n") * R("n,m") * S("m,j");
162 
163  //Forming the RHS Eanti
164  Array2 Eanti = FRS("i,j") - FRS("j,i");
165  //Forming Esymm from above
166  Array2 Esymm = FRS("i,j") + FRS("j,i");
167 
168  //Pre computing S.R.S into SRS
169  Array2 SRS = S("i,n") * R("n,m") * S("m,j");
170 
171  //Making the X matrix which is an anti-symmetric matrix that performs the
172  //Rotations on R.
173  Array2 XD(S.world(), S.trange());
174  XD.set_all_local(0.0);
175 
176  //Solving for XD using a modified conjugate gradient method. `
177  conjgradmat(F, XD, SRS, Esymm, S, Eanti, iter * 20);
178 
181  double scale = 0.1/TA::expressions::norminf(XD("i,j"));
182  XD("i,j") = XD("i,j") * std::min(1.0,scale);
183 
184  XD.world().gop.fence();
185  //Rotating the Rm to Rn with X
186  RotateR(R,XD,S,rotation_order);
187 
188  XD.world().gop.fence();
189  //Purifying Rn so that it meets the criteria for a valid density matrix
190  Purify(R, S);
191 
192  R.world().gop.fence();
193  }
194 } // namespace curvy_steps
195 } // namespace tests
196 } // namespace mpqc
197 
198 
199 #endif /* CURVY_STEPS_HPP_ */
mpqc
Contains new MPQC code since version 3.
Definition: integralenginepool.hpp:37

Generated at Sun Jan 26 2020 23:24:01 for MPQC 3.0.0-alpha using the documentation package Doxygen 1.8.16.