From 9641b514b14b7f4aa31cc471e1e0607ac5091372 Mon Sep 17 00:00:00 2001
From: Marian Piatkowski <marian.piatkowski@iwr.uni-heidelberg.de>
Date: Sat, 7 Jun 2014 15:22:55 +0200
Subject: [PATCH] [Bugfix] fixed order of the scalar product when applying the
 Arnoldi algorithm in GMRes solver, which is important in the complex case

minor cleanups in the MinRes solver
---
 dune/istl/solvers.hh | 51 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 41 insertions(+), 10 deletions(-)

diff --git a/dune/istl/solvers.hh b/dune/istl/solvers.hh
index c89036996..8926ae294 100644
--- a/dune/istl/solvers.hh
+++ b/dune/istl/solvers.hh
@@ -904,7 +904,9 @@ namespace Dune {
       z = 0.0;
       _prec.apply(z,b);
 
-      beta = std::sqrt(std::abs(_sp.dot(z,b)));
+      // beta is real and positive in exact arithmetic
+      // since it is the norm of the basis vectors (in unpreconditioned case)
+      beta = std::sqrt(_sp.dot(b,z));
       field_type beta0 = beta;
 
       // the search directions
@@ -933,13 +935,18 @@ namespace Dune {
         // symmetrically preconditioned Lanczos algorithm (see Greenbaum p.121)
         _op.apply(z,q[i2]); // q[i2] = Az
         q[i2].axpy(-beta,q[i0]);
-        alpha = _sp.dot(q[i2],z);
+        // alpha is real since it is the diagonal entry of the hermitian tridiagonal matrix
+        // from the Lanczos Algorithm
+        // so the order in the scalar product doesn't matter even for the complex case
+        alpha = _sp.dot(z,q[i2]);
         q[i2].axpy(-alpha,q[i1]);
 
         z = 0.0;
         _prec.apply(z,q[i2]);
 
-        beta = std::sqrt(std::abs(_sp.dot(q[i2],z)));
+        // beta is real and positive in exact arithmetic
+        // since it is the norm of the basis vectors (in unpreconditioned case)
+        beta = std::sqrt(_sp.dot(q[i2],z));
 
         q[i2] *= 1.0/beta;
         z *= 1.0/beta;
@@ -1026,7 +1033,17 @@ namespace Dune {
 
   private:
 
-    void generateGivensRotation(field_type& dx, field_type& dy, real_type& cs, field_type& sn)
+    template<typename T>
+    typename enable_if<is_same<field_type,real_type>::value,T>::type conjugate(const T& t) {
+      return t;
+    }
+
+    template<typename T>
+    typename enable_if<!is_same<field_type,real_type>::value,T>::type conjugate(const T& t) {
+      return conj(t);
+    }
+
+    void generateGivensRotation(field_type &dx, field_type &dy, real_type &cs, field_type &sn)
     {
       real_type norm_dx = std::abs(dx);
       real_type norm_dy = std::abs(dy);
@@ -1042,12 +1059,12 @@ namespace Dune {
         sn = cs;
         cs *= temp;
         sn *= dx/norm_dx;
-        sn *= std::conj(dy)/norm_dy;
+        sn *= conjugate(dy)/norm_dy;
       } else {
         real_type temp = norm_dy/norm_dx;
         cs = 1.0/std::sqrt(1.0 + temp*temp);
         sn = cs;
-        sn *= std::conj(dy/dx);
+        sn *= conjugate(dy/dx);
       }
     }
 
@@ -1235,7 +1252,11 @@ namespace Dune {
           _A.apply(v[i],v[i+1]);
           _W.apply(w,v[i+1]);
           for(int k=0; k<i+1; k++) {
-            H[k][i] = _sp.dot(w,v[k]);
+            // notice that _sp.dot(v[k],w) = v[k]\adjoint w
+            // so one has to pay attention to the order
+            // the in scalar product for the complex case
+            // doing the modified Gram-Schmidt algorithm
+            H[k][i] = _sp.dot(v[k],w);
             // w -= H[k][i] * v[k]
             w.axpy(-H[k][i],v[k]);
           }
@@ -1344,6 +1365,16 @@ namespace Dune {
       }
     }
 
+    template<typename T>
+    typename enable_if<is_same<field_type,real_type>::value,T>::type conjugate(const T& t) {
+      return t;
+    }
+
+    template<typename T>
+    typename enable_if<!is_same<field_type,real_type>::value,T>::type conjugate(const T& t) {
+      return conj(t);
+    }
+
     void
     generatePlaneRotation(field_type &dx, field_type &dy, real_type &cs, field_type &sn)
     {
@@ -1361,12 +1392,12 @@ namespace Dune {
         sn = cs;
         cs *= temp;
         sn *= dx/norm_dx;
-        sn *= std::conj(dy)/norm_dy;
+        sn *= conjugate(dy)/norm_dy;
       } else {
         real_type temp = norm_dy/norm_dx;
         cs = 1.0/std::sqrt(1.0 + temp*temp);
         sn = cs;
-        sn *= std::conj(dy/dx);
+        sn *= conjugate(dy/dx);
       }
     }
 
@@ -1375,7 +1406,7 @@ namespace Dune {
     applyPlaneRotation(field_type &dx, field_type &dy, real_type &cs, field_type &sn)
     {
       field_type temp  =  cs * dx + sn * dy;
-      dy = -std::conj(sn) * dx + cs * dy;
+      dy = -conjugate(sn) * dx + cs * dy;
       dx = temp;
     }
 
-- 
GitLab