qr_decomposition.hpp

Back to QR decomposition

pastel/math/matrix/qr_decomposition/

#ifndef PASTELMATH_QR_DECOMPOSITION_HPP
#define PASTELMATH_QR_DECOMPOSITION_HPP

#include "pastel/math/matrix/qr_decomposition.h"
#include "pastel/math/matrix/householder_reflection.h"

#include "pastel/sys/vector/vector_tools.h"

namespace Pastel
{

    template <typename Real, int M, int N>
    QrDecomposition<Real, M, N>::QrDecomposition(integer m, integer n)
        : q_(m, m)
        , r_(m, n)
    {
        ENSURE_OP(m, >=, 0);
        ENSURE_OP(n, >=, 0);
        q_ = Matrix<Real, M, N>::Identity(m, m);
        r_ = Matrix<Real, M, N>::Identity(m, m);
    }

    template <typename Real, int M, int N>
    QrDecomposition<Real, M, N>::QrDecomposition(
        const MatrixView<Real, M, N>& that)
        : q_()
        , r_()
    {
        q_ = Matrix<Real, M, N>::Identity(that.rows(), that.rows());
        r_ = asMatrix(that);
        decompose();
    }

    template <typename Real, int M, int N>
    QrDecomposition<Real, M, N>::QrDecomposition(
        const QrDecomposition& that)
        : q_(that.q_)
        , r_(that.r_)
    {
    }

    template <typename Real, int M, int N>
    QrDecomposition<Real, M, N>::QrDecomposition(
        QrDecomposition&& that)
        : q_(that.m(), that.m())
        , r_(that.m(), that.n())
    {
        swap(that);
    }

    template <typename Real, int M, int N>
    integer QrDecomposition<Real, M, N>::m() const
    {
        return q_.rows();
    }

    template <typename Real, int M, int N>
    integer QrDecomposition<Real, M, N>::n() const
    {
        return r_.cols();
    }

    template <typename Real, int M, int N>
    void QrDecomposition<Real, M, N>::swap(
        QrDecomposition& that)
    {
        q_.swap(that.q_);
        r_.swap(that.r_);
    }

    template <typename Real, int M, int N>
    QrDecomposition<Real, M, N>& QrDecomposition<Real, M, N>::operator=(
        QrDecomposition that)
    {
        swap(that);
        return *this;
    }

    template <typename Real, int M, int N>
    MatrixView<const Real, M, M> QrDecomposition<Real, M, N>::qTransposed() const
    {
        return view(q_);
    }

    template <typename Real, int M, int N>
    MatrixView<const Real, M, N> QrDecomposition<Real, M, N>::r() const
    {
        return view(r_);
    }

    // Private

    template <typename Real, int M, int N>
    void QrDecomposition<Real, M, N>::decompose()
    {
        /*
       QR decomposition
       ================
       
       Let
       alpha = -sign(x_1) |x|
       v = x - alpha e_1
       Q = I - 2vv^T.
       
       Then
       Qx = alpha e_1
       
       Given a matrix A, let its first column be x.
       Then QA has as its first column alpha e_1.
       Recurse by consider the minor you get by
       removing the first column and row from QA.
       
       In the end you end up with:

       Q_n ... Q_2 Q_1 A = R

       such that Q_i are orthogonal matrices
       and R is an upper triangular matrix.
       Here the Q_i have been augmented from minor
       Householder reflection k x k matrices to full 
       n x n orthogonal matrices by adding a leading 
       identity matrix.
       
       Denote
       Q' = (Q_n ... Q_2 Q_1)^-1 = Q_1^T  ... Q_n^T

       Then
       A = Q' R

       The trick to efficient implementation is to
       take advantage of the special form of the
       Householder reflection as well as that
       we can consider the minors recursively.
       
       At each multiplication with a Householder
       matrix with x some column of the current
       minor:

       x' = (I - 2vv^T / (v^T v))x
       = x - 2v (v^T x) / (v^T v)
       = x - c v <v, x>

       where
       c = 2 / (v^T v)

       We can trace the Q' matrix at the same
       time, if we start from the identity matrix and
       apply the same transformations to that
       as well.
       */

        integer m = r_.rows();
        integer n = r_.cols();

        // We will reuse the memory space for v
        // to avoid reallocation.
        Vector<Real> v(ofDimension(m));

        for (integer j = 0;j < n - 1;++j)
        {
            // Consider the (m - j) x (n - j) minor at
            // the bottom right.

            // Note that for each j we only use
            // the components v[j], ..., v[m - 1] of v.
            for (integer i = j;i < m;++i)
            {
                v[i] = r_(i, j);
            }

            // Compute R := Q_{j + 1} ... Q_1 A.
            householderInplace(view(r_), v, j, j);
            // Compute Q^T := Q_{j + 1} ... Q_1.
            householderInplace(view(q_), v, j, 0);
        }
    }

}

#include "pastel/math/matrix/matrix_diagonal_product.h"
#include "pastel/math/matrix/solve_linear.h"

namespace Pastel
{

    //! Computes the absolute determinant from a qr decomposition.
    /*!
   The determinant of the orthogonal matrix is either -1 or 1.
   It would be costly to find out which. However, the
   absolute value of the determinant can be found quickly
   from the upper triangular matrix.
   */
    template <typename Real, int M, int N>
    Real absDeterminant(
        const QrDecomposition<Real, M, N>& qr)
    {
        return abs(diagonalProduct(qr.r()));
    }

    //! Solves the linear system QRx = b.
    template <
        typename Real, int M, int N,
        typename Real_b, int N_b
    >
    requires
        IsPlain<Real> &&
        IsSameObject<Real, Real_b> &&
        IsEqualDim<N, N_b>
    Vector<Real> solveLinear(
        const QrDecomposition<Real, M, N>& qr,
        const Vector<Real_b, N_b>& b)
    {
        ENSURE_OP(qr.n(), ==, b.size());

        integer n = b.size();

        // QRx = b

        Vector<Real> x(ofDimension(n));

        // First solve Qy = b.

        x = asMatrix(qr.qTransposed()) * b;

        // Then solve Rx = y.
        x = solveUpperTriangular(qr.r(), x);

        return x;
    }

}

#endif