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 org.openimaj.math.matrix; 031 032import no.uib.cipr.matrix.DenseMatrix; 033import no.uib.cipr.matrix.DenseVector; 034 035import org.netlib.lapack.LAPACK; 036import org.netlib.util.intW; 037import org.openimaj.util.array.ArrayUtils; 038import org.openimaj.util.pair.IndependentPair; 039 040import Jama.Matrix; 041 042/** 043 * Methods for solving the Generalised Eigenvalue Problem: A x = L B x. 044 * <p> 045 * Internally the methods in this class use LAPACK to compute the solutions. 046 * 047 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 048 * 049 */ 050public class GeneralisedEigenvalueProblem { 051 private final static int sygvd(int itype, String jobz, String uplo, DenseMatrix A, DenseMatrix B, DenseVector W) { 052 int info = dsygvd(itype, jobz, uplo, A.numColumns(), A.getData(), A.numRows(), B.getData(), B.numRows(), W.getData()); 053 if (info == 0) { 054 return 0; 055 } else { 056 if (info < 0) throw new IllegalArgumentException("LAPACK ERROR: DSYGVD returned " + info); 057 throw new RuntimeException("LAPACK ERROR: DSYGVD returned " + info); 058 } 059 } 060 061 private final static int dsygvd(int itype, String jobz, String uplo, int n, double[] a, int lda, double[] b, int ldb, double[] w) { 062 double[] work = new double[1]; 063 double[] tmp = new double[1]; 064 intW info = new intW(-1); 065 int lwork; 066 int[] iwork = new int[1]; 067 int liwork; 068 069 //call with info=-1 to determine size of working space 070 LAPACK.getInstance().dsygvd(itype, jobz, uplo, n, tmp, lda, tmp, ldb, tmp, work, -1, iwork, 0, info); 071 072 if (info.val != 0) 073 return info.val; //an error occurred 074 075 //setup working space 076 lwork = (int) work[0]; 077 work = new double[lwork]; 078 liwork = (int) iwork[0]; 079 iwork = new int[liwork]; 080 //and do the work 081 LAPACK.getInstance().dsygvd(itype, jobz, uplo, n, a, lda, b, ldb, w, work, lwork, iwork, liwork, info); 082 083 return info.val; 084 } 085 086 /** 087 * Compute the generalised eigenvalues, L, of the problem A x = L B x. 088 * The returned eigenvalues are not ordered. 089 * 090 * @param A symmetric Matrix A; only the upper triangle is used. 091 * @param B symmetric Matrix B; only the upper triangle is used. 092 * @return the eigenvalues L. 093 */ 094 public static DenseVector symmetricGeneralisedEigenvalues(DenseMatrix A, DenseMatrix B) { 095 if (!A.isSquare() || !B.isSquare()) 096 throw new IllegalArgumentException("Input matrices must be square"); 097 098 DenseVector W = new DenseVector(A.numRows()); 099 sygvd(1, "N", "U", A.copy(), B.copy(), W); 100 101 return W; 102 } 103 104 /** 105 * Solve the general problem A x = L B x. 106 * The returned eigenvalues are not ordered. 107 * 108 * @param A symmetric matrix A 109 * @param B symmetric matrix B 110 * @return The eigenvectors x and eigenvalues L. 111 */ 112 public static IndependentPair<DenseMatrix, DenseVector> symmetricGeneralisedEigenvectors(DenseMatrix A, DenseMatrix B) { 113 if (!A.isSquare() || !B.isSquare()) 114 throw new IllegalArgumentException("Input matrices must be square"); 115 116 DenseMatrix vecs = A.copy(); 117 DenseVector W = new DenseVector(A.numRows()); 118 119 sygvd(1, "V", "U", vecs, B.copy(), W); 120 121 return new IndependentPair<DenseMatrix, DenseVector>(vecs, W); 122 } 123 124 /** 125 * Compute the generalised eigenvalues, L, of the problem A x = L B x. 126 * The returned eigenvalues are not ordered. 127 * 128 * @param A symmetric Matrix A; only the upper triangle is used. 129 * @param B symmetric Matrix B; only the upper triangle is used. 130 * @return the eigenvalues L. 131 */ 132 public static double[] symmetricGeneralisedEigenvalues(Matrix A, Matrix B) { 133 if ((A.getRowDimension() != A.getColumnDimension()) || (B.getRowDimension() != B.getColumnDimension())) 134 throw new IllegalArgumentException("Input matrices must be square"); 135 136 DenseVector W = new DenseVector(A.getRowDimension()); 137 sygvd(1, "N", "U", new DenseMatrix(A.getArray()), new DenseMatrix(B.getArray()), W); 138 139 return W.getData(); 140 } 141 142 /** 143 * Solve the general problem A x = L B x. 144 * The returned eigenvalues are not ordered. 145 * 146 * @param A symmetric matrix A 147 * @param B symmetric matrix B 148 * @return The eigenvectors x and eigenvalues L. 149 */ 150 public static IndependentPair<Matrix, double[]> symmetricGeneralisedEigenvectors(Matrix A, Matrix B) { 151 if ((A.getRowDimension() != A.getColumnDimension()) || (B.getRowDimension() != B.getColumnDimension())) 152 throw new IllegalArgumentException("Input matrices must be square"); 153 154 int dim = A.getRowDimension(); 155 DenseMatrix vecs = new DenseMatrix(A.getArray()); 156 DenseVector W = new DenseVector(dim); 157 158 sygvd(1, "V", "U", vecs, new DenseMatrix(B.getArray()), W); 159 160 Matrix evecs = new Matrix(dim, dim); 161 final double[][] evecsData = evecs.getArray(); 162 final double[] vecsData = vecs.getData(); 163 for (int r=0; r<dim; r++) 164 for (int c=0; c<dim; c++) 165 evecsData[r][c] = vecsData[r + c * dim]; 166 167 return new IndependentPair<Matrix, double[]>(evecs, W.getData()); 168 } 169 170 /** 171 * Solve the general problem A x = L B x. 172 * The returned eigenvalues ordered in a decreasing manner. 173 * 174 * @param A symmetric matrix A 175 * @param B symmetric matrix B 176 * @return The eigenvectors x and eigenvalues L. 177 */ 178 public static IndependentPair<Matrix, double[]> symmetricGeneralisedEigenvectorsSorted(Matrix A, Matrix B) { 179 if ((A.getRowDimension() != A.getColumnDimension()) || (B.getRowDimension() != B.getColumnDimension())) 180 throw new IllegalArgumentException("Input matrices must be square"); 181 182 int dim = A.getRowDimension(); 183 DenseMatrix vecs = new DenseMatrix(A.getArray()); 184 DenseVector W = new DenseVector(dim); 185 186 sygvd(1, "V", "U", vecs, new DenseMatrix(B.getArray()), W); 187 188 Matrix evecs = new Matrix(dim, dim); 189 final double[][] evecsData = evecs.getArray(); 190 final double[] vecsData = vecs.getData(); 191 final double[] valsData = W.getData(); 192 193 int [] indices = ArrayUtils.range(0, valsData.length-1); 194 ArrayUtils.parallelQuicksortDescending(valsData, indices); 195 196 for (int r=0; r<dim; r++) 197 for (int c=0; c<dim; c++) 198 evecsData[r][c] = vecsData[r + indices[c] * dim]; 199 200 return new IndependentPair<Matrix, double[]>(evecs, valsData); 201 } 202 203 /** 204 * Solve the general problem A x = L B x. 205 * The returned eigenvalues ordered in a decreasing manner. Only the 206 * top numVecs eigenvalues and vectors are returned. 207 * 208 * @param A symmetric matrix A 209 * @param B symmetric matrix B 210 * @param numVecs number of eigenvalues/eigenvectors 211 * @return The eigenvectors x and eigenvalues L. 212 */ 213 public static IndependentPair<Matrix, double[]> symmetricGeneralisedEigenvectorsSorted(Matrix A, Matrix B, int numVecs) { 214 if ((A.getRowDimension() != A.getColumnDimension()) || (B.getRowDimension() != B.getColumnDimension())) 215 throw new IllegalArgumentException("Input matrices must be square"); 216 217 int dim = A.getRowDimension(); 218 DenseMatrix vecs = new DenseMatrix(A.getArray()); 219 DenseVector W = new DenseVector(dim); 220 221 sygvd(1, "V", "U", vecs, new DenseMatrix(B.getArray()), W); 222 223 Matrix evecs = new Matrix(dim, numVecs); 224 final double[][] evecsData = evecs.getArray(); 225 final double[] vecsData = vecs.getData(); 226 final double[] valsData = W.getData(); 227 228 int [] indices = ArrayUtils.range(0, valsData.length-1); 229 ArrayUtils.parallelQuicksortDescending(valsData, indices); 230 231 for (int r=0; r<dim; r++) 232 for (int c=0; c<numVecs; c++) 233 evecsData[r][c] = vecsData[r + indices[c] * dim]; 234 235 return new IndependentPair<Matrix, double[]>(evecs, valsData); 236 } 237}