Programming Projects: DIIS convergence acceleration in SCF
Overview
Often, the convergence of the SCF procedure outlined in the previous projects is disappointingly slow. Fortunately, many convergence acceleration procedures have been developed, and, in this tutorial, you will implement one of the most popular: the direct inversion of the iterative subspace (DIIS). This tutorial is based upon the DIIS procedure described by Peter Pulay here.
This tutorial assumes you have already developed a working Hatree-Fock code, based on one of these tutorials.
The C++ code for the DIIS-accelerated SCF plugin can be found here. The C++ code and corresponding header for the DIIS manager class can be found here and here, respectively.
Procedure:
The basic idea of DIIS is simple. In the course of an SCF optimization, we have a series of approximate solution vectors (e.g. orbitals, densities, or Fock matrices). For each approximate solution vector, we can define an associated error vector. In DIIS, we choose an extrapolated solution vector as a linear combination of previous vectors, and the coefficients are chosen in a way that minimizes the error associated with the new vector.
Step 1. Develop a Hartree-Fock code
You must first develop your own Hartree-Fock SCF plugin to Psi4. You can base your plugin off of either of these tutorials. The notation used below is consistent with that used in those tutorials.
Step 2. Choose the Fock matrix as the current solution vector
Once we begin the SCF iterations, the parameter that we will extrapolate will be the Fock matrix (in the orthonormal basis):
$${\bf \bar{F}}^\prime = \sum_i c_i {\bf F}^\prime_i$$ The coefficients, ci, will be chosen such that the error associated with the extrapolated Fock matrix will tend to zero
$${\bf \bar{e}} = \sum_i c_i {\bf e}_i \approx 0$$ We will also require that the coefficients sum to one
$$\sum_i c_i = 1$$ In general, this extrapolation should involve a small number of vectors, and you can store these matrices in main memory or on disk. The DIIS manager class given here stores eight solution and error vectors on disk.
Step 3. Choose an error vector
There are many possible definitions of the error vectors. For example, we could simply choose the change in the Fock matrix each iteration as an estimate of the error. Pulay proposed that the error vector should be given by the commutator of the Fock and density matrices (the orbital gradient) FiDiS-SDiFi, where Fi and Di are the Fock and density matrices at iteration i (in the AO or SO basis), and S is the overlap matrix. In the orthonormal basis defined by Löwdin's symmetric orthogonalization we have
$${\bf e}_i = ({\bf S}^{-1/2})^T [{\bf F}_i {\bf D}_i {\bf S} - {\bf S}{\bf D}_i{\bf F}_i ]{\bf S}^{-1/2}$$ As with the current solution vectors (the Fock matrices), we store a small number (eight) of error vectors. Again, you can store these in main memory or on disk.
Check your error vector from the first SCF iteration.
Step 4. Determining the coefficients, ci
Once you have accumulated at least two solution and error vectors, you can determine the coefficients, ci. The coefficients are chosen such that they minimize the norm of the extrapolated error vector
$$|{\bf \bar{e}}|^2 = \sum_{ij} c_i c_j {\bf e}_i \cdot {\bf e}_j$$ To find the coefficients, we use the method of Lagrange multipliers. We define the Lagrangian
$$L = \sum_{ij} c_i c_j B_{ij} - 2 \lambda \left ( \sum_i c_i -1 \right )$$ where
$$B_{ij} = {\bf e}_i \cdot {\bf e}_j$$ are elements of the error matrix, and λ is the Lagrange multiplier for the constraint that the coefficients sum to one. The extra factor of two in the Lagrangian is introduced for convenience and has no effect on the optimal value of the coefficients, ci. We arrive at the following system of equations when we require that the Lagrangian be stationary with respect to variations in all of the coefficients and the Lagrange multiplier.
$$\begin{pmatrix} B_{11} & B_{12} & \cdots & B_{1n} & -1 \\ B_{21} & B_{22} & \cdots & B_{2n} & -1 \\ \vdots & \vdots & \ddots & \vdots & \vdots \\ B_{n1} & B_{n2} & \cdots & B_{nn} & -1 \\ -1 & -1 & \cdots & -1 & 0 \\ \end{pmatrix} \begin{pmatrix} c_1 \\ c_2 \\ \vdots \\ c_n \\ \lambda \\ \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \\ \vdots \\ 0 \\ -1 \\ \end{pmatrix}$$ In your Psi4 plugin, you can solve this system of equations using the LAPACK routine DGESV. To do so, include the following header file:
#include "psi4/libqt/qt.h"You can then use the function C_DGESV. For the call structure for C_DGESV, see lapack_intfc.cc.
Note that the computational cost of the construction of the error matrix can be reduced by recognizing (i) the matrix is symmetric so only the lower or upper triangle need be computed and (ii) only one row/column must explicitly be computed each iteration. The remainder of the matrix can be stored (in memory or on disk) and reused iteration to iteration.
Check your coefficients from the first DIIS extrapolation.
Step 5. Extrapolate the Fock matrix
Once you have determined the coefficients, ci, extrapolate the Fock matrix according to
$${\bf \bar{F}}^\prime = \sum_i c_i {\bf F}^\prime_i$$ Now, we resume the Hatree-Fock procedure. Diagonalize the extrapolated Fock matrix to determine its eigenvectors (the molecular orbitals, C') and eigenvalues (the molecular orbital energies, ε)
$${\bf \bar{F}}^\prime {\bf C}^\prime = \epsilon {\bf C}^\prime$$ As in the previous tutorials, back transform one index of C' to the original AO basis in order to build the density matrix for the next iteration:
$${\bf C} = {\bf S}^{-1/2}{\bf C}^\prime$$
Step 6. Replacing solution and error vectors
Since only a small number of solution and error vectors (e.g eight) are used in the extrapolation procedure, we will need to replace some of the older vectors as the SCF procedure goes beyond eight iterations. You can choose to replace either the oldest vectors or those vectors with the largest associated error vector. Our DIIS manager replaces the vector with the largest associated error.
Step 7. Monitoring convergence
In our previous tutorials, the SCF procedure was considered converged when changes in energy and density between iterations fell below some predefined thresholds. Here, we use the change in the energy and the the root-mean square (RMS) orbital gradient to measure convergence.
Consider the Hartree-Fock procedure for the same molecule/basis set combinations used in the previous tutorials. Without DIIS, the energy and orbital gradient should converge to 12 decimal places in about 28 iterations:
Guess energy: -118.308230720196 ==> Begin SCF Iterations <== Iter energy dE RMS |[F,P]| 0 -73.196938802615 45.111291917580 0.161149650787 1 -74.939192979935 -1.742254177320 0.012516596135 2 -74.944687307745 -0.005494327810 0.002868867004 3 -74.945047846381 -0.000360538636 0.000948342720 4 -74.945095717316 -0.000047870934 0.000366501367 5 -74.945103245253 -0.000007527938 0.000147962847 6 -74.945104500848 -0.000001255594 0.000060823893 7 -74.945104714602 -0.000000213755 0.000025151359 8 -74.945104751242 -0.000000036639 0.000010420756 9 -74.945104757536 -0.000000006294 0.000004320278 10 -74.945104758618 -0.000000001082 0.000001791480 11 -74.945104758804 -0.000000000186 0.000000742916 12 -74.945104758836 -0.000000000032 0.000000308089 13 -74.945104758842 -0.000000000006 0.000000127766 14 -74.945104758842 -0.000000000001 0.000000052985 15 -74.945104758843 -0.000000000000 0.000000021973 16 -74.945104758843 0.000000000000 0.000000009112 17 -74.945104758843 0.000000000000 0.000000003779 18 -74.945104758843 -0.000000000000 0.000000001567 19 -74.945104758843 0.000000000000 0.000000000650 20 -74.945104758843 0.000000000000 0.000000000270 21 -74.945104758843 0.000000000000 0.000000000112 22 -74.945104758843 0.000000000000 0.000000000046 23 -74.945104758843 0.000000000000 0.000000000019 24 -74.945104758843 0.000000000000 0.000000000008 25 -74.945104758843 0.000000000000 0.000000000003 26 -74.945104758843 -0.000000000000 0.000000000001 27 -74.945104758843 0.000000000000 0.000000000001 SCF iterations converged! * SCF total energy: -74.945104758843
For the same convergence thresholds, DIIS reduces the number of iterations to only nine:
Guess energy: -118.308230720196 ==> Begin SCF Iterations <== Iter energy dE RMS |[F,P]| 0 -73.196938802615 45.111291917580 0.161149650787 1 -74.939192979935 -1.742254177320 0.012516596135 2 -74.944687307745 -0.005494327810 0.002868867004 3 -74.945059469653 -0.000372161908 0.000830998716 4 -74.945103419151 -0.000043949498 0.000142890940 5 -74.945104756790 -0.000001337639 0.000006828537 6 -74.945104758843 -0.000000002052 0.000000068811 7 -74.945104758843 -0.000000000000 0.000000000398 8 -74.945104758843 0.000000000000 0.000000000000 SCF iterations converged! * SCF total energy: -74.945104758843
If we double the O-H bond length
molecule { O H 1 R H 1 R 2 A R = 1.8 A = 104.5 # add this line! symmetry c1 }
we find that the basic SCF procedure does not converge:
Guess energy: -116.837914524518 ==> Begin SCF Iterations <== Iter energy dE RMS |[F,P]| 0 -73.226880190015 43.611034334503 0.037704098895 1 -73.362990265269 -0.136110075254 0.068817042077 2 -73.553585133343 -0.190594868074 0.076418280252 3 -73.590354082648 -0.036768949305 0.082674491025 4 -73.682157222699 -0.091803140051 0.082580895233 5 -73.678727769308 0.003429453391 0.085365751694 6 -73.735922659086 -0.057194889778 0.084107422060 7 -73.717253914233 0.018668744853 0.086133433309 8 -73.760261977557 -0.043008063324 0.084608450174 9 -73.735037381968 0.025224595589 0.086406150756 10 -73.771698133826 -0.036660751858 0.084803784715 11 -73.743474775749 0.028223358077 0.086517537730 12 -73.777171073830 -0.033696298081 0.084888261557 13 -73.747532013052 0.029639060777 0.086566973068 14 -73.779813873486 -0.032281860434 0.084926973128 15 -73.749495788237 0.030318085249 0.086589937774 16 -73.781095646346 -0.031599858109 0.084945260963 17 -73.750449319650 0.030646326696 0.086600861938 18 -73.781718642130 -0.031269322479 0.084954034686 19 -73.750913034440 0.030805607690 0.086606120963 20 -73.782021759446 -0.031108725006 0.084958276341 21 -73.751138715146 0.030883044300 0.086608667749 22 -73.782169315262 -0.031030600116 0.084960334716 23 -73.751248589775 0.030920725488 0.086609904671 24 -73.782241162329 -0.030992572554 0.084961335442 25 -73.751302092769 0.030939069560 0.086610506272 26 -73.782276149910 -0.030974057141 0.084961822407 27 -73.751328148099 0.030948001811 0.086610799076 28 -73.782293188914 -0.030965040815 0.084962059474 29 -73.751340837277 0.030952351637 0.086610941634 30 -73.782301487171 -0.030960649894 0.084962174909 31 -73.751347017148 0.030954470023 0.086611011053 32 -73.782305528605 -0.030958511458 0.084962231124 33 -73.751350026892 0.030955501713 0.086611044860 34 -73.782307496887 -0.030957469994 0.084962258500 35 -73.751351492717 0.030956004170 0.086611061324 36 -73.782308455493 -0.030956962776 0.084962271833 37 -73.751352206614 0.030956248879 0.086611069342 38 -73.782308922361 -0.030956715747 0.084962278327 39 -73.751352554301 0.030956368059 0.086611073248 40 -73.782309149738 -0.030956595437 0.084962281489 41 -73.751352723635 0.030956426103 0.086611075149 42 -73.782309260478 -0.030956536843 0.084962283029 43 -73.751352806106 0.030956454372 0.086611076076 44 -73.782309314411 -0.030956508306 0.084962283779 45 -73.751352846271 0.030956468140 0.086611076527 46 -73.782309340678 -0.030956494407 0.084962284145 47 -73.751352865833 0.030956474845 0.086611076747 48 -73.782309353471 -0.030956487638 0.084962284323 49 -73.751352875360 0.030956478111 0.086611076854 50 -73.782309359701 -0.030956484342 0.084962284409
Without DIIS, the SCF procedure appears to oscillate between two different solutions. On the other hand, SCF with DIIS converges nicely in only 15 iterations:
Guess energy: -116.837914524518 ==> Begin SCF Iterations <== Iter energy dE RMS |[F,P]| 0 -73.226880190015 43.611034334503 0.037704098895 1 -73.362990265269 -0.136110075254 0.068817042077 2 -73.553585133343 -0.190594868074 0.076418280252 3 -74.464883669436 -0.911298536093 0.032080900797 4 -74.429023374665 0.035860294771 0.041606480071 5 -74.456614357821 -0.027590983157 0.034310669154 6 -74.511256860259 -0.054642502438 0.000395057332 7 -74.511195829728 0.000061030531 0.001422608517 8 -74.511030534112 0.000165295616 0.002554059031 9 -74.511052613564 -0.000022079452 0.002456520204 10 -74.511052329069 0.000000284495 0.002457817744 11 -74.511322873032 -0.000270543963 0.000008732217 12 -74.511322876759 -0.000000003727 0.000000074001 13 -74.511322876760 -0.000000000000 0.000000000420 14 -74.511322876760 0.000000000000 0.000000000001 SCF iterations converged! * SCF total energy: -74.511322876760
You can check your expansion coefficients, F' matrices, or error vectors for this case.
We should note here that there are other methods, such as level shifting, that can improve the convergence of SCF in divergent cases without the use of DIIS.