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}