TiledArray  0.7.0
diis.h
Go to the documentation of this file.
1 /*
2  * This file is a part of TiledArray.
3  * Copyright (C) 2013 Virginia Tech
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 3 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
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * Eduard Valeyev
19  * Department of Chemistry, Virginia Tech
20  *
21  * diis.h
22  * May 20, 2013
23  *
24  */
25 
26 #ifndef TILEDARRAY_ALGEBRA_DIIS_H__INCLUDED
27 #define TILEDARRAY_ALGEBRA_DIIS_H__INCLUDED
28 
29 #include <deque>
30 #include <TiledArray/math/eigen.h>
32 #include "../dist_array.h"
33 
34 namespace TiledArray {
35 
37 
80  template <typename D>
81  class DIIS {
82  public:
83  typedef typename D::element_type value_type;
87 
89 
108  DIIS(unsigned int strt=1,
109  unsigned int ndi=5,
110  scalar_type dmp =0,
111  unsigned int ngr=1,
112  unsigned int ngrdiis=1,
113  scalar_type mf=0) :
114  error_(0), errorset_(false),
115  start(strt), ndiis(ndi),
116  iter(0), ngroup(ngr),
117  ngroupdiis(ngrdiis),
118  damping_factor(dmp),
119  mixing_fraction(mf)
120  {
121  init();
122  }
123  ~DIIS() {
124  x_.clear();
125  errors_.clear();
126  x_extrap_.clear();
127  }
128 
136  void extrapolate(D& x,
137  D& error,
138  bool extrapolate_error = false)
139  {
140  iter++;
141 
142  // compute extrapolation coefficients C_ and number of skipped vectors nskip_
144 
145  // extrapolate x using above computed parameters (C_ and nskip_)
146  extrapolate(x, C_, nskip_);
147 
148  const unsigned int nvec = errors_.size();
149 
150  // sizes of the x set and the error set should equal, otherwise throw
151  TA_USER_ASSERT(x_.size() == errors_.size(),
152  "DIIS: numbers of guess and error vectors do not match, likely due to a programming error");
153 
154  // extrapolate the error if needed
155  if (extrapolate_error && (mixing_fraction == 0.0 || x_extrap_.empty())) {
156  for (unsigned int k=nskip_, kk=1; k < nvec; ++k, ++kk) {
157  axpy(error, C_[kk], errors_[k]);
158  }
159  }
160  }
161 
170  void extrapolate(D& x,
171  const EigenVectorX &c,
172  unsigned int nskip = 0,
173  bool increase_iter = false) {
174  if (increase_iter) {
175  iter++;
176  }
177 
178  const bool do_mixing = (mixing_fraction != 0.0);
179 
180  // if have ndiis vectors
181  if (x_.size() == ndiis) { // holding max # of vectors already? drop the least recent x
182  x_.pop_front();
183  if (not x_extrap_.empty()) x_extrap_.pop_front();
184  }
185 
186  // push x to the set
187  x_.push_back(x);
188 
189  if (iter == 1) { // the first iteration
190  if (not x_extrap_.empty() && do_mixing) {
191  zero(x);
192  axpy(x, (1.0-mixing_fraction), x_[0]);
193  axpy(x, mixing_fraction, x_extrap_[0]);
194  }
195  }
196  else if (iter > start && (((iter - start) % ngroup) < ngroupdiis)) { // not the first iteration and need to extrapolate?
197 
198  const unsigned int nvec = x_.size();
199  const unsigned int rank = nvec - nskip + 1; // size of coefficients
200 
201  TA_USER_ASSERT(c.size() == rank,
202  "DIIS: numbers of coefficients and x's do not match");
203  zero(x);
204  for (unsigned int k=nskip, kk=1; k < nvec; ++k, ++kk) {
205  if (not do_mixing || x_extrap_.empty()) {
206  //std::cout << "contrib " << k << " c=" << c[kk] << ":" << std::endl << x_[k] << std::endl;
207  axpy(x, c[kk], x_[k]);
208  } else {
209  axpy(x, c[kk] * (1.0 - mixing_fraction), x_[k]);
210  axpy(x, c[kk] * mixing_fraction, x_extrap_[k]);
211  }
212  }
213 
214  } // do DIIS
215 
216  // only need to keep extrapolated x if doing mixing
217  if (do_mixing) x_extrap_.push_back(x);
218  }
219 
225  void compute_extrapolation_parameters(const D &error,
226  bool increase_iter = false) {
227  if (increase_iter) {
228  iter++;
229  }
230 
231  const scalar_type zero_determinant = 1.0e-15;
232  const scalar_type zero_norm = 1.0e-10;
233  const scalar_type scale = 1.0 + damping_factor;
234 
235  // if have ndiis vectors
236  if (errors_.size() == ndiis) { // holding max # of vectors already? drop the least recent error
237  errors_.pop_front();
238  EigenMatrixX Bcrop = B_.bottomRightCorner(ndiis-1,ndiis-1);
239  Bcrop.conservativeResize(ndiis,ndiis);
240  B_ = Bcrop;
241  }
242 
243  // push error to the set
244  errors_.push_back(error);
245  const unsigned int nvec = errors_.size();
246 
247  // and compute the most recent elements of B, B(i,j) = <ei|ej>
248  for (unsigned int i=0; i < nvec-1; i++)
249  B_(i,nvec-1) = B_(nvec-1,i) = dot_product(errors_[i], errors_[nvec-1]);
250  B_(nvec-1,nvec-1) = dot_product(errors_[nvec-1], errors_[nvec-1]);
251 
252  // compute extrapolation coefficients C_ and number of skipped vectors nskip_
253  if (iter > start && (((iter - start) % ngroup) < ngroupdiis)) { // not the first iteration and need to extrapolate?
254 
255  scalar_type absdetA;
256  nskip_ = 0; // how many oldest vectors to skip for the sake of conditioning?
257  // try zero
258  do {
259  const unsigned int rank = nvec - nskip_ + 1; // size of matrix A
260 
261  // set up the DIIS linear system: A c = rhs
262  EigenMatrixX A(rank, rank);
263  C_.resize(rank);
264 
265  A.col(0).setConstant(-1.0);
266  A.row(0).setConstant(-1.0);
267  A(0,0) = 0.0;
268  EigenVectorX rhs = EigenVectorX::Zero(rank);
269  rhs[0] = -1.0;
270 
271  scalar_type norm = 1.0;
272  if (std::abs(B_(nskip_,nskip_)) > zero_norm)
273  norm = 1.0/std::abs(B_(nskip_,nskip_));
274 
275  A.block(1, 1, rank-1, rank-1) = B_.block(nskip_, nskip_, rank-1, rank-1) * norm;
276  A.diagonal() *= scale;
277  //for (unsigned int i=1; i < rank ; i++) {
278  // for (unsigned int j=1; j <= i ; j++) {
279  // A(i, j) = A(j, i) = B_(i+nskip-1, j+nskip-1) * norm;
280  // if (i==j) A(i, j) *= scale;
281  // }
282  //}
283 
284 #if 0
285  std::cout << "DIIS: iter=" << iter << " nskip=" << nskip << " nvec=" << nvec << std::endl;
286  std::cout << "DIIS: B=" << B_ << std::endl;
287  std::cout << "DIIS: A=" << A << std::endl;
288  std::cout << "DIIS: rhs=" << rhs << std::endl;
289 #endif
290 
291  // finally, solve the DIIS linear system
292  Eigen::ColPivHouseholderQR<EigenMatrixX> A_QR = A.colPivHouseholderQr();
293  C_ = A_QR.solve(rhs);
294  absdetA = A_QR.absDeterminant();
295 
296  //std::cout << "DIIS: |A|=" << absdetA << " sol=" << c << std::endl;
297 
298  ++nskip_;
299 
300  } while (absdetA < zero_determinant && nskip_ < nvec); // while (system is poorly conditioned)
301 
302  // failed?
303  if (absdetA < zero_determinant) {
304  std::ostringstream oss;
305  oss << "DIIS::extrapolate: poorly-conditioned system, |A| = " << absdetA;
306  throw std::domain_error(oss.str());
307  }
308  --nskip_; // undo the last ++ :-(
309 
310  parameters_computed_ = true;
311  }
312 
313  }
314 
319  if (start > iter) start = iter+1;
320  }
321 
322  void reinitialize(const D* data = 0) {
323  iter=0;
324  if (data) {
325  const bool do_mixing = (mixing_fraction != 0.0);
326  if (do_mixing) x_extrap_.push_front(*data);
327  }
328  }
329 
332  TA_USER_ASSERT(parameters_computed_ && C_.size() > 0,
333  "DIIS: empty coefficients, because they have not been computed");
334  return C_;
335  }
336 
338  unsigned int get_nskip() { return nskip_; }
339 
341  bool parameters_computed() { return parameters_computed_; }
342 
343  private:
344  scalar_type error_;
345  bool errorset_;
346 
347  unsigned int start;
348  unsigned int ndiis;
349  unsigned int iter;
350  unsigned int ngroup;
351  unsigned int ngroupdiis;
352  scalar_type damping_factor;
353  scalar_type mixing_fraction;
354 
355  EigenMatrixX B_;
356  EigenVectorX C_;
357  bool parameters_computed_;
358  unsigned int nskip_;
359 
360  std::deque<D> x_;
361  std::deque<D> errors_;
362  std::deque<D> x_extrap_;
363 
364  void set_error(scalar_type e) { error_ = e; errorset_ = true; }
365  scalar_type error() { return error_; }
366 
367  void init() {
368  iter = 0;
369 
370  B_ = EigenMatrixX::Zero(ndiis,ndiis);
371  C_.resize(0);
372  parameters_computed_ = false;
373  nskip_ = 0;
374 
375  x_.clear();
376  errors_.clear();
377  x_extrap_.clear();
378  //x_.resize(ndiis);
379  //errors_.resize(ndiis);
380  // x_extrap_ is bigger than the other because
381  // it must hold data associated with the next iteration
382  //x_extrap_.resize(diis+1);
383  }
384 
385  }; // class DIIS
386 
387 } // namespace TiledArray
388 
389 #endif // TILEDARRAY_ALGEBRA_DIIS_H__INCLUDED
Eigen::Matrix< value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > EigenMatrixX
Definition: diis.h:85
auto data(T &t)
Container data pointer accessor.
Definition: utility.h:89
DIIS(unsigned int strt=1, unsigned int ndi=5, scalar_type dmp=0, unsigned int ngr=1, unsigned int ngrdiis=1, scalar_type mf=0)
Constructor.
Definition: diis.h:108
void extrapolate(D &x, const EigenVectorX &c, unsigned int nskip=0, bool increase_iter=false)
Definition: diis.h:170
void scale(DistArray< Tile, Policy > &a, typename DistArray< Tile, Policy >::element_type scaling_factor)
Definition: utils.h:108
decltype(auto) norm(const Tile< Arg > &arg)
Vector 2-norm of a tile.
Definition: tile.h:930
bool parameters_computed()
calling this function returns whether diis parameters C_ and nskip_ have been computed ...
Definition: diis.h:341
auto abs(const ComplexConjugate< T > &a)
Definition: complex.h:247
Eigen::Matrix< value_type, Eigen::Dynamic, 1 > EigenVectorX
Definition: diis.h:86
void start_extrapolation()
Definition: diis.h:318
unsigned int get_nskip()
calling this function returns number of skipped vectors in extrapolation
Definition: diis.h:338
typename TiledArray::detail::scalar_type< T >::type scalar_t
Definition: type_traits.h:555
detail::scalar_t< value_type > scalar_type
Definition: diis.h:84
void axpy(DistArray< Tile, Policy > &y, typename DistArray< Tile, Policy >::element_type a, const DistArray< Tile, Policy > &x)
Definition: utils.h:115
void zero(DistArray< Tile, Policy > &a)
Definition: utils.h:63
D::element_type value_type
Definition: diis.h:83
DIIS (‘‘direct inversion of iterative subspace’’) extrapolation.
Definition: diis.h:81
void extrapolate(D &x, D &error, bool extrapolate_error=false)
Definition: diis.h:136
void reinitialize(const D *data=0)
Definition: diis.h:322
#define TA_USER_ASSERT(a, m)
Definition: error.h:123
void compute_extrapolation_parameters(const D &error, bool increase_iter=false)
Definition: diis.h:225
const EigenVectorX & get_coeffs()
calling this function returns extrapolation coefficients
Definition: diis.h:331
DistArray< Tile, Policy >::element_type dot_product(const DistArray< Tile, Policy > &a1, const DistArray< Tile, Policy > &a2)
Definition: utils.h:89