Skip to content
matrixfree_cg.cpp 4.14 KiB
Newer Older
Luker's avatar
Luker committed
#include <iostream>
#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/IterativeLinearSolvers>
Luker's avatar
Luker committed
#include <unsupported/Eigen/IterativeSolvers>
Luker's avatar
Luker committed

class MatrixReplacement;
Luker's avatar
Luker committed
using Eigen::SparseMatrix;
Luker's avatar
Luker committed

namespace Eigen {
namespace internal {
Luker's avatar
Luker committed
  // MatrixReplacement looks-like a SparseMatrix, so let's inherits its traits:
Luker's avatar
Luker committed
  template<>
Luker's avatar
Luker committed
  struct traits<MatrixReplacement> :  public Eigen::internal::traits<Eigen::SparseMatrix<double> >
Luker's avatar
Luker committed
  {};
}
}

Luker's avatar
Luker committed
// 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.
Luker's avatar
Luker committed
class MatrixReplacement : public Eigen::EigenBase<MatrixReplacement> {
public:
Luker's avatar
Luker committed
  // Required typedefs, constants, and method:
Luker's avatar
Luker committed
  typedef double Scalar;
  typedef double RealScalar;
Luker's avatar
Luker committed
  typedef int StorageIndex;
Luker's avatar
Luker committed
  enum {
    ColsAtCompileTime = Eigen::Dynamic,
    MaxColsAtCompileTime = Eigen::Dynamic,
Luker's avatar
Luker committed
    IsRowMajor = false
Luker's avatar
Luker committed
  };

Luker's avatar
Luker committed
  Index rows() const { return mp_mat->rows(); }
  Index cols() const { return mp_mat->cols(); }
Luker's avatar
Luker committed

  template<typename Rhs>
Luker's avatar
Luker committed
  Eigen::Product<MatrixReplacement,Rhs,Eigen::AliasFreeProduct> operator*(const Eigen::MatrixBase<Rhs>& x) const {
    return Eigen::Product<MatrixReplacement,Rhs,Eigen::AliasFreeProduct>(*this, x.derived());
Luker's avatar
Luker committed
  }

Luker's avatar
Luker committed
  // Custom API:
  MatrixReplacement() : mp_mat(0) {}
Luker's avatar
Luker committed

Luker's avatar
Luker committed
  void attachMyMatrix(const SparseMatrix<double> &mat) {
    mp_mat = &mat;
Luker's avatar
Luker committed
  }
Luker's avatar
Luker committed
  const SparseMatrix<double> my_matrix() const { return *mp_mat; }
Luker's avatar
Luker committed

Luker's avatar
Luker committed
private:
  const SparseMatrix<double> *mp_mat;
Luker's avatar
Luker committed
};


Luker's avatar
Luker committed
// Implementation of MatrixReplacement * Eigen::DenseVector though a specialization of internal::generic_product_impl:
Luker's avatar
Luker committed
namespace Eigen {
namespace internal {

Luker's avatar
Luker committed
  template<typename Rhs>
  struct generic_product_impl<MatrixReplacement, Rhs, SparseShape, DenseShape, GemvProduct> // GEMV stands for matrix-vector
  : generic_product_impl_base<MatrixReplacement,Rhs,generic_product_impl<MatrixReplacement,Rhs> >
Luker's avatar
Luker committed
  {
Luker's avatar
Luker committed
    typedef typename Product<MatrixReplacement,Rhs>::Scalar Scalar;

    template<typename Dest>
    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<lhs.cols(); ++i)
        dst += rhs(i) * lhs.my_matrix().col(i);
    }
  };
Luker's avatar
Luker committed

}
}

int main()
{
Luker's avatar
Luker committed
  int n = 10;
  Eigen::SparseMatrix<double> S = Eigen::MatrixXd::Random(n,n).sparseView(0.5,1);
  S = S.transpose()*S;

Luker's avatar
Luker committed
  MatrixReplacement A;
Luker's avatar
Luker committed
  A.attachMyMatrix(S);
Luker's avatar
Luker committed

Luker's avatar
Luker committed
  Eigen::VectorXd b(n), x;
  b.setRandom();
Luker's avatar
Luker committed

Luker's avatar
Luker committed
  // Solve Ax = b using various iterative solver with matrix-free version:
  {
    Eigen::ConjugateGradient<MatrixReplacement, Eigen::Lower|Eigen::Upper, Eigen::IdentityPreconditioner> cg;
    cg.compute(A);
    x = cg.solve(b);
    std::cout << "CG:       #iterations: " << cg.iterations() << ", estimated error: " << cg.error() << std::endl;
  }

  {
    Eigen::BiCGSTAB<MatrixReplacement, Eigen::IdentityPreconditioner> bicg;
    bicg.compute(A);
    x = bicg.solve(b);
    std::cout << "BiCGSTAB: #iterations: " << bicg.iterations() << ", estimated error: " << bicg.error() << std::endl;
  }

  {
    Eigen::GMRES<MatrixReplacement, Eigen::IdentityPreconditioner> gmres;
    gmres.compute(A);
    x = gmres.solve(b);
    std::cout << "GMRES:    #iterations: " << gmres.iterations() << ", estimated error: " << gmres.error() << std::endl;
  }
Luker's avatar
Luker committed

Luker's avatar
Luker committed
  {
    Eigen::DGMRES<MatrixReplacement, Eigen::IdentityPreconditioner> gmres;
    gmres.compute(A);
    x = gmres.solve(b);
    std::cout << "DGMRES:   #iterations: " << gmres.iterations() << ", estimated error: " << gmres.error() << std::endl;
  }
Luker's avatar
Luker committed

Luker's avatar
Luker committed
  {
    Eigen::MINRES<MatrixReplacement, Eigen::Lower|Eigen::Upper, Eigen::IdentityPreconditioner> minres;
    minres.compute(A);
    x = minres.solve(b);
    std::cout << "MINRES:   #iterations: " << minres.iterations() << ", estimated error: " << minres.error() << std::endl;
  }
Luker's avatar
Luker committed
}