From 2adc07a3e1ba8d4346557f8761901835e82715ac Mon Sep 17 00:00:00 2001
From: Jesper Andersson <jesper@fmrib.ox.ac.uk>
Date: Mon, 4 Jun 2007 13:02:33 +0000
Subject: [PATCH] Equation solvers from IML library

---
 bicg.h     |  93 +++++++++++++++++++++++++++++
 bicgstab.h |  99 +++++++++++++++++++++++++++++++
 cg.h       |  85 +++++++++++++++++++++++++++
 cgs.h      |  90 +++++++++++++++++++++++++++++
 cheby.h    |  88 ++++++++++++++++++++++++++++
 ir.h       |  69 ++++++++++++++++++++++
 qmr.h      | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 691 insertions(+)
 create mode 100644 bicg.h
 create mode 100644 bicgstab.h
 create mode 100644 cg.h
 create mode 100644 cgs.h
 create mode 100644 cheby.h
 create mode 100644 ir.h
 create mode 100644 qmr.h

diff --git a/bicg.h b/bicg.h
new file mode 100644
index 0000000..10f2d37
--- /dev/null
+++ b/bicg.h
@@ -0,0 +1,93 @@
+//*****************************************************************
+// Iterative template routine -- BiCG
+//
+// BiCG solves the unsymmetric linear system Ax = b 
+// using the Preconditioned BiConjugate Gradient method
+//
+// BiCG follows the algorithm described on p. 22 of the 
+// SIAM Templates book.
+//
+// The return value indicates convergence within max_iter (input)
+// iterations (0), or no convergence within max_iter iterations (1).
+//
+// Upon successful return, output arguments have the following values:
+//  
+//        x  --  approximate solution to Ax = b
+// max_iter  --  the number of iterations performed before the
+//               tolerance was reached
+//      tol  --  the residual after the final iteration
+//  
+//*****************************************************************
+//
+// Slightly modified version of IML++ template. See ReadMe file.
+//
+// Jesper Andersson
+//
+
+#ifndef bicg_h
+#define bicg_h
+
+namespace MISCMATHS {
+
+
+template < class Matrix, class Vector, class Preconditioner, class Real >
+int 
+BiCG(const Matrix &A, Vector &x, const Vector &b,
+     const Preconditioner &M, int &max_iter, Real &tol)
+{
+  Real resid;
+  Vector rho_1(1), rho_2(1), alpha(1), beta(1);
+  Vector z, ztilde, p, ptilde, q, qtilde;
+
+  Real normb = b.NormFrobenius();
+  Vector r = b - A * x;
+  Vector rtilde = r;
+
+  if (normb == 0.0)
+    normb = 1;
+  
+  if ((resid = r.NormFrobenius() / normb) <= tol) {
+    tol = resid;
+    max_iter = 0;
+    return 0;
+  }
+
+  for (int i = 1; i <= max_iter; i++) {
+    z = M.solve(r);
+    ztilde = M.trans_solve(rtilde);
+    rho_1(1) = DotProduct(z, rtilde);
+    if (rho_1(1) == 0) { 
+      tol = r.NormFrobenius() / normb;
+      max_iter = i;
+      return 2;
+    }
+    if (i == 1) {
+      p = z;
+      ptilde = ztilde;
+    } else {
+      beta(1) = rho_1(1) / rho_2(1);
+      p = z + beta(1) * p;
+      ptilde = ztilde + beta(1) * ptilde;
+    }
+    q = A * p;
+    qtilde = A.trans_mult(ptilde);
+    alpha(1) = rho_1(1) / DotProduct(ptilde, q);
+    x += alpha(1) * p;
+    r -= alpha(1) * q;
+    rtilde -= alpha(1) * qtilde;
+
+    rho_2(1) = rho_1(1);
+    if ((resid = r.NormFrobenius() / normb) < tol) {
+      tol = resid;
+      max_iter = i;
+      return 0;
+    }
+  }
+
+  tol = resid;
+  return 1;
+}
+  
+} // End namespace MISCMATHS
+
+#endif // End #ifndef cg_h
diff --git a/bicgstab.h b/bicgstab.h
new file mode 100644
index 0000000..fbaac51
--- /dev/null
+++ b/bicgstab.h
@@ -0,0 +1,99 @@
+//*****************************************************************
+// Iterative template routine -- BiCGSTAB
+//
+// BiCGSTAB solves the unsymmetric linear system Ax = b 
+// using the Preconditioned BiConjugate Gradient Stabilized method
+//
+// BiCGSTAB follows the algorithm described on p. 27 of the 
+// SIAM Templates book.
+//
+// The return value indicates convergence within max_iter (input)
+// iterations (0), or no convergence within max_iter iterations (1).
+//
+// Upon successful return, output arguments have the following values:
+//  
+//        x  --  approximate solution to Ax = b
+// max_iter  --  the number of iterations performed before the
+//               tolerance was reached
+//      tol  --  the residual after the final iteration
+//  
+//*****************************************************************
+//
+// Slightly modified version of IML++ template. See ReadMe file.
+//
+// Jesper Andersson
+//
+
+#ifndef bicgstab_h
+#define bicgstab_h
+
+namespace MISCMATHS {
+
+template < class Matrix, class Vector, class Preconditioner, class Real >
+int 
+BiCGSTAB(const Matrix &A, Vector &x, const Vector &b,
+         const Preconditioner &M, int &max_iter, Real &tol)
+{
+  Real resid;
+  Vector rho_1(1), rho_2(1), alpha(1), beta(1), omega(1);
+  Vector p, phat, s, shat, t, v;
+
+  Real normb = b.NormFrobenius();
+  Vector r = b - A * x;
+  Vector rtilde = r;
+
+  if (normb == 0.0)
+    normb = 1;
+  
+  if ((resid = r.NormFrobenius() / normb) <= tol) {
+    tol = resid;
+    max_iter = 0;
+    return 0;
+  }
+
+  for (int i = 1; i <= max_iter; i++) {
+    rho_1(1) = DotProduct(rtilde, r);
+    if (rho_1(1) == 0) {
+      tol = r.NormFrobenius() / normb;
+      return 2;
+    }
+    if (i == 1)
+      p = r;
+    else {
+      beta(1) = (rho_1(1)/rho_2(1)) * (alpha(1)/omega(1));
+      p = r + beta(1) * (p - omega(1) * v);
+    }
+    phat = M.solve(p);
+    v = A * phat;
+    alpha(1) = rho_1(1) / DotProduct(rtilde, v);
+    s = r - alpha(1) * v;
+    if ((resid = s.NormFrobenius()/normb) < tol) {
+      x += alpha(1) * phat;
+      tol = resid;
+      return 0;
+    }
+    shat = M.solve(s);
+    t = A * shat;
+    omega = DotProduct(t,s) / DotProduct(t,t);
+    x += alpha(1) * phat + omega(1) * shat;
+    r = s - omega(1) * t;
+
+    rho_2(1) = rho_1(1);
+    if ((resid = r.NormFrobenius() / normb) < tol) {
+      tol = resid;
+      max_iter = i;
+      return 0;
+    }
+    if (omega(1) == 0) {
+      tol = r.NormFrobenius() / normb;
+      return 3;
+    }
+  }
+
+  tol = resid;
+  return 1;
+}
+
+} // End namespace MISCMATHS
+
+#endif // End #ifndef bicgstab_h
diff --git a/cg.h b/cg.h
new file mode 100644
index 0000000..42094e1
--- /dev/null
+++ b/cg.h
@@ -0,0 +1,85 @@
+//*****************************************************************
+// Iterative template routine -- CG
+//
+// CG solves the symmetric positive definite linear
+// system Ax=b using the Conjugate Gradient method.
+//
+// CG follows the algorithm described on p. 15 in the 
+// SIAM Templates book.
+//
+// The return value indicates convergence within max_iter (input)
+// iterations (0), or no convergence within max_iter iterations (1).
+//
+// Upon successful return, output arguments have the following values:
+//  
+//        x  --  approximate solution to Ax = b
+// max_iter  --  the number of iterations performed before the
+//               tolerance was reached
+//      tol  --  the residual after the final iteration
+//  
+//*****************************************************************
+//
+// Slightly modified version of IML++ template. See ReadMe file.
+//
+// Jesper Andersson
+//
+
+#ifndef cg_h
+#define cg_h
+
+namespace MISCMATHS {
+
+template < class Matrix, class Vector, class Preconditioner, class Real >
+int 
+CG(const Matrix &A, Vector &x, const Vector &b,
+   const Preconditioner &M, int &max_iter, Real &tol)
+{
+  Real resid;
+  Vector p, z, q;
+  Vector alpha(1), beta(1), rho(1), rho_1(1);
+
+  Real normb = b.NormFrobenius();
+  Vector r = b - A*x;
+
+  if (normb == 0.0) 
+    normb = 1;
+  
+  if ((resid = r.NormFrobenius() / normb) <= tol) {
+    tol = resid;
+    max_iter = 0;
+    return 0;
+  }
+
+  for (int i = 1; i <= max_iter; i++) {
+    z = M.solve(r);
+    rho(1) = DotProduct(r, z);
+    
+    if (i == 1)
+      p = z;
+    else {
+      beta(1) = rho(1) / rho_1(1);
+      p = z + beta(1) * p;
+    }
+    
+    q = A*p;
+    alpha(1) = rho(1) / DotProduct(p, q);
+    
+    x += alpha(1) * p;
+    r -= alpha(1) * q;
+    
+    if ((resid = r.NormFrobenius() / normb) <= tol) {
+      tol = resid;
+      max_iter = i;
+      return 0;     
+    }
+
+    rho_1(1) = rho(1);
+  }
+  
+  tol = resid;
+  return 1;
+}
+
+} // End namespace MISCMATHS
+
+#endif // End #ifndef cg_h
diff --git a/cgs.h b/cgs.h
new file mode 100644
index 0000000..1765336
--- /dev/null
+++ b/cgs.h
@@ -0,0 +1,90 @@
+//*****************************************************************
+// Iterative template routine -- CGS
+//
+// CGS solves the unsymmetric linear system Ax = b 
+// using the Conjugate Gradient Squared method
+//
+// CGS follows the algorithm described on p. 26 of the 
+// SIAM Templates book.
+//
+// The return value indicates convergence within max_iter (input)
+// iterations (0), or no convergence within max_iter iterations (1).
+//
+// Upon successful return, output arguments have the following values:
+//  
+//        x  --  approximate solution to Ax = b
+// max_iter  --  the number of iterations performed before the
+//               tolerance was reached
+//      tol  --  the residual after the final iteration
+//  
+//*****************************************************************
+//
+// Slightly modified version of IML++ template. See ReadMe file.
+//
+// Jesper Andersson
+//
+
+#ifndef cgs_h
+#define cgs_h
+
+namespace MISCMATHS {
+
+template < class Matrix, class Vector, class Preconditioner, class Real >
+int 
+CGS(const Matrix &A, Vector &x, const Vector &b,
+    const Preconditioner &M, int &max_iter, Real &tol)
+{
+  Real resid;
+  Vector rho_1(1), rho_2(1), alpha(1), beta(1);
+  Vector p, phat, q, qhat, vhat, u, uhat;
+
+  Real normb = b.NormFrobenius();
+  Vector r = b - A*x;
+  Vector rtilde = r;
+
+  if (normb == 0.0)
+    normb = 1;
+  
+  if ((resid = r.NormFrobenius() / normb) <= tol) {
+    tol = resid;
+    max_iter = 0;
+    return 0;
+  }
+
+  for (int i = 1; i <= max_iter; i++) {
+    rho_1(1) = DotProduct(rtilde, r);
+    if (rho_1(1) == 0) {
+      tol = r.NormFrobenius() / normb;
+      return 2;
+    }
+    if (i == 1) {
+      u = r;
+      p = u;
+    } else {
+      beta(1) = rho_1(1) / rho_2(1);
+      u = r + beta(1) * q;
+      p = u + beta(1) * (q + beta(1) * p);
+    }
+    phat = M.solve(p);
+    vhat = A*phat;
+    alpha(1) = rho_1(1) / DotProduct(rtilde, vhat);
+    q = u - alpha(1) * vhat;
+    uhat = M.solve(u + q);
+    x += alpha(1) * uhat;
+    qhat = A * uhat;
+    r -= alpha(1) * qhat;
+    rho_2(1) = rho_1(1);
+    if ((resid = r.NormFrobenius() / normb) < tol) {
+      tol = resid;
+      max_iter = i;
+      return 0;
+    }
+  }
+  
+  tol = resid;
+  return 1;
+}
+
+} // End namespace MISCMATHS
+
+#endif // End #ifndef cgs_h
diff --git a/cheby.h b/cheby.h
new file mode 100644
index 0000000..67be8d5
--- /dev/null
+++ b/cheby.h
@@ -0,0 +1,88 @@
+//*****************************************************************
+// Iterative template routine -- CHEBY
+//
+// CHEBY solves the symmetric positive definite linear
+// system Ax = b using the Preconditioned Chebyshev Method
+//
+// CHEBY follows the algorithm described on p. 30 of the 
+// SIAM Templates book.
+//
+// The return value indicates convergence within max_iter (input)
+// iterations (0), or no convergence within max_iter iterations (1).
+//
+// Upon successful return, output arguments have the following values:
+//  
+//        x  --  approximate solution to Ax = b
+// max_iter  --  the number of iterations performed before the
+//               tolerance was reached
+//      tol  --  the residual after the final iteration
+//  
+//*****************************************************************
+//
+// Slightly modified version of IML++ template. See ReadMe file.
+//
+// Jesper Andersson
+//
+
+#ifndef cheby_h
+#define cheby_h
+
+namespace MISCMATHS {
+
+template < class Matrix, class Vector, class Preconditioner, class Real,
+           class Type >
+int 
+CHEBY(const Matrix &A, Vector &x, const Vector &b,
+      const Preconditioner &M, int &max_iter, Real &tol,
+      Type eigmin, Type eigmax)
+{
+  Real resid;
+  Type alpha, beta, c, d;
+  Vector p, q, z;
+
+  Real normb = b.NormFrobenius();
+  Vector r = b - A * x;
+
+  if (normb == 0.0)
+    normb = 1;
+  
+  if ((resid = r.NormFrobenius() / normb) <= tol) {
+    tol = resid;
+    max_iter = 0;
+    return 0;
+  }
+
+  c = (eigmax - eigmin) / 2.0;
+  d = (eigmax + eigmin) / 2.0;
+
+  for (int i = 1; i <= max_iter; i++) {
+    z = M.solve(r);                 // apply preconditioner
+
+    if (i == 1) {
+      p = z;
+      alpha = 2.0 / d;
+    } else {
+      beta = c * alpha / 2.0;       // calculate new beta
+      beta = beta * beta;
+      alpha = 1.0 / (d - beta);     // calculate new alpha
+      p = z + beta * p;             // update search direction
+    }
+
+    q = A * p;
+    x += alpha * p;                 // update approximation vector
+    r -= alpha * q;                 // compute residual
+
+    if ((resid = r.NormFrobenius() / normb) <= tol) {
+      tol = resid;
+      max_iter = i;
+      return 0;                     // convergence
+    }
+  }
+
+  tol = resid;
+  return 1;                         // no convergence
+}
+
+} // End namespace MISCMATHS
+
+#endif // End #ifndef cheby_h
diff --git a/ir.h b/ir.h
new file mode 100644
index 0000000..c6f6ee2
--- /dev/null
+++ b/ir.h
@@ -0,0 +1,69 @@
+//*****************************************************************
+// Iterative template routine -- Preconditioned Richardson
+//
+// IR solves the unsymmetric linear system Ax = b using 
+// Iterative Refinement (preconditioned Richardson iteration).
+//
+// The return value indicates convergence within max_iter (input)
+// iterations (0), or no convergence within max_iter iterations (1).
+//
+// Upon successful return, output arguments have the following values:
+//  
+//        x  --  approximate solution to Ax = b
+// max_iter  --  the number of iterations performed before the
+//               tolerance was reached
+//      tol  --  the residual after the final iteration
+//  
+//*****************************************************************
+//
+// Slightly modified version of IML++ template. See ReadMe file.
+//
+// Jesper Andersson
+//
+
+#ifndef ir_h
+#define ir_h
+
+namespace MISCMATHS {
+
+template < class Matrix, class Vector, class Preconditioner, class Real >
+int 
+IR(const Matrix &A, Vector &x, const Vector &b,
+   const Preconditioner &M, int &max_iter, Real &tol)
+{
+  Real resid;
+  Vector z;
+
+  Real normb = b.NormFrobenius();
+  Vector r = b - A*x;
+
+  if (normb == 0.0) 
+    normb = 1;
+  
+  if ((resid = r.NormFrobenius() / normb) <= tol) {
+    tol = resid;
+    max_iter = 0;
+    return 0;
+  }
+  
+  for (int i = 1; i <= max_iter; i++) {
+    z = M.solve(r);
+    x += z;
+    r = b - A * x;
+    
+    if ((resid = r.NormFrobenius() / normb) <= tol) {
+      tol = resid;
+      max_iter = i;
+      return 0;
+    }
+  }
+
+  tol = resid;
+  return 1;
+}
+
+} // End namespace MISCMATHS
+
+#endif // End #ifndef ir_h
+
+
diff --git a/qmr.h b/qmr.h
new file mode 100644
index 0000000..608d3f3
--- /dev/null
+++ b/qmr.h
@@ -0,0 +1,167 @@
+//*****************************************************************
+// Iterative template routine -- QMR
+//
+// QMR.h solves the unsymmetric linear system Ax = b using the
+// Quasi-Minimal Residual method following the algorithm as described
+// on p. 24 in the SIAM Templates book.
+//
+//   -------------------------------------------------------------
+//   return value     indicates
+//   ------------     ---------------------
+//        0           convergence within max_iter iterations
+//        1           no convergence after max_iter iterations
+//                    breakdown in:
+//        2             rho
+//        3             beta
+//        4             gamma
+//        5             delta
+//        6             ep
+//        7             xi
+//   -------------------------------------------------------------
+//   
+// Upon successful return, output arguments have the following values:
+//
+//        x  --  approximate solution to Ax=b
+// max_iter  --  the number of iterations performed before the
+//               tolerance was reached
+//      tol  --  the residual after the final iteration
+//
+//*****************************************************************
+//
+// Slightly modified version of IML++ template. See ReadMe file.
+//
+// Jesper Andersson
+//
+
+#ifndef qmr_h
+#define qmr_h
+
+#include <math.h>
+
+namespace MISCMATHS {
+
+template < class Matrix, class Vector, class Preconditioner1,
+           class Preconditioner2, class Real >
+int 
+QMR(const Matrix &A, Vector &x, const Vector &b, const Preconditioner1 &M1, 
+    const Preconditioner2 &M2, int &max_iter, Real &tol)
+{
+  Real resid;
+
+  Vector rho(1), rho_1(1), xi(1), gamma(1), gamma_1(1), theta(1), theta_1(1);
+  Vector eta(1), delta(1), ep(1), beta(1);
+
+  Vector r, v_tld, y, w_tld, z;
+  Vector v, w, y_tld, z_tld;
+  Vector p, q, p_tld, d, s;
+
+  Real normb = b.NormFrobenius();
+
+  r = b - A * x;
+
+  if (normb == 0.0)
+    normb = 1;
+
+  if ((resid = r.NormFrobenius() / normb) <= tol) {
+    tol = resid;
+    max_iter = 0;
+    return 0;
+  }
+
+  v_tld = r;
+  y = M1.solve(v_tld);
+  rho(1) = y.NormFrobenius();
+
+  w_tld = r;
+  z = M2.trans_solve(w_tld);
+  xi(1) = z.NormFrobenius();
+
+  gamma(1) = 1.0;
+  eta(1) = -1.0;
+  theta(1) = 0.0;
+
+  for (int i = 1; i <= max_iter; i++) {
+
+    if (rho(1) == 0.0)
+      return 2;                        // return on breakdown
+
+    if (xi(1) == 0.0)
+      return 7;                        // return on breakdown
+
+    v = (1. / rho(1)) * v_tld;
+    y = (1. / rho(1)) * y;
+
+    w = (1. / xi(1)) * w_tld;
+    z = (1. / xi(1)) * z;
+
+    delta(1) = DotProduct(z, y);
+    if (delta(1) == 0.0)
+      return 5;                        // return on breakdown
+
+    y_tld = M2.solve(y);               // apply preconditioners
+    z_tld = M1.trans_solve(z);
+
+    if (i > 1) {
+      p = y_tld - (xi(1) * delta(1) / ep(1)) * p;
+      q = z_tld - (rho(1) * delta(1) / ep(1)) * q;
+    } else {
+      p = y_tld;
+      q = z_tld;
+    }
+
+    p_tld = A * p;
+    ep(1) = DotProduct(q, p_tld);
+    if (ep(1) == 0.0)
+      return 6;                        // return on breakdown
+
+    beta(1) = ep(1) / delta(1);
+    if (beta(1) == 0.0)
+      return 3;                        // return on breakdown
+
+    v_tld = p_tld - beta(1) * v;
+    y = M1.solve(v_tld);
+
+    rho_1(1) = rho(1);
+    rho(1) = y.NormFrobenius();
+    w_tld = A.trans_mult(q) - beta(1) * w;
+    z = M2.trans_solve(w_tld);
+
+    xi(1) = z.NormFrobenius();
+
+    gamma_1(1) = gamma(1);
+    theta_1(1) = theta(1);
+
+    theta(1) = rho(1) / (gamma_1(1) * beta(1));
+    gamma(1) = 1.0 / sqrt(1.0 + theta(1) * theta(1));
+
+    if (gamma(1) == 0.0)
+      return 4;                        // return on breakdown
+
+    eta(1) = -eta(1) * rho_1(1) * gamma(1) * gamma(1) / 
+      (beta(1) * gamma_1(1) * gamma_1(1));
+
+    if (i > 1) {
+      d = eta(1) * p + (theta_1(1) * theta_1(1) * gamma(1) * gamma(1)) * d;
+      s = eta(1) * p_tld + (theta_1(1) * theta_1(1) * gamma(1) * gamma(1)) * s;
+    } else {
+      d = eta(1) * p;
+      s = eta(1) * p_tld;
+    }
+
+    x += d;                            // update approximation vector
+    r -= s;                            // compute residual
+
+    if ((resid = r.NormFrobenius() / normb) <= tol) {
+      tol = resid;
+      max_iter = i;
+      return 0;
+    }
+  }
+
+  tol = resid;
+  return 1;                            // no convergence
+}
+
+} // End namespace MISCMATHS
+
+#endif // End #ifndef qmr_h
-- 
GitLab