matrix_eigen.hpp

Back to Matrix algorithms

pastel/math/matrix/

#ifndef PASTELMATH_MATRIX_EIGEN_HPP
#define PASTELMATH_MATRIX_EIGEN_HPP

#include "pastel/math/matrix/matrix_eigen.h"
#include "pastel/math/matrix/matrix_trace.h"
#include "pastel/math/matrix/matrix_determinant.h"

#include "pastel/sys/math_functions.h"

namespace Pastel
{

    template <typename Real>
    Vector<Real, 2> symmetricEigenValues(
        const Matrix<Real>& matrix)
    {
        // Let the matrix be
        //
        // [a b]
        // [c d]
        //
        // For 2 x 2 matrices we can solve the eigenvalues directly
        // from the characteristic equation:
        //
        // det [a - k     b] = 0
        //     [c     d - k]
        // =>
        // (a - k)(d - k) - bc = 0
        // =>
        // ad - k(a + d) + k^2 - bc = 0
        // =>
        // k^2 - k(a + d) + (ad - bc) = 0

        Vector<Real, 2> eigenValue;

        quadratic(
            1, -trace(matrix), determinant(matrix),
            eigenValue[0], eigenValue[1], true);

        return eigenValue;
    }

    template <typename Real>
    void symmetricEigenDecomposition(
        const Matrix<Real>& matrix,
        Matrix<Real>& eigenVector,
        Vector<Real, 2>& eigenValue)
    {
        eigenValue = symmetricEigenValues(matrix);

        // Because the eigenvalues are listed in ascending order,
        // the axis lengths are listed in descending order:
        // thus the eigenvalue corresponding to the longest axis
        // is in 'eigenValue[0]'.

        // If an eigenvalue k is given, then a corresponding
        // eigenvector is a [b, k - a] or [k - d, c].
        // We have to be careful for the case
        // in which both of these vectors are zero.
        // This happens precisely when the 'matrix' is
        // a multiple of the identity matrix.

        Vector<Real, 2> aCandidate(
            matrix(1, 0), eigenValue[0] - matrix(0, 0));

        if (dot(aCandidate) > square(0.001))
        {
            eigenVector[0] = aCandidate;
        }
        else
        {
            Vector<Real, 2> bCandidate(
                eigenValue[0] - matrix(1, 1), matrix(0, 1));
            if (dot(bCandidate) > square(0.001))
            {
                eigenVector[0] = bCandidate;
            }
            else
            {
                eigenVector[0].set(1, 0);
            }
        }

        // Because S is symmetric and real, the eigenvectors
        // must be orthogonal to each other.
        eigenVector[1] = cross(eigenVector[0]);
    }

}

#endif