Program Listing for File MatrixFreeSolver.h

Return to documentation for file (SPlisHSPlasH/Utilities/MatrixFreeSolver.h)

#ifndef __MatrixFreeSolver_H__
#define __MatrixFreeSolver_H__

#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/Sparse>

using SystemMatrixType = Eigen::SparseMatrix<Real>;

namespace SPH
{
    class MatrixReplacement;
}

namespace Eigen
{
    namespace internal
    {
        template<> struct traits<SPH::MatrixReplacement> : public Eigen::internal::traits<SystemMatrixType> {};
    }
}

namespace SPH
{
    class MatrixReplacement : public Eigen::EigenBase<MatrixReplacement>
    {
    public:
        // Required typedefs, constants, and method:
        typedef Real Scalar;
        typedef Real RealScalar;
        typedef int StorageIndex;
        typedef void(*MatrixVecProdFct) (const Real*, Real*, void *);

        enum
        {
            ColsAtCompileTime = Eigen::Dynamic,
            MaxColsAtCompileTime = Eigen::Dynamic,
            IsRowMajor = false
        };

        Index rows() const { return m_dim; }
        Index cols() const { return m_dim; }

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

        MatrixReplacement(const unsigned int dim, MatrixVecProdFct fct, void *userData) : m_dim(dim), m_matrixVecProdFct(fct), m_userData(userData) {}
        void * getUserData() { return m_userData; }
        MatrixVecProdFct getMatrixVecProdFct() { return m_matrixVecProdFct; }

    protected:
        unsigned int m_dim;
        void *m_userData;
        MatrixVecProdFct m_matrixVecProdFct;
    };


    class JacobiPreconditioner1D
    {
    public:
        typedef typename SystemMatrixType::StorageIndex StorageIndex;
        typedef void(*DiagonalMatrixElementFct) (const unsigned int, Real&, void *);

        enum {
            ColsAtCompileTime = Eigen::Dynamic,
            MaxColsAtCompileTime = Eigen::Dynamic
        };

        JacobiPreconditioner1D() {}

        void init(const unsigned int dim, DiagonalMatrixElementFct fct, void *userData)
        {
            m_dim = dim; m_diagonalElementFct = fct; m_userData = userData;
        }

        Eigen::Index rows() const { return m_dim; }
        Eigen::Index cols() const { return m_dim; }

        Eigen::ComputationInfo info() { return Eigen::Success; }

        template<typename MatType>
        JacobiPreconditioner1D& analyzePattern(const MatType&) { return *this; }

        template<typename MatType>
        JacobiPreconditioner1D& factorize(const MatType& mat) { return *this; }

        template<typename MatType>
        JacobiPreconditioner1D& compute(const MatType& mat)
        {
            m_invDiag.resize(m_dim);
            #pragma omp parallel default(shared)
            {
                #pragma omp for schedule(static)
                for (int i = 0; i < (int)m_dim; i++)
                {
                    Real res;
                    m_diagonalElementFct(i, res, m_userData);
                    m_invDiag[i] = static_cast<Real>(1.0) / res;
                }
            }
            return *this;
        }

        template<typename Rhs, typename Dest>
        void _solve_impl(const Rhs& b, Dest& x) const
        {
            x = m_invDiag.array() * b.array();
        }

        template<typename Rhs>
        inline const Eigen::Solve<JacobiPreconditioner1D, Rhs> solve(const Eigen::MatrixBase<Rhs>& b) const
        {
            return Eigen::Solve<JacobiPreconditioner1D, Rhs>(*this, b.derived());
        }

    protected:
        unsigned int m_dim;
        DiagonalMatrixElementFct m_diagonalElementFct;
        void *m_userData;
        VectorXr m_invDiag;
    };

    class JacobiPreconditioner3D
    {
    public:
        typedef typename SystemMatrixType::StorageIndex StorageIndex;
        typedef void(*DiagonalMatrixElementFct) (const unsigned int, Vector3r&, void *);

        enum {
            ColsAtCompileTime = Eigen::Dynamic,
            MaxColsAtCompileTime = Eigen::Dynamic
        };

        JacobiPreconditioner3D() {}

        void init(const unsigned int dim, DiagonalMatrixElementFct fct, void *userData)
        {
            m_dim = dim; m_diagonalElementFct = fct; m_userData = userData;
        }

        Eigen::Index rows() const { return 3*m_dim; }
        Eigen::Index cols() const { return 3*m_dim; }

        Eigen::ComputationInfo info() { return Eigen::Success; }

        template<typename MatType>
        JacobiPreconditioner3D& analyzePattern(const MatType&) { return *this; }

        template<typename MatType>
        JacobiPreconditioner3D& factorize(const MatType& mat) { return *this; }

