views:

134

answers:

3

Hi All, I am using jama to calculate SVD. It work very good. If i pass square matrix. For example 2x2 or 3x3 etc. matrix. But when I pass some thing like this 2x3 or 4x8 it give error . I used all of their example. They have different constructor to perform the job. Also my second question is, I am usded 3x3 matrix and it gave

double[][] vals = {{1.,1.,0},{1.,0.,1.},{1.,3.,4.},{6.,4.,8.}};
  Matrix A = new Matrix(vals);

It produced following error:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3

After that I thaught to use another constructor that is as follow

double[][] vals = {{1.,1.,0,4},{1.,0.,1.,2},{1.,3.,4.,8},{1.,3.,4.,8}};
  Matrix A = new Matrix(vals,4,3);

It produced following output:

A = 
 1.0 1.0 0.0
 1.0 0.0 1.0
 1.0 3.0 4.0
 6.0 4.0 8.0

A = U S V^T

U = 
 0.078 -0.115 -0.963
 0.107 -0.281 0.260
 0.402 0.886 -0.018
 0.906 -0.351 0.060

Sigma = 
 11.861881 0.000000 0.000000
 0.000000 2.028349 0.000000
 0.000000 0.000000 1.087006

V = 
 0.507705 -0.795196 -0.331510
 0.413798 0.562579 -0.715735
 0.755650 0.226204 0.614675

rank = 3
condition number = 10.912437186202627
2-norm = 11.86188091889931
singular values = 
 11.861881 2.028349 1.087006

It worked for non square matrix. But it produced wrong results for svd because V and S doesn't have same rows=4 ( I am sorry if i couldn't analyze result properly as i am new for SVD) . Any idea? What should I do?

+1  A: 

Read the wiki article on SVD. The following code is representative of the example in Section 2.

import Jama.Matrix; 
import Jama.SingularValueDecomposition; 

public class JAMATest { 

    static public void printMatrix(Matrix m){
        double[][] d = m.getArray();

        for(int row = 0; row < d.length; row++){
            for(int col = 0; col < d[row].length; col++){
                System.out.printf("%6.4f\t", m.get(row, col));
            }
            System.out.println();
        }
        System.out.println();
    }

    public static void main(String[] args) { 
        double[][] vals = { {1., 0., 0., 0., 2.}, 
                            {0., 0., 3., 0., 0.}, 
                            {0., 0., 0., 0., 0.}, 
                            {0., 4., 0., 0., 0.} 
                          };  
        Matrix A = new Matrix(vals);         
        SingularValueDecomposition svd = new SingularValueDecomposition(A); 

        System.out.println("A = ");
        printMatrix(A);

        System.out.println("U = ");
        printMatrix(svd.getU());

        System.out.println("Sigma = ");
        printMatrix(svd.getS());

        System.out.println("V = ");
        printMatrix(svd.getV());
    } 
} 

and yields the outputL:

A = 
1.0000  0.0000  0.0000  0.0000  2.0000  
0.0000  0.0000  3.0000  0.0000  0.0000  
0.0000  0.0000  0.0000  0.0000  0.0000  
0.0000  4.0000  0.0000  0.0000  0.0000  

U = 
0.0000  0.0000  -1.0000 0.0000  
0.0000  1.0000  -0.0000 0.0000  
0.0000  0.0000  -0.0000 1.0000  
1.0000  0.0000  -0.0000 0.0000  

Sigma = 
4.0000  0.0000  0.0000  0.0000  0.0000  
0.0000  3.0000  0.0000  0.0000  0.0000  
0.0000  0.0000  2.2361  0.0000  0.0000  
0.0000  0.0000  0.0000  0.0000  0.0000  
0.0000  0.0000  0.0000  0.0000  0.0000  

V = 
0.0000  -0.0000 -0.4472 -0.8944 -0.0000 
0.0000  -0.0000 -0.0000 -0.0000 -0.0000 
0.0000  1.0000  -0.0000 -0.0000 -0.0000 
0.0000  -0.0000 -0.0000 -0.0000 1.0000  
1.0000  -0.0000 -0.8944 0.4472  -0.0000 

Hope this helps. Also, FWIW here is Matlab's output on the same problem:

