Skip to content
Snippets Groups Projects
Commit 1f4cd6b4 authored by Marian Piatkowski's avatar Marian Piatkowski Committed by Steffen Müthing
Browse files

fixed GMRes

removed bool recalc_defect
reimplement the update function with respect to optimality
fixed strange behaviour when maxiter is reached and GMRes wants to restart infinitely many times
fixed strange behaviour when linear reduction is reached but GMRes wants to restart and continue calculation
fixed the printing of the results
parent 84f70788
No related branches found
No related tags found
No related merge requests found
...@@ -1046,11 +1046,9 @@ namespace Dune { ...@@ -1046,11 +1046,9 @@ namespace Dune {
Generalized Minimal Residual method as described the SIAM Templates Generalized Minimal Residual method as described the SIAM Templates
book (http://www.netlib.org/templates/templates.pdf). book (http://www.netlib.org/templates/templates.pdf).
\todo construct F via rebind and an appropriate field_type
*/ */
template<class X, class Y=X, class F = Y> template<class X, class Y=X>
class RestartedGMResSolver : public InverseOperator<X,Y> class RestartedGMResSolver : public InverseOperator<X,Y>
{ {
public: public:
...@@ -1060,29 +1058,23 @@ namespace Dune { ...@@ -1060,29 +1058,23 @@ namespace Dune {
typedef Y range_type; typedef Y range_type;
//! \brief The field type of the operator to be inverted //! \brief The field type of the operator to be inverted
typedef typename X::field_type field_type; typedef typename X::field_type field_type;
//! \brief The real type of the field type (is the same of using real numbers, but differs for std::complex)
typedef typename FieldTraits<field_type>::real_type real_type;
//! \brief The field type of the basis vectors
typedef F basis_type;
/*! /*!
\brief Set up solver. \brief Set up solver.
\copydoc LoopSolver::LoopSolver(L&,P&,double,int,int) \copydoc LoopSolver::LoopSolver(L&,P&,double,int,int)
\param restart number of GMRes cycles before restart \param restart number of GMRes cycles before restart
\param recalc_defect recalculate the defect after everey restart or not [default=false]
*/ */
template<class L, class P> template<class L, class P>
RestartedGMResSolver (L& op, P& prec, double reduction, int restart, int maxit, int verbose, bool recalc_defect = false) : RestartedGMResSolver (L& op, P& prec, double reduction, int restart, int maxit, int verbose) :
_A_(op), _M(prec), _A(op), _W(prec),
ssp(), _sp(ssp), _restart(restart), ssp(), _sp(ssp), _restart(restart),
_reduction(reduction), _maxit(maxit), _verbose(verbose), _reduction(reduction), _maxit(maxit), _verbose(verbose)
_recalc_defect(recalc_defect)
{ {
dune_static_assert(static_cast<int>(P::category) == static_cast<int>(L::category), dune_static_assert(static_cast<int>(P::category) == static_cast<int>(L::category),
"P and L must be the same category!"); "P and L must be the same category!");
dune_static_assert( static_cast<int>(L::category) == static_cast<int>(SolverCategory::sequential), dune_static_assert(static_cast<int>(L::category) == static_cast<int>(SolverCategory::sequential),
"L must be sequential!"); "L must be sequential!");
} }
/*! /*!
...@@ -1090,14 +1082,12 @@ namespace Dune { ...@@ -1090,14 +1082,12 @@ namespace Dune {
\copydoc LoopSolver::LoopSolver(L&,S&,P&,double,int,int) \copydoc LoopSolver::LoopSolver(L&,S&,P&,double,int,int)
\param restart number of GMRes cycles before restart \param restart number of GMRes cycles before restart
\param recalc_defect recalculate the defect after everey restart or not [default=false]
*/ */
template<class L, class S, class P> template<class L, class S, class P>
RestartedGMResSolver (L& op, S& sp, P& prec, double reduction, int restart, int maxit, int verbose, bool recalc_defect = false) : RestartedGMResSolver (L& op, S& sp, P& prec, double reduction, int restart, int maxit, int verbose) :
_A_(op), _M(prec), _A(op), _W(prec),
_sp(sp), _restart(restart), _sp(sp), _restart(restart),
_reduction(reduction), _maxit(maxit), _verbose(verbose), _reduction(reduction), _maxit(maxit), _verbose(verbose)
_recalc_defect(recalc_defect)
{ {
dune_static_assert(static_cast<int>(P::category) == static_cast<int>(L::category), dune_static_assert(static_cast<int>(P::category) == static_cast<int>(L::category),
"P and L must have the same category!"); "P and L must have the same category!");
...@@ -1118,218 +1108,189 @@ namespace Dune { ...@@ -1118,218 +1108,189 @@ namespace Dune {
*/ */
virtual void apply (X& x, Y& b, double reduction, InverseOperatorResult& res) virtual void apply (X& x, Y& b, double reduction, InverseOperatorResult& res)
{ {
int m = _restart; const double EPSILON = 1e-80;
real_type norm; const int m = _restart;
real_type norm_old = 0.0; field_type norm, norm_old = 0.0, norm_0;
real_type norm_0; int j = 1;
real_type beta;
int i, j = 1, k;
std::vector<field_type> s(m+1), cs(m), sn(m); std::vector<field_type> s(m+1), cs(m), sn(m);
// need copy of rhs if GMRes has to be restarted
Y b2(b);
// helper vector // helper vector
X w(b); X w(b);
std::vector< std::vector<field_type> > H(m+1,s); std::vector< std::vector<field_type> > H(m+1,s);
std::vector<F> v(m+1,b); std::vector<X> v(m+1,b);
// start timer // start timer
Timer watch; // start a timer Dune::Timer watch;
watch.reset();
// clear solver statistics // clear solver statistics and set res.converged to false
res.clear(); res.clear();
_M.pre(x,b); _W.pre(x,b);
if (_recalc_defect)
{
// norm_0 = norm(M^-1 b)
w = 0.0; _M.apply(w,b); // w = M^-1 b
norm_0 = _sp.norm(w); // use defect of preconditioned residual
// r = _M.solve(b - A * x);
w = b;
_A_.applyscaleadd(-1,x, /* => */ w); // w = b - Ax;
v[0] = 0.0; _M.apply(v[0],w); // r = M^-1 w
beta = _sp.norm(v[0]);
}
else
{
// norm_0 = norm(b-Ax)
_A_.applyscaleadd(-1,x, /* => */ b); // b = b - Ax;
v[0] = 0.0; _M.apply(v[0],b); // r = M^-1 b
beta = _sp.norm(v[0]);
norm_0 = beta; // use defect of preconditioned residual
}
// avoid division by zero // calculate defect and overwrite rhs with it
if (norm_0 == 0.0) _A.applyscaleadd(-1.0,x,b); // b -= Ax
norm_0 = 1.0; // calculate preconditioned defect
norm = norm_old = beta; v[0] = 0.0; _W.apply(v[0],b); // r = W^-1 b
norm_0 = _sp.norm(v[0]);
norm = norm_0;
norm_old = norm;
// print header // print header
if (_verbose > 0) if(_verbose > 0)
{
std::cout << "=== RestartedGMResSolver" << std::endl;
if (_verbose > 1)
{ {
this->printHeader(std::cout); std::cout << "=== RestartedGMResSolver" << std::endl;
this->printOutput(std::cout,0,norm_0); if(_verbose > 1) {
this->printOutput(std::cout,0,norm, norm_0); this->printHeader(std::cout);
this->printOutput(std::cout,0,norm_0);
}
} }
}
// check convergence if(norm_0 < EPSILON) {
if (norm <= reduction * norm_0) { _W.post(x);
_M.post(x); // postprocess preconditioner res.converged = true;
res.converged = true; if(_verbose > 0) // final print
if (_verbose > 0) // final print
print_result(res); print_result(res);
return;
} }
while (j <= _maxit && res.converged != true) { while(j <= _maxit && res.converged != true) {
v[0] *= (1.0 / beta);
for (i=1; i<=m; i++) s[i] = 0.0;
s[0] = beta;
for (i = 0; i < m && j <= _maxit && res.converged != true; i++, j++) { int i = 0;
v[0] *= 1.0/norm;
s[0] = norm;
for(i=1; i<m+1; i++)
s[i] = 0.0;
for(i=0; i < m && j <= _maxit && res.converged != true; i++, j++) {
w = 0.0; w = 0.0;
v[i+1] = 0.0; // use v[i+1] as temporary vector // use v[i+1] as temporary vector
_A_.apply(v[i], /* => */ v[i+1]); v[i+1] = 0.0;
_M.apply(w, v[i+1]); // do Arnoldi algorithm
for (k = 0; k <= i; k++) { _A.apply(v[i],v[i+1]);
H[k][i] = _sp.dot(w, v[k]); _W.apply(w,v[i+1]);
// w -= H[k][i] * v[k]; for(int k=0; k<i+1; k++) {
w.axpy(-H[k][i], v[k]); H[k][i] = _sp.dot(w,v[k]);
// w -= H[k][i] * v[k]
w.axpy(-H[k][i],v[k]);
} }
H[i+1][i] = _sp.norm(w); H[i+1][i] = _sp.norm(w);
if (H[i+1][i] == 0.0) if(std::abs(H[i+1][i]) < EPSILON)
DUNE_THROW(ISTLError,"breakdown in GMRes - |w| " DUNE_THROW(ISTLError,
<< " == 0.0 after " << j << " iterations"); "breakdown in GMRes - |w| == 0.0 after " << j << " iterations");
// v[i+1] = w * (1.0 / H[i+1][i]);
v[i+1] = w; v[i+1] *= (1.0 / H[i+1][i]); // normalize new vector
v[i+1] = w; v[i+1] *= 1.0/H[i+1][i];
for (k = 0; k < i; k++) // update QR factorization
applyPlaneRotation(H[k][i], H[k+1][i], cs[k], sn[k]); for(int k=0; k<i; k++)
applyPlaneRotation(H[k][i],H[k+1][i],cs[k],sn[k]);
generatePlaneRotation(H[i][i], H[i+1][i], cs[i], sn[i]); // compute new givens rotation
applyPlaneRotation(H[i][i], H[i+1][i], cs[i], sn[i]); generatePlaneRotation(H[i][i],H[i+1][i],cs[i],sn[i]);
applyPlaneRotation(s[i], s[i+1], cs[i], sn[i]); // finish updating QR factorization
applyPlaneRotation(H[i][i],H[i+1][i],cs[i],sn[i]);
applyPlaneRotation(s[i],s[i+1],cs[i],sn[i]);
// norm of the defect is the last component the vector s
norm = std::abs(s[i+1]); norm = std::abs(s[i+1]);
if (_verbose > 1) // print // print current iteration statistics
{ if(_verbose > 1) {
this->printOutput(std::cout,j,norm,norm_old); this->printOutput(std::cout,j,norm,norm_old);
} }
norm_old = norm; norm_old = norm;
if (norm < reduction * norm_0) { // check convergence
if(norm < reduction * norm_0)
res.converged = true; res.converged = true;
}
}
if (_recalc_defect) } // end for
{
// update x // calculate update vector
update(x, i - 1, H, s, v); w = 0.0;
update(w,i,H,s,v);
// update residuum // and current iterate
// r = M^-1 (b - A * x); x += w;
w = b; _A_.applyscaleadd(-1,x, /* => */ w);
_M.apply(v[0], w); // restart GMRes if convergence was not achieved,
beta = _sp.norm(v[0]); // i.e. linear defect has not reached desired reduction
norm = beta; // and if j < _maxit
} if( res.converged != true && j <= _maxit ) {
else
{ if(_verbose > 0)
// calc update vector std::cout << "=== GMRes::restart" << std::endl;
w = 0; // get saved rhs
update(w, i - 1, H, s, v); b = b2;
// calculate new defect
// update x _A.applyscaleadd(-1.0,x,b); // b -= Ax;
x += w; // calculate preconditioned defect
v[0] = 0.0;
// r = M^-1 (b - A * x); _W.apply(v[0],b);
// update defect norm = _sp.norm(v[0]);
_A_.applyscaleadd(-1,w, /* => */ b); norm_old = norm;
// r = M^-1 (b - A * x);
v[0] = 0.0; _M.apply(v[0],b); // r = M^-1 b
beta = _sp.norm(v[0]);
norm = beta;
res.converged = false;
}
//correct i which is wrong if convergence was not achieved.
j=std::min(_maxit,j);
if (_verbose > 1) // print
{
this->printOutput(std::cout,j,norm,norm_old);
} }
norm_old = norm; } //end while
if (norm < reduction * norm_0) {
// fill statistics
res.converged = true;
}
if (res.converged != true && _verbose > 0) // postprocess preconditioner
std::cout << "=== GMRes::restart\n"; _W.post(x);
}
_M.post(x); // postprocess preconditioner
res.iterations = j; // save solver statistics
res.reduction = norm / norm_0; res.iterations = j-1; // it has to be j-1!!!
res.conv_rate = pow(res.reduction,1.0/j); res.reduction = norm/norm_0;
res.conv_rate = pow(res.reduction,1.0/(j-1));
res.elapsed = watch.elapsed(); res.elapsed = watch.elapsed();
if (_verbose>0) if(_verbose>0)
print_result(res); print_result(res);
} }
private:
void private :
print_result (const InverseOperatorResult & res) const
{ void print_result(const InverseOperatorResult& res) const {
int j = res.iterations>0 ? res.iterations : 1; int k = res.iterations>0 ? res.iterations : 1;
std::cout << "=== rate=" << res.conv_rate std::cout << "=== rate=" << res.conv_rate
<< ", T=" << res.elapsed << ", T=" << res.elapsed
<< ", TIT=" << res.elapsed/j << ", TIT=" << res.elapsed/k
<< ", IT=" << res.iterations << ", IT=" << res.iterations
<< std::endl; << std::endl;
} }
static void void update(X& w, int i,
update(X &x, int k, std::vector<std::vector<field_type> >& H,
std::vector< std::vector<field_type> > & h, std::vector<field_type>& s,
std::vector<field_type> & s, std::vector<F> v) std::vector<X>& v) {
{ // solution vector of the upper triangular system
std::vector<field_type> y(s); std::vector<field_type> y(s);
// Backsolve: // backsolve
for (int i = k; i >= 0; i--) { for(int a=i-1; a>=0; a--) {
y[i] /= h[i][i]; field_type rhs(s[a]);
for (int j = i - 1; j >= 0; j--) for(int b=a+1; b<i; b++)
y[j] -= h[j][i] * y[i]; rhs -= H[a][b]*y[b];
} y[a] = rhs/H[a][a];
for (int j = 0; j <= k; j++) // compute update on the fly
// x += v[j] * y[j]; // w += y[a]*v[a]
x.axpy(y[j],v[j]); w.axpy(y[a],v[a]);
}
} }
void void
generatePlaneRotation(field_type &dx, field_type &dy, field_type &cs, field_type &sn) generatePlaneRotation(field_type &dx, field_type &dy, field_type &cs, field_type &sn)
{ {
if (dy == 0.0) { field_type temp = std::abs(dy);
if (std::abs(dy) < 1e-15 ) {
cs = 1.0; cs = 1.0;
sn = 0.0; sn = 0.0;
} else if (std::abs(dy) > std::abs(dx)) { } else if (temp > std::abs(dx)) {
field_type temp = dx / dy; temp = dx / dy;
sn = 1.0 / std::sqrt( 1.0 + temp*temp ); sn = 1.0 / std::sqrt( 1.0 + temp*temp );
cs = temp * sn; cs = temp * sn;
} else { } else {
field_type temp = dy / dx; temp = dy / dx;
cs = 1.0 / std::sqrt( 1.0 + temp*temp ); cs = 1.0 / std::sqrt( 1.0 + temp*temp );
sn = temp * cs; sn = temp * cs;
} }
...@@ -1344,15 +1305,14 @@ namespace Dune { ...@@ -1344,15 +1305,14 @@ namespace Dune {
dx = temp; dx = temp;
} }
LinearOperator<X,X>& _A_; LinearOperator<X,X>& _A;
Preconditioner<X,X>& _M; Preconditioner<X,X>& _W;
SeqScalarProduct<X> ssp; SeqScalarProduct<X> ssp;
ScalarProduct<X>& _sp; ScalarProduct<X>& _sp;
int _restart; int _restart;
double _reduction; double _reduction;
int _maxit; int _maxit;
int _verbose; int _verbose;
bool _recalc_defect;
}; };
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment