001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package no.uib.cipr.matrix; 031 032import org.netlib.util.intW; 033 034import com.github.fommil.netlib.LAPACK; 035 036/** 037 * Computes economy singular value decompositions. Uses DGESDD internally. 038 */ 039public class EconomySVD { 040 041 /** 042 * Work array 043 */ 044 private final double[] work; 045 046 /** 047 * Work array 048 */ 049 private final int[] iwork; 050 051 /** 052 * Matrix dimension 053 */ 054 private final int m, n; 055 056 /** 057 * The singular values 058 */ 059 private final double[] S; 060 061 /** 062 * Singular vectors 063 */ 064 private final DenseMatrix U, Vt; 065 066 /** 067 * Creates an empty SVD 068 * 069 * @param m 070 * Number of rows 071 * @param n 072 * Number of columns 073 */ 074 public EconomySVD(int m, int n) { 075 this.m = m; 076 this.n = n; 077 078 // Allocate space for the decomposition 079 S = new double[Math.min(m, n)]; 080 U = new DenseMatrix(Matrices.ld(m), Math.min(m, n)); 081 Vt = new DenseMatrix(Matrices.ld(Math.min(m, n)), n); 082 083 // Find workspace requirements 084 iwork = new int[8 * Math.min(m, n)]; 085 086 // Query optimal workspace 087 final double[] worksize = new double[1]; 088 final intW info = new intW(0); 089 LAPACK.getInstance().dgesdd(JobSVD.Part.netlib(), m, n, new double[0], 090 Matrices.ld(m), new double[0], new double[0], U.numRows, 091 new double[0], Vt.numRows, worksize, -1, iwork, info); 092 093 // Allocate workspace 094 int lwork = -1; 095 if (info.val != 0) { 096 // 'S' => LWORK >= min(M,N)*(6+4*min(M,N))+max(M,N) 097 lwork = Math.min(m, n) * (6 + 4 * Math.min(m, n)) + Math.max(m, n); 098 } else 099 lwork = (int) worksize[0]; 100 101 lwork = Math.max(lwork, 1); 102 work = new double[lwork]; 103 } 104 105 /** 106 * Convenience method for computing a full SVD 107 * 108 * @param A 109 * Matrix to decompose, not modified 110 * @return Newly allocated factorization 111 * @throws NotConvergedException 112 */ 113 public static EconomySVD factorize(Matrix A) throws NotConvergedException { 114 return new EconomySVD(A.numRows(), A.numColumns()).factor(new DenseMatrix(A)); 115 } 116 117 /** 118 * Computes an SVD 119 * 120 * @param A 121 * Matrix to decompose. Size must conform, and it will be 122 * overwritten on return. Pass a copy to avoid this 123 * @return The current decomposition 124 * @throws NotConvergedException 125 */ 126 public EconomySVD factor(DenseMatrix A) throws NotConvergedException { 127 if (A.numRows() != m) 128 throw new IllegalArgumentException("A.numRows() != m"); 129 else if (A.numColumns() != n) 130 throw new IllegalArgumentException("A.numColumns() != n"); 131 132 final intW info = new intW(0); 133 134 LAPACK.getInstance().dgesdd(JobSVD.Part.netlib(), m, n, 135 A.getData(), A.numRows, 136 S, 137 U.getData(), U.numRows, 138 Vt.getData(), Vt.numRows, 139 work, work.length, iwork, info); 140 141 if (info.val > 0) 142 throw new NotConvergedException( 143 NotConvergedException.Reason.Iterations); 144 else if (info.val < 0) 145 throw new IllegalArgumentException(); 146 147 return this; 148 } 149 150 /** 151 * Returns the left singular vectors, column-wise. Not available for partial 152 * decompositions 153 * 154 * @return Matrix of size m*m 155 */ 156 public DenseMatrix getU() { 157 return U; 158 } 159 160 /** 161 * Returns the right singular vectors, row-wise. Not available for partial 162 * decompositions 163 * 164 * @return Matrix of size n*n 165 */ 166 public DenseMatrix getVt() { 167 return Vt; 168 } 169 170 /** 171 * Returns the singular values (stored in descending order) 172 * 173 * @return Array of size min(m,n) 174 */ 175 public double[] getS() { 176 return S; 177 } 178 179}