View Raw SPL
/*****************************************************************************
*                                                                            *
*   PINV.SPL     Copyright (C) 2001 DSP Development Corporation              *
*                               All Rights Reserved                          *
*                                                                            *
*   Author:      Randy Race                                                  *
*                                                                            *
*   Synopsis:    Calculates the pseudo inverse of a matrix                   *
*                                                                            *
*   Revisions:   24 Jul 2001  RRR  Creation                                  *
*                                                                            *
*****************************************************************************/

#include 

#if @HELP_PINV

    PINV

    Purpose: Calculates the pseudo inverse of a matrix using SVD

    Syntax:  PINV(a, tol)

                  a - input array

                tol - optional real, singular value tolerance, defaults to
                      (max of rows or cols) * max(singular values) * eps


    Returns: An array, P, the pseudo inverse of the input matrix a, such that
             a *^ P ^* a == a.

    Example:
             a = {{1, 4, 7},
                  {2, 5, 8},
                  {3, 6, 9}}


             P = pinv(a)

             P == {{-0.638889, -0.055556,  0.527778},
                   {-0.166667,  0.000000,  0.166667},
                   { 0.305556,  0.055556, -0.194444}}


             a *^ P *^ a == {{1, 4, 7},
                             {2, 5, 8},
                             {3, 6, 9}}


             P *^ a *^ P == {{-0.638889, -0.055556,  0.527778},
                             {-0.166667,  0.000000,  0.166667},
                             { 0.305556,  0.055556, -0.194444}}

    Remarks:
             PINV use SVD to compute the pseudo inverse. The pseudo inverse
             P, of a matrix m has the same size as transpose(m) such that:

             m *^ P *^ m == m

             P *^ m *^ P == P

    See Also:
             *^
             Norm
             Rank
             Svd
#endif


/* calculate pseudo inverse of a matrix */
pinv(a, tol)
{
        local u, w, v, nr, nc, r, P;

        if (argc < 1) error("pinv - input array required");

        (nr, nc) = size(a);

        /* compact svd decomposition */
        (u, w, v) = svd(a, 0);

        if (argc < 2)
        {
                /* use rank as tolerance */
                tol = maxval(nr, nc) * w[1] * eps;
        }

        /* find rank */
        r = sum(w > tol);

        if (r == 0)
        {
                /* no inverse */
                P = zeros(nc, nr);
        }
        else
        {
                /* SVD inverse */
                w = diag(w);
                w = diag(1 / w[1..r]);
                P = v[.., 1..r] *^ (w *^ conj(transpose(u[.., 1..r])));
        }
        
        return(P);
}