MPQC 3.0.0-alpha
Loading...
Searching...
No Matches
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
14namespace mpqc {
15namespace tests {
16 using Array2 = TA::Array<double,2>;
17 using Array3 = TA::Array<double,3>;
18 using Array4 = TA::Array<double,4>;
19
20namespace 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_ */
Contains new MPQC code since version 3.
Definition integralenginepool.hpp:37

Generated at Wed Sep 25 2024 02:45:30 for MPQC 3.0.0-alpha using the documentation package Doxygen 1.12.0.