conjgrad.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  * conjgrad.h
22  * May 20, 2013
23  *
24  */
25 
26 #ifndef TILEDARRAY_MATH_LINALG_CONJGRAD_H__INCLUDED
27 #define TILEDARRAY_MATH_LINALG_CONJGRAD_H__INCLUDED
28 
31 #include "TiledArray/dist_array.h"
32 
33 namespace TiledArray::math::linalg {
34 
35 // clang-format off
39 
60 // clang-format on
61 template <typename D, typename F>
63  typedef typename D::element_type value_type;
64 
72  value_type operator()(F& a, const D& b, D& x, const D& preconditioner,
73  value_type convergence_target = -1.0) {
74  std::size_t n = volume(preconditioner);
75 
76  const bool use_diis = false;
77  DIIS<D> diis;
78 
79  // solution vector
80  D XX_i;
81  // residual vector
82  D RR_i = clone(b);
83  // preconditioned residual vector
84  D ZZ_i;
85  // direction vector
86  D PP_i;
87  D APP_i = clone(b);
88 
89  // approximate the conditio number as the ratio of the min and max elements
90  // of the preconditioner assuming that preconditioner is the approximate
91  // inverse of A in Ax - b =0
92  const value_type precond_min = abs_min(preconditioner);
93  const value_type precond_max = abs_max(preconditioner);
94  const value_type cond_number = precond_max / precond_min;
95  // std::cout << "condition number = " << precond_max << " / " << precond_min
96  // << " = " << cond_number << std::endl;
97  // if convergence target is given, estimate of how tightly the system can be
98  // converge
99  if (convergence_target < 0.0) {
100  convergence_target = 1e-15 * cond_number;
101  } else { // else warn if the given system is not sufficiently well
102  // conditioned
103  if (convergence_target < 1e-15 * cond_number)
104  std::cout << "WARNING: ConjugateGradient convergence target ("
105  << convergence_target
106  << ") may be too low for 64-bit precision" << std::endl;
107  }
108 
109  bool converged = false;
110  const unsigned int max_niter = n;
111  value_type rnorm2 = 0.0;
112  const std::size_t rhs_size = volume(b);
113 
114  // starting guess: x_0 = D^-1 . b
115  XX_i = b;
116  vec_multiply(XX_i, preconditioner);
117 
118  // r_0 = b - a(x)
119  a(XX_i, RR_i); // RR_i = a(XX_i)
120  scale(RR_i, -1.0);
121  axpy(RR_i, 1.0, b); // RR_i = b - a(XX_i)
122 
123  if (use_diis) diis.extrapolate(XX_i, RR_i, true);
124 
125  // z_0 = D^-1 . r_0
126  ZZ_i = RR_i;
127  vec_multiply(ZZ_i, preconditioner);
128 
129  // p_0 = z_0
130  PP_i = ZZ_i;
131 
132  unsigned int iter = 0;
133  while (not converged) {
134  // alpha_i = (r_i . z_i) / (p_i . A . p_i)
135  value_type rz_norm2 = inner_product(RR_i, ZZ_i);
136  a(PP_i, APP_i);
137 
138  const value_type pAp_i = inner_product(PP_i, APP_i);
139  const value_type alpha_i = rz_norm2 / pAp_i;
140 
141  // x_i += alpha_i p_i
142  axpy(XX_i, alpha_i, PP_i);
143 
144  // r_i -= alpha_i Ap_i
145  axpy(RR_i, -alpha_i, APP_i);
146 
147  if (use_diis) diis.extrapolate(XX_i, RR_i, true);
148 
149  const value_type r_ip1_norm = norm2(RR_i) / rhs_size;
150  if (r_ip1_norm < convergence_target) {
151  converged = true;
152  rnorm2 = r_ip1_norm;
153  }
154 
155  // z_i = D^-1 . r_i
156  ZZ_i = RR_i;
157  vec_multiply(ZZ_i, preconditioner);
158 
159  const value_type rz_ip1_norm2 = inner_product(ZZ_i, RR_i);
160 
161  const value_type beta_i = rz_ip1_norm2 / rz_norm2;
162 
163  // p_i = z_i+1 + beta_i p_i
164  // 1) scale p_i by beta_i
165  // 2) add z_i+1 (i.e. current contents of z_i)
166  scale(PP_i, beta_i);
167  axpy(PP_i, 1.0, ZZ_i);
168 
169  ++iter;
170  // std::cout << "iter=" << iter << " dnorm=" << r_ip1_norm << std::endl;
171 
172  if (converged) {
173  x = XX_i;
174  } else if (iter >= max_niter)
175  throw std::domain_error(
176  "ConjugateGradient: max # of iterations exceeded");
177  } // solver loop
178 
179  x = XX_i;
180 
181  return rnorm2;
182  }
183 };
184 
185 } // namespace TiledArray::math::linalg
186 
187 namespace TiledArray {
189 }
190 
191 #endif // TILEDARRAY_MATH_LINALG_CONJGRAD_H__INCLUDED
auto norm2(const DistArray< Tile, Policy > &a)
Definition: dist_array.h:1660
auto inner_product(const DistArray< Tile, Policy > &a, const DistArray< Tile, Policy > &b)
Definition: dist_array.h:1647
DIIS (`‘direct inversion of iterative subspace’') extrapolation.
Definition: diis.h:83
DistArray< Tile, Policy > clone(const DistArray< Tile, Policy > &arg)
Create a deep copy of an array.
Definition: clone.h:43
void scale(DistArray< Tile, Policy > &a, S scaling_factor)
Definition: basic.h:44
auto abs_min(const DistArray< Tile, Policy > &a)
Definition: dist_array.h:1630
value_type operator()(F &a, const D &b, D &x, const D &preconditioner, value_type convergence_target=-1.0)
Definition: conjgrad.h:72
auto abs_max(const DistArray< Tile, Policy > &a)
Definition: dist_array.h:1635
void extrapolate(D &x, D &error, bool extrapolate_error=false)
Definition: diis.h:144
void vec_multiply(DistArray< Tile, Policy > &a1, const DistArray< Tile, Policy > &a2)
Definition: basic.h:37
size_t volume(const DistArray< Tile, Policy > &a)
Definition: dist_array.h:1622
void axpy(DistArray< Tile, Policy > &y, S alpha, const DistArray< Tile, Policy > &x)
Definition: basic.h:56