1 /* 2 * This file is part of ELKI: 3 * Environment for Developing KDD-Applications Supported by Index-Structures 4 * 5 * Copyright (C) 2018 6 * ELKI Development Team 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Affero General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Affero General Public License for more details. 17 * 18 * You should have received a copy of the GNU Affero General Public License 19 * along with this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 package de.lmu.ifi.dbs.elki.math.linearalgebra; 22 23 import static de.lmu.ifi.dbs.elki.math.linearalgebra.VMath.*; 24 25 import java.util.Arrays; 26 27 /** 28 * LU Decomposition. 29 * <p> 30 * For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n unit 31 * lower triangular matrix L, an n-by-n upper triangular matrix U, and a 32 * permutation vector piv of length m so that A(piv,:) = L*U. If m < n, then 33 * L is m-by-m and U is m-by-n. 34 * <p> 35 * The LU decompostion with pivoting always exists, even if the matrix is 36 * singular, so the constructor will never fail. The primary use of the LU 37 * decomposition is in the solution of square systems of simultaneous linear 38 * equations. This will fail if isNonsingular() returns false. 39 * 40 * @author Arthur Zimek 41 * @since 0.1 42 * 43 * @assoc - transforms - Matrix 44 */ 45 public class LUDecomposition implements java.io.Serializable { 46 /** 47 * Serial version 48 */ 49 private static final long serialVersionUID = 1L; 50 51 /** 52 * Array for internal storage of decomposition. 53 * 54 * @serial internal array storage. 55 */ 56 private double[][] LU; 57 58 /** 59 * Row and column dimensions, and pivot sign. 60 * 61 * @serial column dimension. 62 * @serial row dimension. 63 * @serial pivot sign. 64 */ 65 private int m, n, pivsign; 66 67 /** 68 * Internal storage of pivot vector. 69 * 70 * @serial pivot vector. 71 */ 72 private int[] piv; 73 74 /** 75 * LU Decomposition 76 * 77 * @param LU Rectangular matrix 78 */ LUDecomposition(double[][] LU)79 public LUDecomposition(double[][] LU) { 80 this(LU, LU.length, LU[0].length); 81 } 82 83 /** 84 * LU Decomposition 85 * 86 * @param LU Rectangular matrix 87 * @param m row dimensionality 88 * @param n column dimensionality 89 */ LUDecomposition(double[][] LU, int m, int n)90 public LUDecomposition(double[][] LU, int m, int n) { 91 this.LU = LU = copy(LU); 92 this.m = m; 93 this.n = n; 94 // Use a "left-looking", dot-product, Crout/Doolittle algorithm. 95 piv = new int[m]; 96 for(int i = 0; i < m; i++) { 97 piv[i] = i; 98 } 99 pivsign = 1; 100 // Copy of column 101 double[] LUcolj = new double[m]; 102 103 // Outer loop. 104 for(int j = 0; j < n; j++) { 105 // Make a copy of the j-th column to localize references. 106 for(int i = 0; i < m; i++) { 107 LUcolj[i] = LU[i][j]; 108 } 109 110 // Apply previous transformations. 111 for(int i = 0; i < m; i++) { 112 // Most of the time is spent in the following dot product. 113 int kmax = Math.min(i, j); 114 double s = 0.0; 115 double[] LUrowi = LU[i]; 116 for(int k = 0; k < kmax; k++) { 117 s += LUrowi[k] * LUcolj[k]; 118 } 119 LUrowi[j] = LUcolj[i] -= s; 120 } 121 122 // Find pivot and exchange if necessary. 123 int p = j; 124 for(int i = j + 1; i < m; i++) { 125 if(Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])) { 126 p = i; 127 } 128 } 129 if(p != j) { 130 double[] tmp = LU[j]; 131 LU[j] = LU[p]; 132 LU[p] = tmp; 133 int k = piv[p]; 134 piv[p] = piv[j]; 135 piv[j] = k; 136 pivsign = -pivsign; 137 } 138 139 // Compute multipliers. 140 final double LUjj = LU[j][j]; 141 if(j < m && LUjj != 0.0) { 142 for(int i = j + 1; i < m; i++) { 143 LU[i][j] /= LUjj; 144 } 145 } 146 } 147 } 148 149 /** 150 * Is the matrix nonsingular? 151 * 152 * @return true if U, and hence A, is nonsingular. 153 */ isNonsingular()154 public boolean isNonsingular() { 155 for(int j = 0; j < n; j++) { 156 if(LU[j][j] == 0) { 157 return false; 158 } 159 } 160 return true; 161 } 162 163 /** 164 * Return lower triangular factor 165 * 166 * @return L 167 */ getL()168 public double[][] getL() { 169 double[][] L = new double[m][n]; 170 L[0][0] = 1.; 171 for(int i = 1; i < m; i++) { 172 final double[] Li = L[i]; 173 System.arraycopy(LU[i], 0, Li, 0, Math.min(i, n)); 174 if (i < n) { 175 Li[i] = 1.; 176 } 177 } 178 return L; 179 } 180 181 /** 182 * Return upper triangular factor 183 * 184 * @return U 185 */ getU()186 public double[][] getU() { 187 double[][] U = new double[n][n]; 188 for(int i = 0; i < n; i++) { 189 System.arraycopy(LU[i], i, U[i], i, n - i); 190 } 191 return U; 192 } 193 194 /** 195 * Return pivot permutation vector 196 * 197 * @return piv 198 */ getPivot()199 public int[] getPivot() { 200 return piv.clone(); 201 } 202 203 /** 204 * Determinant 205 * 206 * @return det(A) 207 * @exception IllegalArgumentException Matrix must be square 208 */ det()209 public double det() { 210 if(m != n) { 211 throw new IllegalArgumentException(ERR_MATRIX_NONSQUARE); 212 } 213 double d = pivsign; 214 for(int j = 0; j < n; j++) { 215 d *= LU[j][j]; 216 } 217 return d; 218 } 219 220 /** 221 * Solve A*X = B 222 * 223 * @param B A Matrix with as many rows as A and any number of columns. 224 * @return X so that L*U*X = B(piv,:) 225 * @throws IllegalArgumentException Matrix row dimensions must agree. 226 * @throws ArithmeticException Matrix is singular. 227 */ solve(double[][] B)228 public double[][] solve(double[][] B) { 229 if(B.length != m) { 230 throw new IllegalArgumentException(ERR_MATRIX_DIMENSIONS); 231 } 232 double[][] sol = solveInplace(getMatrix(B, piv, 0, B[0].length)); 233 return n < sol.length ? Arrays.copyOf(sol, n) : sol; 234 } 235 236 /** 237 * Solve A*X = B 238 * 239 * @param B A Matrix with as many rows as A and any number of columns. 240 * @return B 241 * @throws IllegalArgumentException Matrix row dimensions must agree. 242 * @throws ArithmeticException Matrix is singular. 243 */ solveInplace(double[][] B)244 private double[][] solveInplace(double[][] B) { 245 int mx = B.length; 246 if(mx != m) { 247 throw new IllegalArgumentException(ERR_MATRIX_DIMENSIONS); 248 } 249 if(!this.isNonsingular()) { 250 throw new ArithmeticException(ERR_SINGULAR); 251 } 252 // Solve L*Y = B(piv,:) 253 for(int k = 0; k < n; k++) { 254 final double[] Bk = B[k]; 255 for(int i = k + 1; i < n; i++) { 256 minusTimesEquals(B[i], Bk, LU[i][k]); 257 } 258 } 259 // Solve U*X = Y; 260 for(int k = n - 1; k >= 0; k--) { 261 final double[] Bk = B[k]; 262 timesEquals(Bk, 1. / LU[k][k]); 263 for(int i = 0; i < k; i++) { 264 minusTimesEquals(B[i], Bk, LU[i][k]); 265 } 266 } 267 return B; 268 } 269 270 /** 271 * Solve A*X = b 272 * 273 * @param b A column vector with as many rows as A 274 * @return X so that L*U*X = b(piv) 275 * @throws IllegalArgumentException Matrix row dimensions must agree. 276 * @throws ArithmeticException Matrix is singular. 277 */ solve(double[] b)278 public double[] solve(double[] b) { 279 if(b.length != m) { 280 throw new IllegalArgumentException(ERR_MATRIX_DIMENSIONS); 281 } 282 if(!this.isNonsingular()) { 283 throw new ArithmeticException(ERR_SINGULAR); 284 } 285 double[] bc = new double[piv.length]; 286 for(int i = 0; i < piv.length; i++) { 287 bc[i] = b[piv[i]]; 288 } 289 solveInplace(bc); 290 return n < bc.length ? Arrays.copyOf(bc, n) : bc; 291 } 292 293 /** 294 * Solve A*X = b 295 * 296 * @param b A vector 297 * @return b 298 * @throws IllegalArgumentException Matrix row dimensions must agree. 299 * @throws ArithmeticException Matrix is singular. 300 */ solveInplace(double[] b)301 public double[] solveInplace(double[] b) { 302 if(b.length != m) { 303 throw new IllegalArgumentException(ERR_MATRIX_DIMENSIONS); 304 } 305 if(!this.isNonsingular()) { 306 throw new ArithmeticException(ERR_SINGULAR); 307 } 308 // Solve L*Y = B(piv,:) 309 for(int k = 0; k < n; k++) { 310 for(int i = k + 1; i < n; i++) { 311 b[i] -= b[k] * LU[i][k]; 312 } 313 } 314 // Solve U*X = Y; 315 for(int k = n - 1; k >= 0; k--) { 316 b[k] /= LU[k][k]; 317 for(int i = 0; i < k; i++) { 318 b[i] -= b[k] * LU[i][k]; 319 } 320 } 321 return b; 322 } 323 324 /** 325 * Find the inverse matrix. 326 * 327 * @return Inverse matrix 328 * @throws ArithmeticException Matrix is rank deficient. 329 */ inverse()330 public double[][] inverse() { 331 // Build permuted identity matrix efficiently: 332 double[][] b = new double[piv.length][m]; 333 for(int i = 0; i < piv.length; i++) { 334 b[piv[i]][i] = 1.; 335 } 336 return solveInplace(b); 337 } 338 } 339