FV3 Bundle
DRPLanczosMinimizer.h
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2009-2016 ECMWF.
3  *
4  * This software is licensed under the terms of the Apache Licence Version 2.0
5  * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6  * In applying this licence, ECMWF does not waive the privileges and immunities
7  * granted to it by virtue of its status as an intergovernmental organisation nor
8  * does it submit to any jurisdiction.
9  */
10 
11 #ifndef OOPS_ASSIMILATION_DRPLANCZOSMINIMIZER_H_
12 #define OOPS_ASSIMILATION_DRPLANCZOSMINIMIZER_H_
13 
14 #include <cmath>
15 #include <string>
16 #include <vector>
17 
18 #include <boost/ptr_container/ptr_vector.hpp>
19 
27 #include "oops/util/dot_product.h"
28 #include "oops/util/formats.h"
29 #include "oops/util/Logger.h"
30 
31 namespace oops {
32 
33 /// DRPLanczos Minimizer
34 /*!
35  * \brief Derber-Rosati Preconditioned Lanczos solver.
36  *
37  * This solver is the Lanczos version of the DRPCG algorithm
38  * It solves \f$ Ax=b\f$ for the particular case \f$ A=B^{-1}+C\f$,
39  * without requiring the application of \f$ B^{-1}\f$.
40  *
41  * A must be square, symmetric, positive definite.
42  *
43  * A preconditioner must be supplied that, given a vector q, returns an
44  * approximation to \f$ (AB)^{-1} q\f$. Possible preconditioning
45  * is detailed in S. Gurol, PhD Manuscript, 2013.
46  * Note that the traditional \f$ B\f$-preconditioning corresponds to
47  * precond=\f$I\f$.
48  *
49  * On entry:
50  * - dx = starting point.
51  * - dxh = starting point, \f$ B^{-1} dx_{0}\f$.
52  * - rr = residual at starting point.
53  * - B = \f$ B \f$.
54  * - C = \f$ C \f$.
55  * - precond = preconditioner \f$ F_k \approx (AB)^{-1} \f$.
56  *
57  * On exit, dxh will contain \f$ B^{-1} x\f$ where x is the solution.
58  * The return value is the achieved reduction in residual norm.
59  *
60  * Iteration will stop if the maximum iteration limit "maxiter" is reached
61  * or if the residual norm reduces by a factor of "tolerance".
62  *
63  * Each matrix must implement a method:
64  * - void multiply(const VECTOR&, VECTOR&) const
65  *
66  * which applies the matrix to the first argument, and returns the
67  * matrix-vector product in the second. (Note: the const is optional, but
68  * recommended.)
69  */
70 
71 // -----------------------------------------------------------------------------
72 
73 template<typename MODEL> class DRPLanczosMinimizer : public DRMinimizer<MODEL> {
78 
79  public:
80  const std::string classname() const override {return "DRPLanczosMinimizer";}
81  DRPLanczosMinimizer(const eckit::Configuration &, const CostFct_ &);
83 
84  private:
85  double solve(CtrlInc_ &, CtrlInc_ &, CtrlInc_ &, const Bmat_ &, const HtRinvH_ &,
86  const double, const double, const int, const double) override;
87 
89 
90  boost::ptr_vector<CtrlInc_> hvecs_;
91  boost::ptr_vector<CtrlInc_> vvecs_;
92  boost::ptr_vector<CtrlInc_> zvecs_;
93  std::vector<double> alphas_;
94  std::vector<double> betas_;
95 };
96 
97 // =============================================================================
98 
99 template<typename MODEL>
101  const CostFct_ & J)
102  : DRMinimizer<MODEL>(J), lmp_(conf),
103  hvecs_(), vvecs_(), zvecs_(), alphas_(), betas_() {}
104 
105 // -----------------------------------------------------------------------------
106 
107 template<typename MODEL>
109  const Bmat_ & B, const HtRinvH_ & HtRinvH,
110  const double costJ0Jb, const double costJ0JoJc,
111  const int maxiter, const double tolerance) {
112  // dx increment
113  // dxh B^{-1} dx
114  // rr (sum B^{-1} dx_i^{b} +) G^T H^{-1} d
115 
116  CtrlInc_ zz(dxh);
117  CtrlInc_ pr(dxh);
118  CtrlInc_ vv(rr);
119 
120  std::vector<double> ss;
121  std::vector<double> dd;
122 
123  // J0
124  const double costJ0 = costJ0Jb + costJ0JoJc;
125 
126  lmp_.update(vvecs_, hvecs_, zvecs_, alphas_, betas_);
127 
128  // z_{0} = B LMP r_{0}
129  lmp_.multiply(vv, pr);
130  B.multiply(pr, zz);
131 
132  // beta_{0} = sqrt( z_{0}^T r_{0} )
133  double beta = sqrt(dot_product(zz, vv));
134  const double beta0 = beta;
135 
136  // v_{1} = r_{0} / beta_{0}
137  vv *= 1/beta;
138  // pr_{1} = LMP r_{0} / beta_{0}
139  pr *= 1/beta;
140  // z_{1} = z_{0} / beta_{0}
141  zz *= 1/beta;
142 
143  // hvecs[0] = pr_{1} --> required for solution
144  hvecs_.push_back(new CtrlInc_(pr));
145  // zvecs[0] = z_{1} ---> for re-orthogonalization
146  zvecs_.push_back(new CtrlInc_(zz));
147  // vvecs[0] = v_{1} ---> for re-orthogonalization
148  vvecs_.push_back(new CtrlInc_(vv));
149 
150  double normReduction = 1.0;
151 
152  Log::info() << std::endl;
153  for (int jiter = 0; jiter < maxiter; ++jiter) {
154  Log::info() << "DRPLanczos Starting Iteration " << jiter+1 << std::endl;
155 
156  // v_{i+1} = ( pr_{i} + H^T R^{-1} H z_{i} ) - beta * v_{i-1}
157  HtRinvH.multiply(zz, vv);
158  vv += pr;
159  if (jiter > 0) vv.axpy(-beta, vvecs_[jiter-1]);
160 
161  // alpha_{i} = v_{i+1}^T z_{i}
162  double alpha = dot_product(zz, vv);
163 
164  // v_{i+1} = v_{i+1} - alpha_{i} v_{i}
165  vv.axpy(-alpha, vvecs_[jiter]); // vv = vv - alpha * v_j
166 
167  // Re-orthogonalization
168  for (int jj = 0; jj < jiter; ++jj) {
169  double proj = dot_product(vv, zvecs_[jj]);
170  vv.axpy(-proj, vvecs_[jj]);
171  }
172 
173  // z_{i+1} = B LMP v_{i+1}
174  lmp_.multiply(vv, pr);
175  B.multiply(pr, zz);
176 
177  // beta_{i+1} = sqrt( zz_{i+1}^t, vv_{i+1} )
178  beta = sqrt(dot_product(zz, vv));
179 
180  // v_{i+1} = v_{i+1} / beta_{i+1}
181  vv *= 1/beta;
182  // pr_{i+1} = pr_{i+1} / beta_{i+1}
183  pr *= 1/beta;
184  // z_{i+1} = z_{i+1} / beta_{i+1}
185  zz *= 1/beta;
186 
187  // hvecs[i+1] =pr_{i+1}
188  hvecs_.push_back(new CtrlInc_(pr));
189  // zvecs[i+1] = z_{i+1}
190  zvecs_.push_back(new CtrlInc_(zz));
191  // vvecs[i+1] = v_{i+1}
192  vvecs_.push_back(new CtrlInc_(vv));
193 
194  alphas_.push_back(alpha);
195 
196  if (jiter == 0) {
197  ss.push_back(beta0/alpha);
198  dd.push_back(beta0);
199  } else {
200  // Solve the tridiagonal system T_{i} s_{i} = beta0 * e_1
201  dd.push_back(beta0*dot_product(zvecs_[0], vv));
202  TriDiagSolve(alphas_, betas_, dd, ss);
203  }
204 
205  betas_.push_back(beta);
206 
207  // Compute the quadratic cost function
208  // J[du_{i}] = J[0] - 0.5 s_{i}^T Z_{i}^T r_{0}
209  // Jb[du_{i}] = 0.5 s_{i}^T V_{i}^T Z_{i} s_{i}
210  double costJ = costJ0;
211  double costJb = costJ0Jb;
212  for (int jj = 0; jj < jiter+1; ++jj) {
213  costJ -= 0.5 * ss[jj] * dot_product(zvecs_[jj], rr);
214  costJb += 0.5 * ss[jj] * dot_product(vvecs_[jj], zvecs_[jj]) * ss[jj];
215  }
216  double costJoJc = costJ - costJb;
217 
218  // Gradient norm in precond metric --> sqrt(r'z) --> beta * s_{i}
219  double rznorm = beta*std::abs(ss[jiter]);
220  normReduction = rznorm/beta0;
221 
222  Log::info() << "DRPLanczos end of iteration " << jiter+1 << std::endl
223  << " Norm reduction (" << std::setw(2) << jiter+1 << ") = "
224  << util::full_precision(normReduction) << std::endl
225  << " Quadratic cost function: J (" << std::setw(2) << jiter+1 << ") = "
226  << util::full_precision(costJ) << std::endl
227  << " Quadratic cost function: Jb (" << std::setw(2) << jiter+1 << ") = "
228  << util::full_precision(costJb) << std::endl
229  << " Quadratic cost function: JoJc(" << std::setw(2) << jiter+1 << ") = "
230  << util::full_precision(costJoJc) << std::endl << std::endl;
231 
232  if (normReduction < tolerance) {
233  Log::info() << "DRPLanczos: Achieved required reduction in residual norm." << std::endl;
234  break;
235  }
236  }
237 
238  // Calculate the solution (dxh = Binv dx)
239  for (unsigned int jj = 0; jj < ss.size(); ++jj) {
240  dx.axpy(ss[jj], zvecs_[jj]);
241  dxh.axpy(ss[jj], hvecs_[jj]);
242  }
243 
244  return normReduction;
245 }
246 
247 // -----------------------------------------------------------------------------
248 
249 } // namespace oops
250 
251 #endif // OOPS_ASSIMILATION_DRPLANCZOSMINIMIZER_H_
DRPLanczos Minimizer.
const std::string classname() const override
real(double_kind), dimension(numint), parameter pr
Definition: mpp_efp.F90:47
boost::ptr_vector< CtrlInc_ > vvecs_
CostFunction< MODEL > CostFct_
Definition: conf.py:1
std::vector< double > betas_
boost::ptr_vector< CtrlInc_ > hvecs_
DRPLanczosMinimizer(const eckit::Configuration &, const CostFct_ &)
Cost Function.
Definition: CostFunction.h:56
void TriDiagSolve(const std::vector< double > &diag, const std::vector< double > &sub, const std::vector< double > &rhs, std::vector< double > &sol)
Definition: TriDiagSolve.h:18
std::vector< double > alphas_
The solvers represent matrices as objects that implement a "multiply" method.
Definition: SpectralLMP.h:51
The namespace for the main oops code.
void multiply(const CtrlInc_ &dx, CtrlInc_ &dz) const
Definition: HtRinvHMatrix.h:64
boost::ptr_vector< CtrlInc_ > zvecs_
subroutine, public info(self)
SpectralLMP< CtrlInc_ > lmp_
real(fp), parameter, public tolerance
double solve(CtrlInc_ &, CtrlInc_ &, CtrlInc_ &, const Bmat_ &, const HtRinvH_ &, const double, const double, const int, const double) override
The matrix.
Definition: BMatrix.h:27
ControlIncrement< MODEL > CtrlInc_
HtRinvHMatrix< MODEL > HtRinvH_
DR (Derber and Rosati) Minimizers.
Definition: DRMinimizer.h:44
void axpy(const double, const ControlIncrement &)
void multiply(const CtrlInc_ &x, CtrlInc_ &bx) const
Definition: BMatrix.h:33