I found a better solution and implemented it.

The solution is based on the following.  Compute the singular value
decomposition of the matrix, let this be U*S*V^T (U and V are
orthogonal, S is diagonal, ^T means transposition).  Now x*U*U^T is
the projection of vector x to the row space of the matrix.

I use the GSL library to compute the singular value decomposition.
One could use LAPACK instead, which would have the advantage that
LAPACK has svd implemented for complex matrices as well.

In my question I asked the solution to work even if the matrix is not
of complete rank.  This might not be a reasonable request, for such a
case is very instable in the matrix.  This solution nevertheless
attempts to honor this request using tolerant comparisions of J.

Let's see the code -- C wrapper first, then J code.


$ cat svdwrap.c
#include <gsl/gsl_vector_double.h>
#include <gsl/gsl_matrix_double.h>
#include <gsl/gsl_linalg.h>

/*
wrap_svd_decompose(m, n, A, S, V)
Performs SVD decomposition on matrix of doubles.
In condition: m <= n.
A is input matrix, size m x n,
U is output matrix, same place as A, size m x n,
S is output vector, size n x n,
V is output matrix, size n x n.
Out condition: U and V are orthogonal matrices (ie. U*U^T=I),
S is all nonnegative and sorted non-increasing,
A = U*diag(S)*V^T
*/
int
wrap_svd_decompose(
        int zm, int zn, double *ac, double *sc, double *vc
) {
        if (zn < 0 || zm < zn)
                return 1;
        gsl_matrix_view ai = gsl_matrix_view_array(ac, zm, zn);
        gsl_vector_view si = gsl_vector_view_array(sc, zn);
        gsl_matrix_view vi = gsl_matrix_view_array(vc, zn, zn);
        gsl_vector *wv = gsl_vector_alloc(zn);
        int err = gsl_linalg_SV_decomp(&ai.matrix, &vi.matrix, &si.vector, wv);
        gsl_vector_free(wv);
        return err;
}

$ gcc -lgsl -lgslcblas -Wall -O -fPIC -shared -o svdwrap.so svdwrap.c
$ cat svd.ijs
NB. singular value decomposition
svd=: 3 :0"2
tr=. 0
if. 2>#$y do. 13!:8]3 end.
if. tr=. </$y do. y=. |: y end.
'mz nz'=: $y
p=. mz;nz;y;(i.nz);(i.,~nz)
'err mr nr ut si vt'=. './svdwrap.so wrap_svd_decompose i i i *d *d *d' 15!:0 p
if. err do. 13!:8]10 end.
if. tr do. vt;si;ut else. ut;si;vt end.
)
svd_tol=: 3 :0"2
'ut si vt'=.svd y
mb=. ({.<{.+])si
(mb#"1 ut);(mb#si);(mb#"1 vt)
)

NB. orthogonal projection
oproj=: 4 :0
ut=. >{.svd_tol |:y
(x +/ .* ut) +/ .* |: ut
)

$ j701console # j701_64 on amd64-linux
   0!:0<'svd.ijs'
   NB. test svd
   NB.a=: 4 _9 _4 ,: 4 _3 _2
   NB.a=: 0 1 2,:0 1 2
   NB.a=: 10-?4 10$21
   a=: 10-?10 4$21
   'ut si vt'=: svd_tol a
   NB.echo a;ut;si;vt
   NB. following four numbers must be small
   >./|,(=i.#{.ut)- ut+/ .*~|:ut
6.66134e_16
   >./|,(=i.#{.vt)- vt+/ .*~|:vt
6.66134e_16
   -.*./2>:/\si
0
   >./|,a- ut+/ .*si*|:vt
7.10543e_15

   NB. test orthogonal projection
   ve=: _1 _7 12
   sp=: 4 _9 _4 ,: 4 _3 _2
   ve oproj sp
_4 _3 _4.71845e_16

   sp=: 10-?4 10 $ 21
   ve=: 10-?10 $ 21
   NB. following three should be equal
   ve oproj sp
1.08054 _6.34628 1.53816 _1.43236 0.630816 _4.42371 _1.54375 _1.3119
_4.15668 1.93827
   ve (] +/ .* %.) |: sp
1.08054 _6.34628 1.53816 _1.43236 0.630816 _4.42371 _1.54375 _1.3119
_4.15668 1.93827
   ve ([-[:{.[:((],~[-[:+/+/ .*~*(%+/&:*:"1)@]),:^:(2>#...@$))/,) sp
1.08054 _6.34628 1.53816 _1.43236 0.630816 _4.42371 _1.54375 _1.3119
_4.15668 1.93827

   exit 0
$


--
Ambrus
----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to