]> https://gitweb.dealii.org/ - dealii.git/commitdiff
add idr(s) solver
authortcclevenger <tcleven@clemson.edu>
Mon, 30 Sep 2019 07:08:26 +0000 (03:08 -0400)
committertcclevenger <tcleven@clemson.edu>
Thu, 5 Dec 2019 14:55:22 +0000 (09:55 -0500)
include/deal.II/lac/solver_gmres.h
include/deal.II/lac/solver_idr.h [new file with mode: 0644]
tests/lac/solver_02.cc
tests/lac/solver_02.output

index f6e1ad966e979db8fb4f832bb4153cfaed609fae..f707151be3cd73cd4183e61c64f2bfa392eb0e04 100644 (file)
@@ -95,7 +95,7 @@ namespace internal
 
     private:
       /**
-       * Pool were vectors are obtained from.
+       * Pool where vectors are obtained from.
        */
       VectorMemory<VectorType> &mem;
 
diff --git a/include/deal.II/lac/solver_idr.h b/include/deal.II/lac/solver_idr.h
new file mode 100644 (file)
index 0000000..617a693
--- /dev/null
@@ -0,0 +1,478 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2000 - 2019 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+#ifndef dealii_solver_idr_h
+#define dealii_solver_idr_h
+
+
+#include <deal.II/base/config.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/signaling_nan.h>
+#include <deal.II/base/subscriptor.h>
+#include <deal.II/base/utilities.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/solver.h>
+#include <deal.II/lac/solver_control.h>
+
+#include <cmath>
+#include <random>
+
+DEAL_II_NAMESPACE_OPEN
+
+/*!@addtogroup Solvers */
+/*@{*/
+
+namespace internal
+{
+  /**
+   * A namespace for a helper class to the IDR(s) solver.
+   */
+  namespace SolverIDRImplementation
+  {
+    /**
+     * Class to hold temporary vectors whose size depends on
+     * the solver parameter s.
+     */
+    template <typename VectorType>
+    class TmpVectors
+    {
+    public:
+      /**
+       * Constructor. Prepares an array of @p VectorType of length @p s_param.
+       */
+      TmpVectors(const unsigned int s_param, VectorMemory<VectorType> &vmem);
+
+      /**
+       * Destructor. Delete all allocated vectors.
+       */
+      ~TmpVectors() = default;
+
+      /**
+       * Get vector number @p i. If this vector was unused before, an error
+       * occurs.
+       */
+      VectorType &operator[](const unsigned int i) const;
+
+      /**
+       * Get vector number @p i. Allocate it if necessary.
+       *
+       * If a vector must be allocated, @p temp is used to reinit it to the
+       * proper dimensions.
+       */
+      VectorType &
+      operator()(const unsigned int i, const VectorType &temp);
+
+    private:
+      /**
+       * Pool where vectors are obtained from.
+       */
+      VectorMemory<VectorType> &mem;
+
+      /**
+       * Field for storing the vectors.
+       */
+      std::vector<typename VectorMemory<VectorType>::Pointer> data;
+    };
+  } // namespace SolverIDRImplementation
+} // namespace internal
+
+/**
+ * This class implements the IDR(s) method used for solving nonsymmetric,
+ * indefinite linear systems, developed in <a
+ * href="https://epubs.siam.org/doi/abs/10.1137/070685804">
+ * IDR(s): A Family of Simple and Fast Algorithms for Solving Large
+ * Nonsymmetric Systems of Linear Equations by Martin B. van Gijzen and Peter
+ * Sonneveld </a>. The implementation here is the preconditioned version from <a
+ * href="https://dl.acm.org/citation.cfm?id=2049667">
+ * Algorithm 913: An Elegant IDR(s) Variant that Efficiently Exploits
+ * Biorthogonality Properties
+ * by Martin B. van Gijzen and Peter Sonneveld</a>. The local structure
+ * @p AdditionalData takes the value for the parameter s which can be any
+ * integer greater than or equal to 1. For <code>s=1</code>, this method has
+ * similar convergence to BiCGStab.
+ *
+ * @note Each iteration of IDR(s) requires <code>s+1</code> preconditioning steps
+ * and matrix-vector products. In this implementation the residual is updated
+ * and convergence is checked after each of these inner steps inside the outer
+ * iteration. If the user enables the history data, the residual at each of
+ * these steps is stored and therefore there will be multiple values per
+ * iteration.
+ *
+ * @author Conrad Clevenger, 2019
+ */
+template <class VectorType = Vector<double>>
+class SolverIDR : public SolverBase<VectorType>
+{
+public:
+  /**
+   * Structure for storing additional data needed by the solver.
+   */
+  struct AdditionalData
+  {
+    /**
+     * Constructor. By default, an IDR(2) method is used.
+     */
+    explicit AdditionalData(const unsigned int s = 2)
+      : s(s)
+    {}
+
+    const unsigned int s;
+  };
+
+  /**
+   * Constructor.
+   */
+  SolverIDR(SolverControl &           cn,
+            VectorMemory<VectorType> &mem,
+            const AdditionalData &    data = AdditionalData());
+
+  /**
+   * Constructor. Use an object of type GrowingVectorMemory as a default to
+   * allocate memory.
+   */
+  explicit SolverIDR(SolverControl &       cn,
+                     const AdditionalData &data = AdditionalData());
+
+  /**
+   * Virtual destructor.
+   */
+  virtual ~SolverIDR() override = default;
+
+  /**
+   * Solve the linear system <code>Ax=b</code> for x.
+   */
+  template <typename MatrixType, typename PreconditionerType>
+  void
+  solve(const MatrixType &        A,
+        VectorType &              x,
+        const VectorType &        b,
+        const PreconditionerType &preconditioner);
+
+protected:
+  /**
+   * Interface for derived class. This function gets the current iteration
+   * vector, the residual and the update vector in each step. It can be used
+   * for graphical output of the convergence history.
+   */
+  virtual void
+  print_vectors(const unsigned int step,
+                const VectorType & x,
+                const VectorType & r,
+                const VectorType & d) const;
+
+private:
+  /**
+   * Additional solver parameters.
+   */
+  AdditionalData additional_data;
+};
+
+/*@}*/
+/*------------------------- Implementation ----------------------------*/
+
+#ifndef DOXYGEN
+
+
+namespace internal
+{
+  namespace SolverIDRImplementation
+  {
+    template <class VectorType>
+    inline TmpVectors<VectorType>::TmpVectors(const unsigned int        s_param,
+                                              VectorMemory<VectorType> &vmem)
+      : mem(vmem)
+      , data(s_param)
+    {}
+
+
+
+    template <class VectorType>
+    inline VectorType &TmpVectors<VectorType>::
+                       operator[](const unsigned int i) const
+    {
+      Assert(i < data.size(), ExcIndexRange(i, 0, data.size()));
+
+      Assert(data[i] != nullptr, ExcNotInitialized());
+      return *data[i];
+    }
+
+
+
+    template <class VectorType>
+    inline VectorType &
+    TmpVectors<VectorType>::operator()(const unsigned int i,
+                                       const VectorType & temp)
+    {
+      AssertIndexRange(i, data.size());
+      if (data[i] == nullptr)
+        {
+          data[i] = std::move(typename VectorMemory<VectorType>::Pointer(mem));
+          data[i]->reinit(temp);
+        }
+      return *data[i];
+    }
+  } // namespace SolverIDRImplementation
+} // namespace internal
+
+
+
+template <class VectorType>
+SolverIDR<VectorType>::SolverIDR(SolverControl &           cn,
+                                 VectorMemory<VectorType> &mem,
+                                 const AdditionalData &    data)
+  : SolverBase<VectorType>(cn, mem)
+  , additional_data(data)
+{}
+
+
+
+template <class VectorType>
+SolverIDR<VectorType>::SolverIDR(SolverControl &cn, const AdditionalData &data)
+  : SolverBase<VectorType>(cn)
+  , additional_data(data)
+{}
+
+
+
+template <class VectorType>
+void
+SolverIDR<VectorType>::print_vectors(const unsigned int,
+                                     const VectorType &,
+                                     const VectorType &,
+                                     const VectorType &) const
+{}
+
+
+
+template <class VectorType>
+template <typename MatrixType, typename PreconditionerType>
+void
+SolverIDR<VectorType>::solve(const MatrixType &        A,
+                             VectorType &              x,
+                             const VectorType &        b,
+                             const PreconditionerType &preconditioner)
+{
+  LogStream::Prefix prefix("IDR(s)");
+
+  SolverControl::State iteration_state = SolverControl::iterate;
+  unsigned int         step            = 0;
+
+  const unsigned int s = additional_data.s;
+
+  // Define temporary vectors which do not do not depend on s
+  typename VectorMemory<VectorType>::Pointer r_pointer(this->memory);
+  typename VectorMemory<VectorType>::Pointer v_pointer(this->memory);
+  typename VectorMemory<VectorType>::Pointer vhat_pointer(this->memory);
+  typename VectorMemory<VectorType>::Pointer uhat_pointer(this->memory);
+  typename VectorMemory<VectorType>::Pointer ghat_pointer(this->memory);
+
+  VectorType &r    = *r_pointer;
+  VectorType &v    = *v_pointer;
+  VectorType &vhat = *vhat_pointer;
+  VectorType &uhat = *uhat_pointer;
+  VectorType &ghat = *ghat_pointer;
+
+  r.reinit(x, true);
+  v.reinit(x, true);
+  vhat.reinit(x, true);
+  uhat.reinit(x, true);
+  ghat.reinit(x, true);
+
+  // Initial residual
+  A.vmult(r, x);
+  r.sadd(-1.0, 1.0, b);
+
+  // Check for convergent initial guess
+  double res      = r.l2_norm();
+  iteration_state = this->iteration_status(step, res, x);
+  if (iteration_state == SolverControl::success)
+    return;
+
+  // Initialize sets of vectors/matrices whose size dependent on s
+  internal::SolverIDRImplementation::TmpVectors<VectorType> G(s, this->memory);
+  internal::SolverIDRImplementation::TmpVectors<VectorType> U(s, this->memory);
+  internal::SolverIDRImplementation::TmpVectors<VectorType> Q(s, this->memory);
+  FullMatrix<double>                                        M(s, s);
+
+  // Random number generator for vector entries of
+  // Q (normal distribution, mean=0 sigma=1)
+  std::mt19937               rng;
+  std::normal_distribution<> normal_distribution(0.0, 1.0);
+  for (unsigned int i = 0; i < s; ++i)
+    {
+      VectorType &tmp_g = G(i, x);
+      VectorType &tmp_u = U(i, x);
+      tmp_g             = 0;
+      tmp_u             = 0;
+
+      // Compute random set of s orthonormalized vectors Q
+      // Note: the first vector is chosen to be the initial
+      // residual to match BiCGStab (as is done in comparisons
+      // with BiCGStab in the papers listed in the documentation
+      // of this function)
+      VectorType &tmp_q = Q(i, x);
+      if (i != 0)
+        {
+          for (auto indx : tmp_q.locally_owned_elements())
+            tmp_q(indx) = normal_distribution(rng);
+          tmp_q.compress(VectorOperation::insert);
+        }
+      else
+        tmp_q = r;
+
+      for (unsigned int j = 0; j < i; ++j)
+        {
+          v = Q[j];
+          v *= (v * tmp_q) / (tmp_q * tmp_q);
+          tmp_q.add(-1.0, v);
+        }
+
+      if (i != 0)
+        tmp_q *= 1.0 / tmp_q.l2_norm();
+
+      M(i, i) = 1.;
+    }
+
+  double omega = 1.;
+
+  bool early_exit = false;
+
+  // Outer iteration
+  while (iteration_state == SolverControl::iterate)
+    {
+      ++step;
+
+      // Compute phi
+      Vector<double> phi(s);
+      for (unsigned int i = 0; i < s; ++i)
+        phi(i) = Q[i] * r;
+
+      // Inner iteration over s
+      for (unsigned int k = 0; k < s; ++k)
+        {
+          // Solve M(k:s)*gamma = phi(k:s)
+          Vector<double> gamma(s - k);
+          {
+            Vector<double>            phik(s - k);
+            FullMatrix<double>        Mk(s - k, s - k);
+            std::vector<unsigned int> indices;
+            unsigned int              j = 0;
+            for (unsigned int i = k; i < s; ++i, ++j)
+              {
+                indices.push_back(i);
+                phik(j) = phi(i);
+              }
+            Mk.extract_submatrix_from(M, indices, indices);
+
+            FullMatrix<double> Mk_inv(s - k, s - k);
+            Mk_inv.invert(Mk);
+            Mk_inv.vmult(gamma, phik);
+          }
+
+          {
+            v = r;
+
+            unsigned int j = 0;
+            for (unsigned int i = k; i < s; ++i, ++j)
+              v.add(-1.0 * gamma(j), G[i]);
+            preconditioner.vmult(vhat, v);
+
+            uhat = vhat;
+            uhat *= omega;
+            j = 0;
+            for (unsigned int i = k; i < s; ++i, ++j)
+              uhat.add(gamma(j), U[i]);
+            A.vmult(ghat, uhat);
+          }
+
+          // Update G and U
+          // Orthogonalize ghat to Q0,..,Q_{k-1}
+          // and update uhat
+          for (unsigned int i = 0; i < k; ++i)
+            {
+              double alpha = (Q[i] * ghat) / M(i, i);
+              ghat.add(-alpha, G[i]);
+              uhat.add(-alpha, U[i]);
+            }
+          G[k] = ghat;
+          U[k] = uhat;
+
+          // Update kth column of M
+          for (unsigned int i = k; i < s; ++i)
+            M(i, k) = Q[i] * G[k];
+
+          // Orthogonalize r to Q0,...,Qk,
+          // update x
+          {
+            double beta = phi(k) / M(k, k);
+            r.add(-1.0 * beta, G[k]);
+            x.add(beta, U[k]);
+
+            print_vectors(step, x, r, U[k]);
+
+            // Check for early convergence. If so, store
+            // information in early_exit so that outer iteration
+            // is broken before recomputing the residual
+            res             = r.l2_norm();
+            iteration_state = this->iteration_status(step, res, x);
+            if (iteration_state != SolverControl::iterate)
+              {
+                early_exit = true;
+                break;
+              }
+
+            // Update phi
+            if (k + 1 < s)
+              {
+                for (unsigned int i = 0; i < k + 1; ++i)
+                  phi(i) = 0.0;
+                for (unsigned int i = k + 1; i < s; ++i)
+                  phi(i) -= beta * M(i, k);
+              }
+          }
+        }
+      if (early_exit == true)
+        break;
+
+      // Update r and x
+      preconditioner.vmult(vhat, r);
+      A.vmult(v, vhat);
+
+      omega = (v * r) / (v * v);
+
+      r.add(-1.0 * omega, v);
+      x.add(omega, vhat);
+
+      print_vectors(step, x, r, vhat);
+
+      // Check for convergence
+      res             = r.l2_norm();
+      iteration_state = this->iteration_status(step, res, x);
+      if (iteration_state != SolverControl::iterate)
+        break;
+    }
+
+  if (iteration_state != SolverControl::success)
+    AssertThrow(false, SolverControl::NoConvergence(step, res));
+}
+
+
+#endif // DOXYGEN
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif
index 46fce2fc6d5e498ea821bfcbef4c130c2ac5176c..a046cc8136cc38cc22d75b4d2f596807a48d88ec 100644 (file)
@@ -23,6 +23,7 @@
 #include <deal.II/lac/solver_cg.h>
 #include <deal.II/lac/solver_control.h>
 #include <deal.II/lac/solver_gmres.h>
