LIBINT 2.9.0
diis.h
1/*
2 * Copyright (C) 2004-2024 Edward F. Valeev
3 *
4 * This file is part of Libint library.
5 *
6 * Libint library is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Libint library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Libint library. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#ifndef _libint2_src_lib_libint_diis_h_
22#define _libint2_src_lib_libint_diis_h_
23
24#include <libint2/util/cxxstd.h>
25#if LIBINT2_CPLUSPLUS_STD < 2011
26#error "libint2/diis.h requires C++11 support"
27#endif
28
29#include <deque>
30
31#pragma GCC diagnostic push
32#pragma GCC system_header
33#include <Eigen/Core>
34#include <Eigen/QR>
35#pragma GCC diagnostic pop
36
37namespace libint2 {
38
39namespace diis {
40
41template <typename D>
42struct traits;
43
44/*
45template <typename D>
46typename traits<D>::element_type
47dot_product(const D& d1, const D& d2);
48
49template <typename D>
50void
51zero(D& d);
52
53template <typename D, typename Scalar>
54void
55axpy(const D& y, Scalar a, const D& x);
56*/
57
58template <typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows,
59 int _MaxCols>
60struct traits<
61 Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>> {
62 typedef Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> D;
63 typedef typename D::Scalar element_type;
64};
65
66template <typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows,
67 int _MaxCols>
68typename traits<Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows,
69 _MaxCols>>::element_type
70dot_product(const Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows,
71 _MaxCols>& d1,
72 const Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows,
73 _MaxCols>& d2) {
74 return d1.cwiseProduct(d2).sum();
75}
76
77template <typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows,
78 int _MaxCols>
79void zero(
80 Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>& d) {
81 d.setZero(d.rows(), d.cols());
82}
83
84template <typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows,
85 int _MaxCols, typename Scalar>
86void axpy(Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>& y,
87 Scalar a,
88 const Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows,
89 _MaxCols>& x) {
90 y += a * x;
91}
92
93} // namespace diis
94
96
139template <typename D>
140class DIIS {
141 public:
142 typedef typename diis::traits<D>::element_type value_type;
143
145
164 DIIS(unsigned int strt = 1, unsigned int ndi = 5, value_type dmp = 0,
165 unsigned int ngr = 1, unsigned int ngrdiis = 1, value_type mf = 0)
166 : error_(0),
167 errorset_(false),
168 start(strt),
169 ndiis(ndi),
170 iter(0),
171 ngroup(ngr),
172 ngroupdiis(ngrdiis),
173 damping_factor(dmp),
174 mixing_fraction(mf) {
175 init();
176 }
177 ~DIIS() {
178 x_.clear();
179 errors_.clear();
180 x_extrap_.clear();
181 }
182
190 void extrapolate(D& x, D& error, bool extrapolate_error = false) {
191 using namespace ::libint2::diis;
192
193 const value_type zero_determinant =
194 std::numeric_limits<value_type>::epsilon();
195 const value_type zero_norm = 1.0e-10;
196
197 iter++;
198
199 const bool do_mixing = (mixing_fraction != 0.0);
200 const value_type scale = 1.0 + damping_factor;
201
202 // if have ndiis vectors
203 if (errors_.size() == ndiis) { // holding max # of vectors already? drop
204 // the least recent {x, error} pair
205 x_.pop_front();
206 errors_.pop_front();
207 if (not x_extrap_.empty()) x_extrap_.pop_front();
208 EigenMatrixX Bcrop = B_.bottomRightCorner(ndiis - 1, ndiis - 1);
209 Bcrop.conservativeResize(ndiis, ndiis);
210 B_ = Bcrop;
211 }
212
213 // push {x, error} to the set
214 x_.push_back(x);
215 errors_.push_back(error);
216 const unsigned int nvec = errors_.size();
217 assert(x_.size() == errors_.size());
218
219 // and compute the most recent elements of B, B(i,j) = <ei|ej>
220 for (unsigned int i = 0; i < nvec - 1; i++)
221 B_(i, nvec - 1) = B_(nvec - 1, i) =
222 dot_product(errors_[i], errors_[nvec - 1]);
223 B_(nvec - 1, nvec - 1) = dot_product(errors_[nvec - 1], errors_[nvec - 1]);
224
225 if (iter == 1) { // the first iteration
226 if (not x_extrap_.empty() && do_mixing) {
227 zero(x);
228 axpy(x, (1.0 - mixing_fraction), x_[0]);
229 axpy(x, mixing_fraction, x_extrap_[0]);
230 }
231 } else if (iter > start && (((iter - start) % ngroup) <
232 ngroupdiis)) { // not the first iteration and
233 // need to extrapolate?
234
235 EigenVectorX c;
236
237 value_type absdetA;
238 unsigned int nskip = 0; // how many oldest vectors to skip for the sake
239 // of conditioning? try zero
240 // skip oldest vectors until found a numerically stable system
241 do {
242 const unsigned int rank = nvec - nskip + 1; // size of matrix A
243
244 // set up the DIIS linear system: A c = rhs
245 EigenMatrixX A(rank, rank);
246 A.col(0).setConstant(-1.0);
247 A.row(0).setConstant(-1.0);
248 A(0, 0) = 0.0;
249 EigenVectorX rhs = EigenVectorX::Zero(rank);
250 rhs[0] = -1.0;
251
252 value_type norm = 1.0;
253 if (B_(nskip, nskip) > zero_norm) norm = 1.0 / B_(nskip, nskip);
254
255 A.block(1, 1, rank - 1, rank - 1) =
256 B_.block(nskip, nskip, rank - 1, rank - 1) * norm;
257 A.diagonal() *= scale;
258 // for (unsigned int i=1; i < rank ; i++) {
259 // for (unsigned int j=1; j <= i ; j++) {
260 // A(i, j) = A(j, i) = B_(i+nskip-1, j+nskip-1) * norm;
261 // if (i==j) A(i, j) *= scale;
262 // }
263 // }
264
265#if 0
266 std::cout << "DIIS: iter=" << iter << " nskip=" << nskip << " nvec=" << nvec << std::endl;
267 std::cout << "DIIS: B=" << B_ << std::endl;
268 std::cout << "DIIS: A=" << A << std::endl;
269 std::cout << "DIIS: rhs=" << rhs << std::endl;
270#endif
271
272 // finally, solve the DIIS linear system
273 Eigen::ColPivHouseholderQR<EigenMatrixX> A_QR = A.colPivHouseholderQr();
274 c = A_QR.solve(rhs);
275 absdetA = A_QR.absDeterminant();
276
277 // std::cout << "DIIS: |A|=" << absdetA << " sol=" << c << std::endl;
278
279 ++nskip;
280
281 } while (absdetA < zero_determinant &&
282 nskip < nvec); // while (system is poorly conditioned)
283
284 // failed?
285 if (absdetA < zero_determinant) {
286 std::ostringstream oss;
287 oss << "DIIS::extrapolate: poorly-conditioned system, |A| = "
288 << absdetA;
289 throw std::domain_error(oss.str());
290 }
291 --nskip; // undo the last ++ :-(
292
293 {
294 zero(x);
295 for (unsigned int k = nskip, kk = 1; k < nvec; ++k, ++kk) {
296 if (not do_mixing || x_extrap_.empty()) {
297 // std::cout << "contrib " << k << " c=" << c[kk] << ":" <<
298 // std::endl << x_[k] << std::endl;
299 axpy(x, c[kk], x_[k]);
300 if (extrapolate_error) axpy(error, c[kk], errors_[k]);
301 } else {
302 axpy(x, c[kk] * (1.0 - mixing_fraction), x_[k]);
303 axpy(x, c[kk] * mixing_fraction, x_extrap_[k]);
304 }
305 }
306 }
307 } // do DIIS
308
309 // only need to keep extrapolated x if doing mixing
310 if (do_mixing) x_extrap_.push_back(x);
311 }
312
317 if (start > iter) start = iter + 1;
318 }
319
320 void reinitialize(const D* data = 0) {
321 iter = 0;
322 if (data) {
323 const bool do_mixing = (mixing_fraction != 0.0);
324 if (do_mixing) x_extrap_.push_front(*data);
325 }
326 }
327
328 private:
329 value_type error_;
330 bool errorset_;
331
332 unsigned int start;
333 unsigned int ndiis;
334 unsigned int iter;
335 unsigned int ngroup;
336 unsigned int ngroupdiis;
337 value_type damping_factor;
338 value_type mixing_fraction;
339
340 typedef Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic,
341 Eigen::RowMajor>
342 EigenMatrixX;
343 typedef Eigen::Matrix<value_type, Eigen::Dynamic, 1> EigenVectorX;
344
345 EigenMatrixX B_;
346
347 std::deque<D>
348 x_;
349 std::deque<D> errors_;
350 std::deque<D> x_extrap_;
351
352 void set_error(value_type e) {
353 error_ = e;
354 errorset_ = true;
355 }
356 value_type error() { return error_; }
357
358 void init() {
359 iter = 0;
360
361 B_ = EigenMatrixX::Zero(ndiis, ndiis);
362
363 x_.clear();
364 errors_.clear();
365 x_extrap_.clear();
366 // x_.resize(ndiis);
367 // errors_.resize(ndiis);
368 // x_extrap_ is bigger than the other because
369 // it must hold data associated with the next iteration
370 // x_extrap_.resize(diis+1);
371 }
372
373}; // class DIIS
374
375} // namespace libint2
376
377#include <libint2/engine.h>
378
379#endif /* _libint2_src_lib_libint_diis_h_ */
DIIS (`‘direct inversion of iterative subspace’') extrapolation.
Definition diis.h:140
void start_extrapolation()
calling this function forces the extrapolation to start upon next call to extrapolate() even if this ...
Definition diis.h:316
void extrapolate(D &x, D &error, bool extrapolate_error=false)
Definition diis.h:190
DIIS(unsigned int strt=1, unsigned int ndi=5, value_type dmp=0, unsigned int ngr=1, unsigned int ngrdiis=1, value_type mf=0)
Constructor.
Definition diis.h:164
Defaults definitions for various parameters assumed by Libint.
Definition algebra.cc:24
Definition diis.h:42