KASKADE 7 development version
linalg/apcg.hh
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the library KASKADE 7 */
4/* https://www.zib.de/research/projects/kaskade7-finite-element-toolbox */
5/* */
6/* Copyright (C) 2012-2018 Zuse Institute Berlin */
7/* */
8/* KASKADE 7 is distributed under the terms of the ZIB Academic License. */
9/* see $KASKADE/academic.txt */
10/* */
11/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13#ifndef NMIII_APCG_HH
14#define NMIII_APCG_HH
15
16#include <numeric>
17#include <string>
18
19#include <boost/circular_buffer.hpp>
20#include <boost/timer/timer.hpp>
21
22#include "dune/common/timer.hh"
23#include "dune/istl/istlexception.hh"
24#include "dune/istl/operators.hh"
25#include "dune/istl/preconditioners.hh"
26#include "dune/istl/scalarproducts.hh"
27#include "dune/istl/solvers.hh"
28
32#include "utilities/scalar.hh"
33
34namespace Kaskade
35{
45 template <class Domain, class Range>
46 bool requiresInitializedInput(Dune::Preconditioner<Domain,Range> const* p)
47 {
48 if (dynamic_cast<Dune::Richardson<Domain,Range> const*>(p))
49 return false;
50 if (auto sp = dynamic_cast<SymmetricPreconditioner<Domain,Range> const*>(p))
51 return sp->requiresInitializedInput();
52 return true;
53 }
54
55 // ----------------------------------------------------------------------------------------------
56
62 template <class R>
64 {
65 public:
69 typedef R Real;
70
74 virtual void clear() = 0;
75
82
87 virtual void step(Real gamma2) = 0;
88
93 virtual void residual(Real sigma) = 0;
94
99 virtual operator bool() const = 0;
100 };
101
107 template <class R>
109 {
110 public:
114 typedef R Real;
115
120 : n(n_), count(0)
121 {}
122
126 virtual void clear()
127 {
128 count = 0;
129 }
130
135 {
136 return *this;
137 }
138
142 virtual void step(Real )
143 {
144 ++count;
145 }
146
150 virtual void residual(Real )
151 {}
152
157 virtual operator bool() const
158 {
159 return count >= n;
160 }
161
162 private:
163 int n;
164 int count;
165 };
166
185 template <class R>
187 {
188 public:
189 typedef R Real;
190
201
205 virtual void clear();
206
213
222
227 virtual void step(Real gamma2);
228
233 virtual void residual(Real sigma) {}
234
239 virtual operator bool() const;
240
244 Real error() const;
245
249 void setMaxit(int m)
250 {
251 maxit = m;
252 }
253
254 std::vector<Real> const& gamma2() const {return gammas2; }
255
256 private:
257 Real tol;
258 int maxit;
259 // squared gammas
260 std::vector<Real> gammas2;
261 int lookah;
262 };
263
264
279 template<class X, class Xstar>
280 class Pcg : public Dune::InverseOperator<X,Xstar>
281 {
282 public:
284 typedef X domain_type;
286 typedef Xstar range_type;
288 typedef typename X::field_type field_type;
289
294
304 PCGTerminationCriterion<Real>& terminate_, int verbose_=0)
305 : proxyOp(op_,zeroDp), op(op_),
306 proxyPrec(prec_,zeroDp), prec(prec_),
307 terminate(terminate_), verbose(verbose_), requiresInit(requiresInitializedInput(&prec))
308 { }
309
321 Pcg(Dune::LinearOperator<X,Xstar>& op_, Dune::Preconditioner<X,Xstar>& prec_,
322 DualPairing<X,Xstar> const& dp_, PCGTerminationCriterion<Real>& terminate_, int verbose_=0)
323 : proxyOp(op_,dp_), op(proxyOp),
324 proxyPrec(prec_,dp_), prec(proxyPrec),
325 terminate(terminate_), verbose(verbose_), requiresInit(requiresInitializedInput(&prec))
326 { }
327
334 virtual void apply (X& u, Xstar& b, Dune::InverseOperatorResult& res)
335 {
336 res.clear(); // clear solver statistics
337 terminate.clear(); // clear termination criterion
338 Dune::Timer watch; // start a timer
339
340 boost::timer::cpu_timer mvTimer;
341 mvTimer.stop();
342 boost::timer::cpu_timer apTimer;
343 apTimer.stop();
344 boost::timer::cpu_timer prTimer;
345 prTimer.stop();
346
347 Xstar r(b);
348 op.applyscaleadd(-1,u,r); // r = b-Au
349
350 // Keeping rq and q in unique_ptr allows efficient computation of q = rq + beta*q below
351 // by swapping the pointers.
352 // More elegant would be a swap method directly on X, but this is hard to implement
353 // as there is no swap either for boost::fusion vectors nor for Dune::BlockVectors.
354 // Even more elegant would be a loop fusion expression framework for BLAS level 1 operations.
355 std::unique_ptr<X> rq(new X(u)); *rq = 0;
356 prec.apply(*rq,r); // rq = B^{-1} r
357
358 std::unique_ptr<X> q(new X(*rq));
359
360 Xstar Aq(b);
361
362
363 // some local variables
364 field_type alpha,beta,sigma,gamma;
365 sigma = op.dp(*rq,r); // preconditioned residual norm squard
367 field_type const sigma0 = std::abs(sigma);
368
369 // Check for trivial case
370 if (sigma0 == 0)
371 return;
372
373 if (verbose>0)
374 { // printing
375 std::cout << "=== Kaskade7 PCG ===\n";
376 if (verbose>1)
377 std::cout << "Iter precond res. ratio avg. rate step ener \n"
378 << " 0" << std::setw(13) << sigma0 << std::endl;
379 }
380
381 // the PCG loop
382 int i=1;
383 for ( ; true; i++ )
384 {
385 // minimize in given search direction p
386 mvTimer.resume();
387 field_type qAq = op.applyDp(*q,Aq); // compute Aq = A*q and qAq = <q,A*q>
388 if (std::isnan(qAq)) std:: cerr << "qAq is nan\n";
389 mvTimer.stop();
390
391 if (std::abs(qAq) < 1e-28*sigma)
392 break; // oops.
393
394 if (qAq <= 0)
395 throw NonpositiveMatrixException("encountered nonpositive energy product " + std::to_string(qAq)
396 + " vs sigma " + std::to_string(sigma) + " in PCG.",
397 __FILE__,__LINE__);
398
399 alpha = sigma/qAq;
400 apTimer.resume();
401 u.axpy(alpha,*q);
402 apTimer.stop();
403 gamma = sigma*alpha;
404 terminate.step(ScalarTraits<field_type>::real(gamma));
405 resDebug.push_back(std::sqrt(sigma));
406
407 // convergence test
408 if (terminate)
409 break;
410
411 apTimer.resume();
412 r.axpy(-alpha,Aq); // r = r - alpha*A*q
413 apTimer.stop();
414
415 prTimer.resume();
416 if (requiresInit)
417 *rq = 0;
418 field_type sigmaNew = prec.applyDp(*rq,r); // compute rq = B^{-1}r and <rq,r>
419 prTimer.stop();
420
421 // determine new search direction
422 terminate.residual(ScalarTraits<field_type>::real(sigmaNew));
423 if (verbose>1) // print
424 {
425 std::cout << std::setw(6) << i
426 << std::setw(13) << std::scientific << std::setprecision(5) << std::abs(sigmaNew)
427 << std::setw(12) << std::fixed << std::setprecision(3) << std::abs(sigmaNew/sigma)
428 << std::setw(12) << std::fixed << std::setprecision(3) << std::pow(std::abs(sigmaNew)/sigma0,1.0/i)
429 << std::setw(12) << std::scientific << std::setprecision(5) << gamma;
430 if (verbose>2)
431 {
432 Xstar Au = u; Au = 0;
433 op.apply(u,Au);
434 auto e = op.dp(u,Au);
435 Au *= 0.5;
436 Au -= b;
437 std::cout << " " << op.dp(u,Au) << " " << e << " " << u.two_norm();
438 }
439 std::cout << std::endl;
440 }
441
442 // convergence check to prevent division by zero if we obtained an exact solution
443 // (which may happen for low dimensional systems)
444 if (std::abs(sigmaNew) < 1e-28*sigma0)
445 break;
446
447 beta = sigmaNew/sigma;
448 sigma = sigmaNew;
449 apTimer.resume();
450 rq->axpy(beta,*q); // compute q = rq + beta*q
451 std::swap(rq,q);
452 apTimer.stop();
453 }
454
455 if (verbose>0) // printing for non verbose
456 std::cout << std::setw(6) << i << std::setw(12) << std::abs(sigma) << std::endl;
457
458 res.iterations = i; // fill statistics
459 res.reduction = std::sqrt(std::abs(sigma)/sigma0);
460 res.conv_rate = pow(res.reduction,1.0/i);
461 res.elapsed = watch.elapsed();
462
463 if (verbose>0) // final print
464 {
465 std::cout << "=== rate=" << res.conv_rate
466 << ", T=" << res.elapsed
467 << ", TIT=" << res.elapsed/i
468 << ", IT=" << i << std::endl;
469
470 std::cout << "Matrix-Vector time: " << mvTimer.format()
471 << "vector ops : " << apTimer.format()
472 << "preconditioner : " << prTimer.format() << '\n';
473 }
474 }
475
481 virtual void apply (X& x, Xstar& b, double tol, Dune::InverseOperatorResult& res)
482 {
483 terminate.tolerance(tol);
484 (*this).apply(x,b,res);
485 }
486
493 virtual Dune::SolverCategory::Category category() const override
494 {
495 return Dune::SolverCategory::sequential;
496 }
497
498 private:
499 // The solver may be constructed with either symmetric operator/preconditioner interface provided,
500 // or with Dune interfaces provided. The implementation relies on the symmetric interface
501 // in order to exploit performance gains with simultaneous evaluation of matrix-vector products
502 // and dual pairings.
503 // Therefore we hold "working" references to symmetric operator/preconditioners. Those reference
504 // either the provided ones, or wrapper objects relaying the evaluation to provided Dune interfaces.
505 // For the second case, we hold the (small) wrapper objects ourselves. In the first case, those
506 // wrapper objects have to be initialized correctly (even though they are never accessed). For this
507 // we need a dual pairing, hence the dummy zeroDp.
508 ZeroDualPairing<X,Xstar> zeroDp; // just a dummy
509
510 SymmetricLinearOperatorWrapper<X,Xstar> proxyOp; // proxy in case a Dune::LinearOperator is provided
511 SymmetricLinearOperator<X,Xstar>& op; // working reference to operator
512
513 SymmetricPreconditionerWrapper<X,Xstar> proxyPrec; // proxy in case a Dune::Preconditioner is provided
514 SymmetricPreconditioner<X,Xstar>& prec; // working reference to preconditioner
515
517 int verbose;
518 bool requiresInit;
519
520 std::vector<field_type> resDebug;
521 };
522} // namespace Kaskade
523#endif
Abstract base class for dual pairing of and its dual space .
To be raised if the matrix is not positive definite.
PCG termination after a given number of iterations.
Definition: linalg/apcg.hh:109
virtual void step(Real)
supplies energy of step to the termination criterion
Definition: linalg/apcg.hh:142
virtual PCGTerminationCriterion< R > & tolerance(Real)
unused
Definition: linalg/apcg.hh:134
PCGCountingTerminationCriterion(int n_)
Constructor.
Definition: linalg/apcg.hh:119
virtual void clear()
re-initializes the termination criterion for a new IterateType::CG run
Definition: linalg/apcg.hh:126
TerminationCriterion based on an absolute energy error estimate.
Definition: linalg/apcg.hh:187
virtual void clear()
re-initializes the termination criterion for a new IterateType::CG run
virtual void residual(Real sigma)
supplies the preconditioned residual to the termination criterion
Definition: linalg/apcg.hh:233
virtual void step(Real gamma2)
supplies energy of step to the termination criterion
PCGEnergyErrorTerminationCriterion(Real atol, int maxit)
constructor
Real error() const
returns the estimated absolute energy error
PCGEnergyErrorTerminationCriterion< Real > & lookahead(int lah)
set requested lookahead value
void setMaxit(int m)
Sets a new value for the maximal number of iterations.
Definition: linalg/apcg.hh:249
std::vector< Real > const & gamma2() const
Definition: linalg/apcg.hh:254
virtual PCGEnergyErrorTerminationCriterion< Real > & tolerance(Real atol)
set requested absolute tolerance
Interface for IterateType::PCG termination criterion policy classes.
Definition: linalg/apcg.hh:64
virtual void clear()=0
re-initializes the termination criterion for a new IterateType::CG run
virtual void residual(Real sigma)=0
supplies the preconditioned residual to the termination criterion
virtual PCGTerminationCriterion< R > & tolerance(Real tol)=0
set requested tolerance
virtual void step(Real gamma2)=0
supplies energy of step to the termination criterion
preconditioned conjugate gradient method
Definition: linalg/apcg.hh:281
ScalarTraits< field_type >::Real Real
the real field type corresponding to field_type
Definition: linalg/apcg.hh:293
virtual void apply(X &x, Xstar &b, double tol, Dune::InverseOperatorResult &res)
Apply inverse operator with given tolerance.
Definition: linalg/apcg.hh:481
Xstar range_type
The range type of the operator to be inverted.
Definition: linalg/apcg.hh:286
virtual void apply(X &u, Xstar &b, Dune::InverseOperatorResult &res)
Apply inverse operator by performing a number of IterateType::PCG iterations.
Definition: linalg/apcg.hh:334
virtual Dune::SolverCategory::Category category() const override
returns the category of the operator
Definition: linalg/apcg.hh:493
X::field_type field_type
The field type of the operator to be inverted.
Definition: linalg/apcg.hh:288
Pcg(Dune::LinearOperator< X, Xstar > &op_, Dune::Preconditioner< X, Xstar > &prec_, DualPairing< X, Xstar > const &dp_, PCGTerminationCriterion< Real > &terminate_, int verbose_=0)
Set up conjugate gradient solver.
Definition: linalg/apcg.hh:321
X domain_type
The domain type of the operator to be inverted.
Definition: linalg/apcg.hh:284
Pcg(SymmetricLinearOperator< X, Xstar > &op_, SymmetricPreconditioner< X, Xstar > &prec_, PCGTerminationCriterion< Real > &terminate_, int verbose_=0)
Set up conjugate gradient solver.
Definition: linalg/apcg.hh:303
Helper class for working with (real or complex) scalar field types.
Definition: scalar.hh:36
Scalar Real
The real type on which the scalar field is based.
Definition: scalar.hh:49
Interface for symmetric linear operators.
Wrapper class to present (hopefully) symmetric linear operators in the SymmetricLinearOperator interf...
Wrapper class presenting a symmetric preconditioner interface for any preconditioner.
Zero dual pairing implementation (mainly useful whenever a dual pairing is needed for formal language...
bool requiresInitializedInput(Dune::Preconditioner< Domain, Range > const *p)
Whether a preconditioner needs zero initialized result vector or not.
Definition: linalg/apcg.hh:46