From 447019ec1ec268754d3e0f2caab4cb1a3434b850 Mon Sep 17 00:00:00 2001 From: Linus Seelinger <S.Linus@gmx.de> Date: Mon, 26 Jun 2017 12:00:41 +0200 Subject: [PATCH] Add condition number estimate to CG solver --- dune/istl/eigenvalue/arpackpp.hh | 4 +- dune/istl/solver.hh | 3 + dune/istl/solvers.hh | 94 ++++++++++++++++++++++ dune/istl/test/CMakeLists.txt | 2 + dune/istl/test/cgconditiontest.cc | 129 ++++++++++++++++++++++++++++++ 5 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 dune/istl/test/cgconditiontest.cc diff --git a/dune/istl/eigenvalue/arpackpp.hh b/dune/istl/eigenvalue/arpackpp.hh index bee2fed15..7b9e6c9e5 100644 --- a/dune/istl/eigenvalue/arpackpp.hh +++ b/dune/istl/eigenvalue/arpackpp.hh @@ -308,7 +308,7 @@ namespace Dune // allocate memory for variables, set parameters const int nev = 1; // Number of eigenvalues to compute - int ncv = std::min(20, nrows) // Number of Arnoldi vectors generated at each iteration (0 == auto) + int ncv = std::min(20, nrows); // Number of Arnoldi vectors generated at each iteration (0 == auto) const Real tol = epsilon; // Stopping tolerance (relative accuracy of Ritz values) (0 == machine precision) const int maxit = nIterationsMax_*nev; // Maximum number of Arnoldi update iterations allowed (0 == 100*nev) Real* ev = new Real[nev]; // Computed eigenvalues of A @@ -849,7 +849,7 @@ namespace Dune // allocate memory for variables, set parameters const int nev = 2; // Number of eigenvalues to compute - const int ncv = std::min(20, nrows); // Number of Arnoldi vectors generated at each iteration (0 == auto) + int ncv = std::min(20, nrows); // Number of Arnoldi vectors generated at each iteration (0 == auto) const Real tol = epsilon; // Stopping tolerance (relative accuracy of Ritz values) (0 == machine precision) const int maxit = nIterationsMax_*nev; // Maximum number of Arnoldi update iterations allowed (0 == 100*nev) Real* ev = new Real[nev]; // Computed eigenvalues of A^T*A diff --git a/dune/istl/solver.hh b/dune/istl/solver.hh index 672710c6a..1eefa7360 100644 --- a/dune/istl/solver.hh +++ b/dune/istl/solver.hh @@ -66,6 +66,9 @@ namespace Dune /** \brief Convergence rate (average reduction per step) */ double conv_rate; + /** \brief Estimate of condition number */ + double condition_estimate = -1; + /** \brief Elapsed time in seconds */ double elapsed; }; diff --git a/dune/istl/solvers.hh b/dune/istl/solvers.hh index ebc0c6a04..08b2860f1 100644 --- a/dune/istl/solvers.hh +++ b/dune/istl/solvers.hh @@ -26,6 +26,8 @@ #include <dune/common/timer.hh> #include <dune/common/ftraits.hh> #include <dune/common/typetraits.hh> +#include <dune/istl/bcrsmatrix.hh> +#include <dune/istl/eigenvalue/arpackpp.hh> namespace Dune { /** @addtogroup ISTL_Solvers @@ -254,6 +256,29 @@ namespace Dune { // copy base class constructors using IterativeSolver<X,X>::IterativeSolver; + /*! + \brief Constructor to initialize a CG solver. + \copydetails IterativeSolver::IterativeSolver(LinearOperator<X,Y>&, Preconditioner<X,Y>&, real_type, int, int) + \param condition_estimate Whether to calculate an estimate of the condition number. + The estimate is given in the InverseOperatorResult returned by apply(). + */ + CGSolver (LinearOperator<X,X>& op, Preconditioner<X,X>& prec, + real_type reduction, int maxit, int verbose, bool condition_estimate) : IterativeSolver<X,X>(op, prec, reduction, maxit, verbose), + condition_estimate_(condition_estimate) + {} + + /*! + \brief Constructor to initialize a CG solver. + \copydetails IterativeSolver::IterativeSolver(LinearOperator<X,Y>&, ScalarProduct<X>&, Preconditioner<X,Y>&, real_type, int, int) + \param condition_estimate Whether to calculate an estimate of the condition number. + The estimate is given in the InverseOperatorResult returned by apply(). + */ + CGSolver (LinearOperator<X,X>& op, ScalarProduct<X>& sp, Preconditioner<X,X>& prec, + real_type reduction, int maxit, int verbose, bool condition_estimate) : IterativeSolver<X,X>(op, sp, prec, reduction, maxit, verbose), + condition_estimate_(condition_estimate) + {} + + /*! \brief Apply inverse operator. @@ -311,6 +336,10 @@ namespace Dune { } } + // Remember lambda and beta values for condition estimate + std::vector<real_type> lambdas(0); + std::vector<real_type> betas(0); + // some local variables real_type def=def0; // loop variables field_type rho,rholast,lambda,alpha,beta; @@ -328,6 +357,8 @@ namespace Dune { _op->apply(p,q); // q=Ap alpha = _sp->dot(p,q); // scalar product lambda = rholast/alpha; // minimization + if (condition_estimate_) + lambdas.push_back(std::real(lambda)); x.axpy(lambda,p); // update solution b.axpy(-lambda,q); // update defect @@ -358,6 +389,8 @@ namespace Dune { _prec->apply(q,b); // apply preconditioner rho = _sp->dot(q,b); // orthogonalization beta = rho/rholast; // scaling factor + if (condition_estimate_) + betas.push_back(std::real(beta)); p *= beta; // scale old search direction p += q; // orthogonalization with correction rholast = rho; // remember rho for recurrence @@ -382,8 +415,69 @@ namespace Dune { << ", TIT=" << res.elapsed/i << ", IT=" << i << std::endl; } + + + if (condition_estimate_) { +#if HAVE_ARPACKPP + + // Build T matrix which has extreme eigenvalues approximating + // those of the original system + // (see Y. Saad, Iterative methods for sparse linear systems) + + COND_MAT T(i, i, COND_MAT::row_wise); + + for (auto row = T.createbegin(); row != T.createend(); ++row) { + if (row.index() > 0) + row.insert(row.index()-1); + row.insert(row.index()); + if (row.index() < T.N() - 1) + row.insert(row.index()+1); + } + for (int row = 0; row < i; ++row) { + if (row > 0) { + T[row][row-1] = std::sqrt(betas[row-1]) / lambdas[row-1]; + } + + T[row][row] = 1.0 / lambdas[row]; + if (row > 0) { + T[row][row] += betas[row-1] / lambdas[row-1]; + } + + if (row < i - 1) { + T[row][row+1] = std::sqrt(betas[row]) / lambdas[row]; + } + } + + // Compute largest and smallest eigenvalue of T matrix and return as estimate + Dune::ArPackPlusPlus_Algorithms<COND_MAT, COND_VEC> arpack(T); + + real_type eps = 0.0; + COND_VEC eigv; + real_type min_eigv, max_eigv; + arpack.computeSymMinMagnitude (eps, eigv, min_eigv); + arpack.computeSymMaxMagnitude (eps, eigv, max_eigv); + + res.condition_estimate = max_eigv / min_eigv; + + if (_verbose > 0) { + std::cout << "Min eigv estimate: " << min_eigv << std::endl; + std::cout << "Max eigv estimate: " << max_eigv << std::endl; + std::cout << "Condition estimate: " << max_eigv / min_eigv << std::endl; + } + +#else + std::cerr << "WARNING: Condition estimate was requested. This requires ARPACK, but ARPACK was not found!" << std::endl; +#endif + } } + private: + bool condition_estimate_ = false; + + // Matrix and vector types used for condition estimate + typedef Dune::BCRSMatrix<Dune::FieldMatrix<real_type,1,1> > COND_MAT; + typedef Dune::BlockVector<Dune::FieldVector<real_type,1> > COND_VEC; + protected: using IterativeSolver<X,X>::_op; using IterativeSolver<X,X>::_prec; diff --git a/dune/istl/test/CMakeLists.txt b/dune/istl/test/CMakeLists.txt index a78ee0b83..8dd8970b3 100644 --- a/dune/istl/test/CMakeLists.txt +++ b/dune/istl/test/CMakeLists.txt @@ -8,6 +8,8 @@ dune_add_test(SOURCES bcrsassigntest.cc) dune_add_test(SOURCES bcrsnormtest.cc) +dune_add_test(SOURCES cgconditiontest.cc) + dune_add_test(SOURCES dotproducttest.cc) dune_add_test(SOURCES complexmatrixtest.cc) diff --git a/dune/istl/test/cgconditiontest.cc b/dune/istl/test/cgconditiontest.cc new file mode 100644 index 000000000..9919337c9 --- /dev/null +++ b/dune/istl/test/cgconditiontest.cc @@ -0,0 +1,129 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <dune/istl/preconditioners.hh> +#include <dune/istl/solvers.hh> + +typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> > MAT; +typedef Dune::BlockVector<Dune::FieldVector<double,1> > VEC; + +void condition_test (MAT& T) { + + int N = T.N(); + + // CG run (noop preconditioner) with condition estimate + + auto prec = std::make_shared<Dune::Richardson<VEC, VEC>> (1.0); + + auto op = std::make_shared<Dune::MatrixAdapter<MAT, VEC, VEC>>(T); + + VEC d (N,N); + VEC v (N,N); + + v = 1.0; + d = 1.0; + + int verbosity = 2; + + Dune::InverseOperatorResult result; + auto solver = std::make_shared<Dune::CGSolver<VEC> >(*op,*prec,1E-6,1000,verbosity,true); + solver->apply(v,d,result); + + + +#if HAVE_ARPACKPP + // Actual condition number using ARPACK + Dune::ArPackPlusPlus_Algorithms<MAT, VEC> arpack(T); + + double eps = 0.0; + VEC eigv; + double min_eigv, max_eigv; + arpack.computeSymMinMagnitude (eps, eigv, min_eigv); + arpack.computeSymMaxMagnitude (eps, eigv, max_eigv); + + std::cout << "Actual condition number from ARPACK: " << max_eigv / min_eigv << std::endl; +#endif +} + + + +int main(int argc, char **argv) +{ + + // Simple stencil + { + int N = 142; + + MAT T(N, N, MAT::row_wise); + + for (auto row = T.createbegin(); row != T.createend(); ++row) { + if (row.index() > 0) + row.insert(row.index()-1); + row.insert(row.index()); + if (row.index() < T.N() - 1) + row.insert(row.index()+1); + } + for (int row = 0; row < N; ++row) { + T[row][row] = 2.0; + if (row > 0) + T[row][row-1] = -1.0; + if (row < N-1) + T[row][row+1] = -1.0; + } + + condition_test (T); + } + + + // Ill-conditioned stencil + { + int N = 142; + + MAT T(N, N, MAT::row_wise); + + for (auto row = T.createbegin(); row != T.createend(); ++row) { + if (row.index() > 0) + row.insert(row.index()-1); + row.insert(row.index()); + if (row.index() < T.N() - 1) + row.insert(row.index()+1); + } + for (int row = 0; row < N; ++row) { + double factor = 0.8 + 0.2 * ((double)row / N); + T[row][row] = 2.0 * factor; + if (row > 0) + T[row][row-1] = -1.0 * factor; + if (row < N-1) + T[row][row+1] = -1.0 * factor; + } + + condition_test (T); + } + + + // Very ill-conditioned stencil + { + int N = 40; + + MAT T(N, N, MAT::row_wise); + + for (auto row = T.createbegin(); row != T.createend(); ++row) { + for (int i = -10; i <= 10; i++) { + if (row.index()+i >= 0 && row.index()+i < T.N()) + row.insert(row.index()+i); + } + } + for (int row = 0; row < N; ++row) { + double factor = 0.8 + 0.2 * ((double)row / N); + for (int i = -10; i <= 10; i++) { + if (row+i >= 0 && row+i < T.N()) + T[row][row+i] = -1.0 * factor; + } + T[row][row] = 20.0 * factor; + } + + condition_test (T); + } + +} -- GitLab