        template<typename MatType>
        JacobiPreconditioner3D& compute(const MatType& mat)
        {
            m_invDiag.resize(m_dim*3);
            #pragma omp parallel default(shared)
            {
                #pragma omp for schedule(static)
                for (int i = 0; i < (int)m_dim; i++)
                {
                    Vector3r res;
                    m_diagonalElementFct(i, res, m_userData);
                    m_invDiag[3*i] = static_cast<Real>(1.0) / res[0];
                    m_invDiag[3*i+1] = static_cast<Real>(1.0) / res[1];
                    m_invDiag[3*i+2] = static_cast<Real>(1.0) / res[2];
                }
            }
            return *this;
        }

        template<typename Rhs, typename Dest>
        void _solve_impl(const Rhs& b, Dest& x) const
        {
            x = m_invDiag.array() * b.array();
        }

        template<typename Rhs>
        inline const Eigen::Solve<JacobiPreconditioner3D, Rhs> solve(const Eigen::MatrixBase<Rhs>& b) const
        {
            return Eigen::Solve<JacobiPreconditioner3D, Rhs>(*this, b.derived());
        }

    protected:
        unsigned int m_dim;
        DiagonalMatrixElementFct m_diagonalElementFct;
        void *m_userData;
        VectorXr m_invDiag;
    };

    class BlockJacobiPreconditioner3D
    {
    public:
        typedef typename SystemMatrixType::StorageIndex StorageIndex;
        typedef void(*DiagonalMatrixElementFct) (const unsigned int, Matrix3r&, void *);

        enum {
            ColsAtCompileTime = Eigen::Dynamic,
            MaxColsAtCompileTime = Eigen::Dynamic
        };

        BlockJacobiPreconditioner3D() {}

        void init(const unsigned int dim, DiagonalMatrixElementFct fct, void *userData)
        {
            m_dim = dim; m_diagonalElementFct = fct; m_userData = userData;
        }

        Eigen::Index rows() const { return 3 * m_dim; }
        Eigen::Index cols() const { return 3 * m_dim; }

        Eigen::ComputationInfo info() { return Eigen::Success; }

        template<typename MatType>
        BlockJacobiPreconditioner3D& analyzePattern(const MatType&) { return *this; }

        template<typename MatType>
        BlockJacobiPreconditioner3D& factorize(const MatType& mat) { return *this; }

        template<typename MatType>
        BlockJacobiPreconditioner3D& compute(const MatType& mat)
        {
            m_invDiag.resize(m_dim);
            #pragma omp parallel default(shared)
            {
                #pragma omp for schedule(static)
                for (int i = 0; i < (int)m_dim; i++)
                {
                    Matrix3r res;
                    m_diagonalElementFct(i, res, m_userData);
                    m_invDiag[i] = res.inverse();
                }
            }
            return *this;
        }

        template<typename Rhs, typename Dest>
        void _solve_impl(const Rhs& b, Dest& x) const
        {
            #pragma omp parallel default(shared)
            {
                #pragma omp for schedule(static)
                for (int i = 0; i < (int)m_dim; i++)
                {
                    static_cast<VectorXr&>(x).block<3, 1>(3 * i, 0) = m_invDiag[i] * static_cast<const VectorXr&>(b).block<3, 1>(3 * i, 0);
                }
            }
        }

        template<typename Rhs>
        inline const Eigen::Solve<BlockJacobiPreconditioner3D, Rhs> solve(const Eigen::MatrixBase<Rhs>& b) const
        {
            return Eigen::Solve<BlockJacobiPreconditioner3D, Rhs>(*this, b.derived());
        }

    protected:
        unsigned int m_dim;
        DiagonalMatrixElementFct m_diagonalElementFct;
        void *m_userData;
        std::vector<Matrix3r> m_invDiag;
    };
}

namespace Eigen
{
    namespace internal
    {
        template<typename Rhs>
        struct generic_product_impl<SPH::MatrixReplacement, Rhs, SparseShape, DenseShape, GemvProduct> // GEMV stands for generic matrix-vector
            : generic_product_impl_base<SPH::MatrixReplacement, Rhs, generic_product_impl<SPH::MatrixReplacement, Rhs> >
        {
            typedef typename Product<SPH::MatrixReplacement, Rhs>::Scalar Scalar;

            template<typename Dest>
            static void scaleAndAddTo(Dest& dst, const SPH::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");

                const Real *vec = &rhs(0);
                Real *res = &dst(0);
                SPH::MatrixReplacement& lhs_ = const_cast<SPH::MatrixReplacement&>(lhs);
                lhs_.getMatrixVecProdFct()(vec, res, lhs_.getUserData());
            }
        };
    }
}


#endif