001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.math4.legacy.field.linalg; 019 020import org.apache.commons.numbers.field.Field; 021import org.apache.commons.math4.legacy.linear.SingularMatrixException; 022 023/** 024 * Calculates the LUP-decomposition of a square matrix. 025 * 026 * <p>The LUP-decomposition of a matrix A consists of three matrices 027 * L, U and P that satisfy: PA = LU, L is lower triangular, and U is 028 * upper triangular and P is a permutation matrix. All matrices are 029 * m×m.</p> 030 * 031 * <p>Since {@link Field field} elements do not provide an ordering 032 * operator, the permutation matrix is computed here only in order to 033 * avoid a zero pivot element, no attempt is done to get the largest 034 * pivot element.</p> 035 * 036 * @param <T> Type of the field elements. 037 * 038 * @see <a href="http://mathworld.wolfram.com/LUDecomposition.html">MathWorld</a> 039 * @see <a href="http://en.wikipedia.org/wiki/LU_decomposition">Wikipedia</a> 040 * 041 * @since 4.0 042 */ 043public final class FieldLUDecomposition<T> { 044 /** Field to which the elements belong. */ 045 private final Field<T> field; 046 /** Entries of LU decomposition. */ 047 private final FieldDenseMatrix<T> mLU; 048 /** Pivot permutation associated with LU decomposition. */ 049 private final int[] pivot; 050 /** Singularity indicator. */ 051 private final boolean isSingular; 052 /** Parity of the permutation associated with the LU decomposition. */ 053 private final boolean isEven; 054 055 /** 056 * Calculates the LU-decomposition of the given {@code matrix}. 057 * 058 * @param matrix Matrix to decompose. 059 * @throws IllegalArgumentException if the matrix is not square. 060 */ 061 private FieldLUDecomposition(FieldDenseMatrix<T> matrix) { 062 matrix.checkMultiply(matrix); 063 064 field = matrix.getField(); 065 final int m = matrix.getRowDimension(); 066 pivot = new int[m]; 067 068 // Initialize permutation array and parity. 069 for (int row = 0; row < m; row++) { 070 pivot[row] = row; 071 } 072 mLU = matrix.copy(); 073 074 boolean even = true; 075 boolean singular = false; 076 // Loop over columns. 077 for (int col = 0; col < m; col++) { 078 T sum = field.zero(); 079 080 // Upper. 081 for (int row = 0; row < col; row++) { 082 sum = mLU.get(row, col); 083 for (int i = 0; i < row; i++) { 084 sum = field.subtract(sum, 085 field.multiply(mLU.get(row, i), 086 mLU.get(i, col))); 087 } 088 mLU.set(row, col, sum); 089 } 090 091 // Lower. 092 int nonZero = col; // Permutation row. 093 for (int row = col; row < m; row++) { 094 sum = mLU.get(row, col); 095 for (int i = 0; i < col; i++) { 096 sum = field.subtract(sum, 097 field.multiply(mLU.get(row, i), 098 mLU.get(i, col))); 099 } 100 mLU.set(row, col, sum); 101 102 if (mLU.get(nonZero, col).equals(field.zero())) { 103 // try to select a better permutation choice 104 ++nonZero; 105 } 106 } 107 108 // Singularity check. 109 if (nonZero >= m) { 110 singular = true; 111 } else { 112 // Pivot if necessary. 113 if (nonZero != col) { 114 T tmp = field.zero(); 115 for (int i = 0; i < m; i++) { 116 tmp = mLU.get(nonZero, i); 117 mLU.set(nonZero, i, mLU.get(col, i)); 118 mLU.set(col, i, tmp); 119 } 120 int temp = pivot[nonZero]; 121 pivot[nonZero] = pivot[col]; 122 pivot[col] = temp; 123 even = !even; 124 } 125 126 // Divide the lower elements by the "winning" diagonal element. 127 final T luDiag = mLU.get(col, col); 128 for (int row = col + 1; row < m; row++) { 129 mLU.set(row, col, field.divide(mLU.get(row, col), 130 luDiag)); 131 } 132 } 133 } 134 135 isSingular = singular; 136 isEven = even; 137 } 138 139 /** 140 * Factory method. 141 * 142 * @param <T> Type of the field elements. 143 * @param m Matrix to decompose. 144 * @return a new instance. 145 */ 146 public static <T> FieldLUDecomposition<T> of(FieldDenseMatrix<T> m) { 147 return new FieldLUDecomposition<>(m); 148 } 149 150 /** 151 * @return {@code true} if the matrix is singular. 152 */ 153 public boolean isSingular() { 154 return isSingular; 155 } 156 157 /** 158 * Builds the "L" matrix of the decomposition. 159 * 160 * @return the lower triangular matrix. 161 * @throws SingularMatrixException if the matrix is singular. 162 */ 163 public FieldDenseMatrix<T> getL() { 164 if (isSingular) { 165 throw new SingularMatrixException(); 166 } 167 168 final int m = pivot.length; 169 final FieldDenseMatrix<T> mL = FieldDenseMatrix.zero(field, m, m); 170 for (int i = 0; i < m; i++) { 171 for (int j = 0; j < i; j++) { 172 mL.set(i, j, mLU.get(i, j)); 173 } 174 mL.set(i, i, field.one()); 175 } 176 177 return mL; 178 } 179 180 /** 181 * Builds the "U" matrix of the decomposition. 182 * 183 * @return the upper triangular matrix. 184 * @throws SingularMatrixException if the matrix is singular. 185 */ 186 public FieldDenseMatrix<T> getU() { 187 if (isSingular) { 188 throw new SingularMatrixException(); 189 } 190 191 final int m = pivot.length; 192 final FieldDenseMatrix<T> mU = FieldDenseMatrix.zero(field, m, m); 193 for (int i = 0; i < m; i++) { 194 for (int j = i; j < m; j++) { 195 mU.set(i, j, mLU.get(i, j)); 196 } 197 } 198 199 return mU; 200 } 201 202 /** 203 * Builds the "P" matrix. 204 * 205 * <p>P is a matrix with exactly one element set to {@link Field#one() one} in 206 * each row and each column, all other elements being set to {@link Field#zero() zero}. 207 * The positions of the "one" elements are given by the {@link #getPivot() 208 * pivot permutation vector}.</p> 209 * @return the "P" rows permutation matrix. 210 * @throws SingularMatrixException if the matrix is singular. 211 * 212 * @see #getPivot() 213 */ 214 public FieldDenseMatrix<T> getP() { 215 if (isSingular) { 216 throw new SingularMatrixException(); 217 } 218 219 final int m = pivot.length; 220 final FieldDenseMatrix<T> mP = FieldDenseMatrix.zero(field, m, m); 221 222 for (int i = 0; i < m; i++) { 223 mP.set(i, pivot[i], field.one()); 224 } 225 226 return mP; 227 } 228 229 /** 230 * Gets the pivot permutation vector. 231 * 232 * @return the pivot permutation vector. 233 * 234 * @see #getP() 235 */ 236 public int[] getPivot() { 237 return pivot.clone(); 238 } 239 240 /** 241 * Return the determinant of the matrix. 242 * @return determinant of the matrix 243 */ 244 public T getDeterminant() { 245 if (isSingular) { 246 return field.zero(); 247 } else { 248 final int m = pivot.length; 249 T determinant = isEven ? 250 field.one() : 251 field.negate(field.one()); 252 253 for (int i = 0; i < m; i++) { 254 determinant = field.multiply(determinant, 255 mLU.get(i, i)); 256 } 257 258 return determinant; 259 } 260 } 261 262 /** 263 * Creates a solver for finding the solution {@code X} of the linear 264 * system of equations {@code A X = B}. 265 * 266 * @return a solver. 267 * @throws SingularMatrixException if the matrix is singular. 268 */ 269 public FieldDecompositionSolver<T> getSolver() { 270 if (isSingular) { 271 throw new SingularMatrixException(); 272 } 273 274 return new Solver<>(mLU, pivot); 275 } 276 277 /** 278 * Specialized solver. 279 * 280 * @param <T> Type of the field elements. 281 */ 282 private static final class Solver<T> implements FieldDecompositionSolver<T> { 283 /** Field to which the elements belong. */ 284 private final Field<T> field; 285 /** LU decomposition. */ 286 private final FieldDenseMatrix<T> mLU; 287 /** Pivot permutation associated with LU decomposition. */ 288 private final int[] pivot; 289 290 /** 291 * Builds a solver from a LU-decomposed matrix. 292 * 293 * @param mLU LU matrix. 294 * @param pivot Pivot permutation associated with the decomposition. 295 */ 296 private Solver(final FieldDenseMatrix<T> mLU, 297 final int[] pivot) { 298 field = mLU.getField(); 299 this.mLU = mLU.copy(); 300 this.pivot = pivot.clone(); 301 } 302 303 /** {@inheritDoc} */ 304 @Override 305 public FieldDenseMatrix<T> solve(final FieldDenseMatrix<T> b) { 306 mLU.checkMultiply(b); 307 308 final FieldDenseMatrix<T> bp = b.copy(); 309 final int nColB = b.getColumnDimension(); 310 final int m = pivot.length; 311 312 // Apply permutations. 313 for (int row = 0; row < m; row++) { 314 final int pRow = pivot[row]; 315 for (int col = 0; col < nColB; col++) { 316 bp.set(row, col, 317 b.get(row, col)); 318 } 319 } 320 321 // Solve LY = b 322 for (int col = 0; col < m; col++) { 323 for (int i = col + 1; i < m; i++) { 324 for (int j = 0; j < nColB; j++) { 325 bp.set(i, j, 326 field.subtract(bp.get(i, j), 327 field.multiply(bp.get(col, j), 328 mLU.get(i, col)))); 329 } 330 } 331 } 332 333 // Solve UX = Y 334 for (int col = m - 1; col >= 0; col--) { 335 for (int j = 0; j < nColB; j++) { 336 bp.set(col, j, 337 field.divide(bp.get(col, j), 338 mLU.get(col, col))); 339 } 340 for (int i = 0; i < col; i++) { 341 for (int j = 0; j < nColB; j++) { 342 bp.set(i, j, 343 field.subtract(bp.get(i, j), 344 field.multiply(bp.get(col, j), 345 mLU.get(i, col)))); 346 } 347 } 348 } 349 350 return bp; 351 } 352 353 /** {@inheritDoc} */ 354 @Override 355 public FieldDenseMatrix<T> getInverse() { 356 return solve(FieldDenseMatrix.identity(field, pivot.length)); 357 } 358 } 359}