From 35f7829af10c61e33dd2e2a7a015058e11a11ea0 Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Sat, 25 Mar 2017 14:17:07 +0100 Subject: update --- eigen/doc/examples/matrixfree_cg.cpp | 210 +++++++++++++---------------------- 1 file changed, 79 insertions(+), 131 deletions(-) (limited to 'eigen/doc/examples/matrixfree_cg.cpp') diff --git a/eigen/doc/examples/matrixfree_cg.cpp b/eigen/doc/examples/matrixfree_cg.cpp index f0631c3..6a205ae 100644 --- a/eigen/doc/examples/matrixfree_cg.cpp +++ b/eigen/doc/examples/matrixfree_cg.cpp @@ -2,179 +2,127 @@ #include #include #include +#include class MatrixReplacement; -template class MatrixReplacement_ProductReturnType; +using Eigen::SparseMatrix; namespace Eigen { namespace internal { + // MatrixReplacement looks-like a SparseMatrix, so let's inherits its traits: template<> - struct traits : Eigen::internal::traits > + struct traits : public Eigen::internal::traits > {}; - - template - struct traits > { - // The equivalent plain objet type of the product. This type is used if the product needs to be evaluated into a temporary. - typedef Eigen::Matrix ReturnType; - }; } } -// Inheriting EigenBase should not be needed in the future. +// Example of a matrix-free wrapper from a user type to Eigen's compatible type +// For the sake of simplicity, this example simply wrap a Eigen::SparseMatrix. class MatrixReplacement : public Eigen::EigenBase { public: - // Expose some compile-time information to Eigen: + // Required typedefs, constants, and method: typedef double Scalar; typedef double RealScalar; + typedef int StorageIndex; enum { ColsAtCompileTime = Eigen::Dynamic, - RowsAtCompileTime = Eigen::Dynamic, MaxColsAtCompileTime = Eigen::Dynamic, - MaxRowsAtCompileTime = Eigen::Dynamic + IsRowMajor = false }; - Index rows() const { return 4; } - Index cols() const { return 4; } + Index rows() const { return mp_mat->rows(); } + Index cols() const { return mp_mat->cols(); } - void resize(Index a_rows, Index a_cols) - { - // This method should not be needed in the future. - assert(a_rows==0 && a_cols==0 || a_rows==rows() && a_cols==cols()); - } - - // In the future, the return type should be Eigen::Product template - MatrixReplacement_ProductReturnType operator*(const Eigen::MatrixBase& x) const { - return MatrixReplacement_ProductReturnType(*this, x.derived()); + Eigen::Product operator*(const Eigen::MatrixBase& x) const { + return Eigen::Product(*this, x.derived()); } -}; + // Custom API: + MatrixReplacement() : mp_mat(0) {} -// The proxy class representing the product of a MatrixReplacement with a MatrixBase<> -template -class MatrixReplacement_ProductReturnType : public Eigen::ReturnByValue > { -public: - typedef MatrixReplacement::Index Index; - - // The ctor store references to the matrix and right-hand-side object (usually a vector). - MatrixReplacement_ProductReturnType(const MatrixReplacement& matrix, const Rhs& rhs) - : m_matrix(matrix), m_rhs(rhs) - {} - - Index rows() const { return m_matrix.rows(); } - Index cols() const { return m_rhs.cols(); } - - // This function is automatically called by Eigen. It must evaluate the product of matrix * rhs into y. - template - void evalTo(Dest& y) const - { - y.setZero(4); - - y(0) += 2 * m_rhs(0); y(1) += 1 * m_rhs(0); - y(0) += 1 * m_rhs(1); y(1) += 2 * m_rhs(1); y(2) += 1 * m_rhs(1); - y(1) += 1 * m_rhs(2); y(2) += 2 * m_rhs(2); y(3) += 1 * m_rhs(2); - y(2) += 1 * m_rhs(3); y(3) += 2 * m_rhs(3); + void attachMyMatrix(const SparseMatrix &mat) { + mp_mat = &mat; } + const SparseMatrix my_matrix() const { return *mp_mat; } -protected: - const MatrixReplacement& m_matrix; - typename Rhs::Nested m_rhs; +private: + const SparseMatrix *mp_mat; }; -/*****/ - -// This class simply warp a diagonal matrix as a Jacobi preconditioner. -// In the future such simple and generic wrapper should be shipped within Eigen itsel. -template -class MyJacobiPreconditioner -{ - typedef _Scalar Scalar; - typedef Eigen::Matrix Vector; - typedef typename Vector::Index Index; - - public: - // this typedef is only to export the scalar type and compile-time dimensions to solve_retval - typedef Eigen::Matrix MatrixType; - - MyJacobiPreconditioner() : m_isInitialized(false) {} - - void setInvDiag(const Eigen::VectorXd &invdiag) { - m_invdiag=invdiag; - m_isInitialized=true; - } - - Index rows() const { return m_invdiag.size(); } - Index cols() const { return m_invdiag.size(); } - - template - MyJacobiPreconditioner& analyzePattern(const MatType& ) { return *this; } - - template - MyJacobiPreconditioner& factorize(const MatType& mat) { return *this; } - - template - MyJacobiPreconditioner& compute(const MatType& mat) { return *this; } - - template - void _solve(const Rhs& b, Dest& x) const - { - x = m_invdiag.array() * b.array() ; - } - - template inline const Eigen::internal::solve_retval - solve(const Eigen::MatrixBase& b) const - { - eigen_assert(m_isInitialized && "MyJacobiPreconditioner is not initialized."); - eigen_assert(m_invdiag.size()==b.rows() - && "MyJacobiPreconditioner::solve(): invalid number of rows of the right hand side matrix b"); - return Eigen::internal::solve_retval(*this, b.derived()); - } - - protected: - Vector m_invdiag; - bool m_isInitialized; -}; - +// Implementation of MatrixReplacement * Eigen::DenseVector though a specialization of internal::generic_product_impl: namespace Eigen { namespace internal { -template -struct solve_retval, Rhs> - : solve_retval_base, Rhs> -{ - typedef MyJacobiPreconditioner<_MatrixType> Dec; - EIGEN_MAKE_SOLVE_HELPERS(Dec,Rhs) - - template void evalTo(Dest& dst) const + template + struct generic_product_impl // GEMV stands for matrix-vector + : generic_product_impl_base > { - dec()._solve(rhs(),dst); - } -}; + typedef typename Product::Scalar Scalar; + + template + static void scaleAndAddTo(Dest& dst, const MatrixReplacement& lhs, const Rhs& rhs, const Scalar& alpha) + { + // This method should implement "dst += alpha * lhs * rhs" inplace, + // however, for iterative solvers, alpha is always equal to 1, so let's not bother about it. + assert(alpha==Scalar(1) && "scaling is not implemented"); + + // Here we could simply call dst.noalias() += lhs.my_matrix() * rhs, + // but let's do something fancier (and less efficient): + for(Index i=0; i S = Eigen::MatrixXd::Random(n,n).sparseView(0.5,1); + S = S.transpose()*S; + MatrixReplacement A; - Eigen::VectorXd b(4), x; - b << 1, 1, 1, 1; + A.attachMyMatrix(S); - // solve Ax = b using CG with matrix-free version: - Eigen::ConjugateGradient < MatrixReplacement, Eigen::Lower|Eigen::Upper, MyJacobiPreconditioner > cg; + Eigen::VectorXd b(n), x; + b.setRandom(); - Eigen::VectorXd invdiag(4); - invdiag << 1./3., 1./4., 1./4., 1./3.; + // Solve Ax = b using various iterative solver with matrix-free version: + { + Eigen::ConjugateGradient cg; + cg.compute(A); + x = cg.solve(b); + std::cout << "CG: #iterations: " << cg.iterations() << ", estimated error: " << cg.error() << std::endl; + } + + { + Eigen::BiCGSTAB bicg; + bicg.compute(A); + x = bicg.solve(b); + std::cout << "BiCGSTAB: #iterations: " << bicg.iterations() << ", estimated error: " << bicg.error() << std::endl; + } + + { + Eigen::GMRES gmres; + gmres.compute(A); + x = gmres.solve(b); + std::cout << "GMRES: #iterations: " << gmres.iterations() << ", estimated error: " << gmres.error() << std::endl; + } - cg.preconditioner().setInvDiag(invdiag); - cg.compute(A); - x = cg.solve(b); + { + Eigen::DGMRES gmres; + gmres.compute(A); + x = gmres.solve(b); + std::cout << "DGMRES: #iterations: " << gmres.iterations() << ", estimated error: " << gmres.error() << std::endl; + } - std::cout << "#iterations: " << cg.iterations() << std::endl; - std::cout << "estimated error: " << cg.error() << std::endl; + { + Eigen::MINRES minres; + minres.compute(A); + x = minres.solve(b); + std::cout << "MINRES: #iterations: " << minres.iterations() << ", estimated error: " << minres.error() << std::endl; + } } -- cgit v1.2.3