>> A = [1.0000,  0.0000,  0.0000,  0.0000,  2.0000; 0, 0, 3, 0, 0; 0, 0, 0, 0, 0; 0, 4, 0, 0, 0];
>> A

A =

     1     0     0     0     2
     0     0     3     0     0
     0     0     0     0     0
     0     4     0     0     0

>> [U, S, V] = svd(A);
>> U

U =

     0     0     1     0
     0     1     0     0
     0     0     0    -1
     1     0     0     0

>> S

S =

    4.0000         0         0         0         0
         0    3.0000         0         0         0
         0         0    2.2361         0         0
         0         0         0         0         0

>> V

V =

         0         0    0.4472         0   -0.8944
    1.0000         0         0         0         0
         0    1.0000         0         0         0
         0         0         0    1.0000         0
         0         0    0.8944         0    0.4472

With regards to your first question, the following code produces no error:

import Jama.Matrix;

public class JAMATest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        double[][] vals = {{1.,1.,0},{1.,0.,1.},{1.,3.,4.},{6.,4.,8.}}; 
        Matrix A = new Matrix(vals); 

    }
}

So something else you're doing must be causing it to have an exception. Try using my printMatrix method in place of whatever you are using and see if it helps.

vicatcu
I am sorry, but i couldn't understand your answer. Do you think the output i wrote in my question is correct? How much V and S has 3 rows? Can you please explain? I am sorry i am not math expert :(
modifications to my previous program above. I included my printMatrix function. Note that I got a similar exception as you did when I tried using m.getRowDimension() and m.getColumnDimension() as they don't appear to be accurate for the svd.getU() method. Converting the matrix to a double[][] seems to do the trick.
vicatcu
On a separate note, the SVD of a matrix is not unique. See this quote from the wikipedia article: "It should also be noted that this particular singular value decomposition is not unique." When you multiply U * S * V you will get the original Matrix back. The dimensions of a matrix sort of "join" when you multiply. So our original matrix was [4 x 5] = [4 x 4] * [4 x 5] * [5 x 5], and you're left with the outer two dimensions ([4 ..x.. 5]). It seems in you have to *recognize* that only the first four rows of Sigma are the actual matrix based on these properties.
vicatcu
@agazerboy, As you can see from the Matlab and Java output, columns 3 and 4 of U in the Matlab output differ in sign from the Java output -- this is acceptable, U forms an orthornormal basis, and as such any column may be negated, and this is acceptable.
Mark E
A: 

The dimensions of U, S and V do not need to be the same dimensions as A. U will have the same number of rows and V^T will have the same number of columns. That is sufficient to recreate A by the rules of matrix multiplication.

The other dimension (columns of U, rows of V^T and rows/columns of S) will be the "rank" of A (in your example 3). This is, roughly speaking, the dimensionality of your data...how many axes are needed to uniquely represent a column or row in A. It will be at most min(rows, cols) but can often be much less. That is ok.

thekindamzkyoulike
+1  A: 

Be careful here, JAMA supports SVD primarily for full rank matrices, and if you read the "readme" you'll notice that the behavior is not necessarily correct for rank deficient (m < n) matrices.

In essence, what causes your ArrayIndexOutOfBounds exception is line 486 in SingularValueDecomposition:

return new Matrix(U,m,Math.min(m+1,n));

Changing this to:

return new Matrix(U);

will solve the problem. Ultimate what happens under the covers (at least for vicatcu's example) is that you inject a matrix with m=4 and n=5, but notice in the actual output U has dimensions m=4 and n=4. If you read the top of the SingularValueDecomposition class it states:

For an m-by-n matrix A with m >= n, the singular value decomposition is an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and an n-by-n orthogonal matrix V so that A = U*S*V'.

But this doesn't hold in this case, because m=4 and n=5 means m<n. So now since you're passing a rank deficient matrix U has different dimensions than the normal calling case of the SVD class, and as such the statement:

new Matrix(U, m, Math.min(m+1,n))

will create a matrix with assumed rows of m, here 4 (which is correct) and assumed columns n, here Math.min(4+1,5)=5 (which is incorrect). So: when you go print the matrix and the print routine calls getColumnDimension, the U matrix returns 5, which is greater than the actual backing array dimension.

In short, switching to the line I pasted above will detect the dimensions of U, and as such return a valid result regardless of the rank.

Mark E