+#include <deal.II/lac/solver_idr.h>
 #include <deal.II/lac/sparse_matrix.h>
 #include <deal.II/lac/vector.h>
 #include <deal.II/lac/vector_memory.h>
@@ -66,4 +67,5 @@ main()
   test<SolverGMRES<Vector<double>>>();
   test<SolverCG<Vector<double>>>();
   test<SolverFGMRES<Vector<double>>>();
+  test<SolverIDR<Vector<double>>>();
 }
index c8977da68f16892f3eb2c350a1ec636fea09e8fb..d097b92c721f4b8cd134c5050705e6281b8dc22f 100644 (file)
@@ -1,16 +1,21 @@
 
 DEAL:GMRES::Check 0    1.000
 DEAL:GMRES::Starting value 1.000
-DEAL:GMRES::Check 1    0
-DEAL:GMRES::Convergence step 1 value 0
-DEAL::0      0      1.000  
+DEAL:GMRES::Check 1    0.000
+DEAL:GMRES::Convergence step 1 value 0.000
+DEAL::0.000  0.000  1.000  
 DEAL:cg::Check 0       1.000
 DEAL:cg::Starting value 1.000
-DEAL:cg::Check 1       0
-DEAL:cg::Convergence step 1 value 0
-DEAL::0      0      1.000  
+DEAL:cg::Check 1       0.000
+DEAL:cg::Convergence step 1 value 0.000
+DEAL::0.000  0.000  1.000  
 DEAL:FGMRES::Check 0   1.000
 DEAL:FGMRES::Starting value 1.000
-DEAL:FGMRES::Check 1   0
-DEAL:FGMRES::Convergence step 1 value 0
-DEAL::0      0      1.000  
+DEAL:FGMRES::Check 1   0.000
+DEAL:FGMRES::Convergence step 1 value 0.000
+DEAL::0.000  0.000  1.000  
+DEAL:IDR(s)::Check 0   1.000
+DEAL:IDR(s)::Starting value 1.000
+DEAL:IDR(s)::Check 1   0.000
+DEAL:IDR(s)::Convergence step 1 value 0.000
+DEAL::0.000  0.000  1.000  

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.