cholesky_decomposition.h

Back to Cholesky decomposition

pastel/math/matrix/cholesky_decomposition/

// Description: Cholesky decomposition

#ifndef PASTELMATH_CHOLESKY_DECOMPOSITION_H
#define PASTELMATH_CHOLESKY_DECOMPOSITION_H

#include "pastel/math/matrix/matrix.h"
#include <cmath>

namespace Pastel
{

    //! Cholesky decomposition
    template <typename Real, int M = Dynamic, int N = Dynamic>
    requires
        IsPlain<Real>
    class CholeskyDecompositionInplace
    {
    public:
        CholeskyDecompositionInplace() = delete;
        CholeskyDecompositionInplace(const CholeskyDecompositionInplace& that) = default;
        CholeskyDecompositionInplace& operator=(const CholeskyDecompositionInplace& that) = default;
        ~CholeskyDecompositionInplace() = default;

        //! Constructs with the decomposition of the given matrix.
        /*!
       Preconditions:
       that.rows() == that.n()
       */
        CholeskyDecompositionInplace(const MatrixView<Real, M, N>& that)
            : cholesky_(that)
            , succeeded_(false)
        {
            ENSURE_OP(that.cols(), ==, that.rows());

            decompose();
        }

        //! Swaps with another decomposition.
        void swap(CholeskyDecompositionInplace& that)
        {
            cholesky_.swap(that.cholesky_);
            std::swap(succeeded_, that.succeeded_);
        }

        //! Returns the L matrix.
        MatrixView<const Real, M, N> lower() const
        {
            return cholesky_;
        }

        //! Returns whether the decomposition succeeded.
        /*!
       This decomposition succeeds if and only if the
       matrix is effectively symmetric positive-definite.
       */
        bool succeeded() const
        {
            return succeeded_;
        }

    private:
        bool decompose()
        {
            // See "Numerical Recipes: The art of scientific
            // computing", 3rd ed, section 2.9: Cholesky Decomposition.

            succeeded_ = false;

            integer n = cholesky_.cols();
            for (integer i = 0;i < n;++i)
            {
                for (integer j = 0;j < n;++j)
                {
                    Real sum = cholesky_(i, j);
                    for (integer k = i - 1;k >= 0;--k)
                    {

                        sum -= cholesky_(i, k) * cholesky_(j, k);
                    }
                    if (i == j)
                    {
                        if (sum <= 0)
                        {
                            // The matrix is not positive definite.
                            return succeeded_;
                        }
                        cholesky_(i, i) = std::sqrt(sum);
                    }
                    else
                    {
                        cholesky_(j, i) = sum / cholesky_(i, i);
                    }
                }
            }

            for (integer i = 0;i < n;++i)
            {
                for (integer j = 0;j < i;++j)
                {
                    cholesky_(j, i) = 0;
                }
            }

            succeeded_ = true;

            return succeeded_;
        }

        MatrixView<Real, M, N> cholesky_;
        bool succeeded_;
    };

}

namespace Pastel {

    template <typename Real, int M, int N>
    Real determinant(const CholeskyDecompositionInplace<Real, M, N>& that)
    {
        auto lower = that.lower();
        integer n = lower.cols();

        Real result = 1;
        for (integer i = 0;i < n;++i)
        {
            result *= square(lower(i, i));
        }

        return result;
    }

}

#endif