View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.math4.legacy.linear;
19  
20  import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
21  import org.apache.commons.math4.core.jdkmath.JdkMath;
22  
23  /**
24   * Calculates the LUP-decomposition of a square matrix.
25   * <p>The LUP-decomposition of a matrix A consists of three matrices L, U and
26   * P that satisfy: P&times;A = L&times;U. L is lower triangular (with unit
27   * diagonal terms), U is upper triangular and P is a permutation matrix. All
28   * matrices are m&times;m.</p>
29   * <p>As shown by the presence of the P matrix, this decomposition is
30   * implemented using partial pivoting.</p>
31   * <p>This class is based on the class with similar name from the
32   * <a href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library.</p>
33   * <ul>
34   *   <li>a {@link #getP() getP} method has been added,</li>
35   *   <li>the {@code det} method has been renamed as {@link #getDeterminant()
36   *   getDeterminant},</li>
37   *   <li>the {@code getDoublePivot} method has been removed (but the int based
38   *   {@link #getPivot() getPivot} method has been kept),</li>
39   *   <li>the {@code solve} and {@code isNonSingular} methods have been replaced
40   *   by a {@link #getSolver() getSolver} method and the equivalent methods
41   *   provided by the returned {@link DecompositionSolver}.</li>
42   * </ul>
43   *
44   * @see <a href="http://mathworld.wolfram.com/LUDecomposition.html">MathWorld</a>
45   * @see <a href="http://en.wikipedia.org/wiki/LU_decomposition">Wikipedia</a>
46   * @since 2.0 (changed to concrete class in 3.0)
47   */
48  public class LUDecomposition {
49      /** Default bound to determine effective singularity in LU decomposition. */
50      private static final double DEFAULT_TOO_SMALL = 1e-11;
51      /** Entries of LU decomposition. */
52      private final double[][] lu;
53      /** Pivot permutation associated with LU decomposition. */
54      private final int[] pivot;
55      /** Parity of the permutation associated with the LU decomposition. */
56      private boolean even;
57      /** Singularity indicator. */
58      private boolean singular;
59      /** Cached value of L. */
60      private RealMatrix cachedL;
61      /** Cached value of U. */
62      private RealMatrix cachedU;
63      /** Cached value of P. */
64      private RealMatrix cachedP;
65  
66      /**
67       * Calculates the LU-decomposition of the given matrix.
68       * This constructor uses 1e-11 as default value for the singularity
69       * threshold.
70       *
71       * @param matrix Matrix to decompose.
72       * @throws NonSquareMatrixException if matrix is not square.
73       */
74      public LUDecomposition(RealMatrix matrix) {
75          this(matrix, DEFAULT_TOO_SMALL);
76      }
77  
78      /**
79       * Calculates the LU-decomposition of the given matrix.
80       * @param matrix The matrix to decompose.
81       * @param singularityThreshold threshold (based on partial row norm)
82       * under which a matrix is considered singular
83       * @throws NonSquareMatrixException if matrix is not square
84       */
85      public LUDecomposition(RealMatrix matrix, double singularityThreshold) {
86          if (!matrix.isSquare()) {
87              throw new NonSquareMatrixException(matrix.getRowDimension(),
88                                                 matrix.getColumnDimension());
89          }
90  
91          final int m = matrix.getColumnDimension();
92          lu = matrix.getData();
93          pivot = new int[m];
94          cachedL = null;
95          cachedU = null;
96          cachedP = null;
97  
98          // Initialize permutation array and parity
99          for (int row = 0; row < m; row++) {
100             pivot[row] = row;
101         }
102         even     = true;
103         singular = false;
104 
105         // Loop over columns
106         for (int col = 0; col < m; col++) {
107 
108             // upper
109             for (int row = 0; row < col; row++) {
110                 final double[] luRow = lu[row];
111                 double sum = luRow[col];
112                 for (int i = 0; i < row; i++) {
113                     sum -= luRow[i] * lu[i][col];
114                 }
115                 luRow[col] = sum;
116             }
117 
118             // lower
119             int max = col; // permutation row
120             double largest = Double.NEGATIVE_INFINITY;
121             for (int row = col; row < m; row++) {
122                 final double[] luRow = lu[row];
123                 double sum = luRow[col];
124                 for (int i = 0; i < col; i++) {
125                     sum -= luRow[i] * lu[i][col];
126                 }
127                 luRow[col] = sum;
128 
129                 // maintain best permutation choice
130                 if (JdkMath.abs(sum) > largest) {
131                     largest = JdkMath.abs(sum);
132                     max = row;
133                 }
134             }
135 
136             // Singularity check
137             if (JdkMath.abs(lu[max][col]) < singularityThreshold) {
138                 singular = true;
139                 return;
140             }
141 
142             // Pivot if necessary
143             if (max != col) {
144                 double tmp = 0;
145                 final double[] luMax = lu[max];
146                 final double[] luCol = lu[col];
147                 for (int i = 0; i < m; i++) {
148                     tmp = luMax[i];
149                     luMax[i] = luCol[i];
150                     luCol[i] = tmp;
151                 }
152                 int temp = pivot[max];
153                 pivot[max] = pivot[col];
154                 pivot[col] = temp;
155                 even = !even;
156             }
157 
158             // Divide the lower elements by the "winning" diagonal elt.
159             final double luDiag = lu[col][col];
160             for (int row = col + 1; row < m; row++) {
161                 lu[row][col] /= luDiag;
162             }
163         }
164     }
165 
166     /**
167      * Returns the matrix L of the decomposition.
168      * <p>L is a lower-triangular matrix</p>
169      * @return the L matrix (or null if decomposed matrix is singular)
170      */
171     public RealMatrix getL() {
172         if (cachedL == null && !singular) {
173             final int m = pivot.length;
174             cachedL = MatrixUtils.createRealMatrix(m, m);
175             for (int i = 0; i < m; ++i) {
176                 final double[] luI = lu[i];
177                 for (int j = 0; j < i; ++j) {
178                     cachedL.setEntry(i, j, luI[j]);
179                 }
180                 cachedL.setEntry(i, i, 1.0);
181             }
182         }
183         return cachedL;
184     }
185 
186     /**
187      * Returns the matrix U of the decomposition.
188      * <p>U is an upper-triangular matrix</p>
189      * @return the U matrix (or null if decomposed matrix is singular)
190      */
191     public RealMatrix getU() {
192         if (cachedU == null && !singular) {
193             final int m = pivot.length;
194             cachedU = MatrixUtils.createRealMatrix(m, m);
195             for (int i = 0; i < m; ++i) {
196                 final double[] luI = lu[i];
197                 for (int j = i; j < m; ++j) {
198                     cachedU.setEntry(i, j, luI[j]);
199                 }
200             }
201         }
202         return cachedU;
203     }
204 
205     /**
206      * Returns the P rows permutation matrix.
207      * <p>P is a sparse matrix with exactly one element set to 1.0 in
208      * each row and each column, all other elements being set to 0.0.</p>
209      * <p>The positions of the 1 elements are given by the {@link #getPivot()
210      * pivot permutation vector}.</p>
211      * @return the P rows permutation matrix (or null if decomposed matrix is singular)
212      * @see #getPivot()
213      */
214     public RealMatrix getP() {
215         if (cachedP == null && !singular) {
216             final int m = pivot.length;
217             cachedP = MatrixUtils.createRealMatrix(m, m);
218             for (int i = 0; i < m; ++i) {
219                 cachedP.setEntry(i, pivot[i], 1.0);
220             }
221         }
222         return cachedP;
223     }
224 
225     /**
226      * Returns the pivot permutation vector.
227      * @return the pivot permutation vector
228      * @see #getP()
229      */
230     public int[] getPivot() {
231         return pivot.clone();
232     }
233 
234     /**
235      * Return the determinant of the matrix.
236      * @return determinant of the matrix
237      */
238     public double getDeterminant() {
239         if (singular) {
240             return 0;
241         } else {
242             final int m = pivot.length;
243             double determinant = even ? 1 : -1;
244             for (int i = 0; i < m; i++) {
245                 determinant *= lu[i][i];
246             }
247             return determinant;
248         }
249     }
250 
251     /**
252      * Get a solver for finding the A &times; X = B solution in exact linear
253      * sense.
254      * @return a solver
255      */
256     public DecompositionSolver getSolver() {
257         return new Solver(lu, pivot, singular);
258     }
259 
260     /** Specialized solver. */
261     private static final class Solver implements DecompositionSolver {
262 
263         /** Entries of LU decomposition. */
264         private final double[][] lu;
265 
266         /** Pivot permutation associated with LU decomposition. */
267         private final int[] pivot;
268 
269         /** Singularity indicator. */
270         private final boolean singular;
271 
272         /**
273          * Build a solver from decomposed matrix.
274          * @param lu entries of LU decomposition
275          * @param pivot pivot permutation associated with LU decomposition
276          * @param singular singularity indicator
277          */
278         private Solver(final double[][] lu, final int[] pivot, final boolean singular) {
279             this.lu       = lu;
280             this.pivot    = pivot;
281             this.singular = singular;
282         }
283 
284         /** {@inheritDoc} */
285         @Override
286         public boolean isNonSingular() {
287             return !singular;
288         }
289 
290         /** {@inheritDoc} */
291         @Override
292         public RealVector solve(RealVector b) {
293             final int m = pivot.length;
294             if (b.getDimension() != m) {
295                 throw new DimensionMismatchException(b.getDimension(), m);
296             }
297             if (singular) {
298                 throw new SingularMatrixException();
299             }
300 
301             final double[] bp = new double[m];
302 
303             // Apply permutations to b
304             for (int row = 0; row < m; row++) {
305                 bp[row] = b.getEntry(pivot[row]);
306             }
307 
308             // Solve LY = b
309             for (int col = 0; col < m; col++) {
310                 final double bpCol = bp[col];
311                 for (int i = col + 1; i < m; i++) {
312                     bp[i] -= bpCol * lu[i][col];
313                 }
314             }
315 
316             // Solve UX = Y
317             for (int col = m - 1; col >= 0; col--) {
318                 bp[col] /= lu[col][col];
319                 final double bpCol = bp[col];
320                 for (int i = 0; i < col; i++) {
321                     bp[i] -= bpCol * lu[i][col];
322                 }
323             }
324 
325             return new ArrayRealVector(bp, false);
326         }
327 
328         /** {@inheritDoc} */
329         @Override
330         public RealMatrix solve(RealMatrix b) {
331 
332             final int m = pivot.length;
333             if (b.getRowDimension() != m) {
334                 throw new DimensionMismatchException(b.getRowDimension(), m);
335             }
336             if (singular) {
337                 throw new SingularMatrixException();
338             }
339 
340             final int nColB = b.getColumnDimension();
341 
342             // Apply permutations to b
343             final double[][] bp = new double[m][nColB];
344             for (int row = 0; row < m; row++) {
345                 final double[] bpRow = bp[row];
346                 final int pRow = pivot[row];
347                 for (int col = 0; col < nColB; col++) {
348                     bpRow[col] = b.getEntry(pRow, col);
349                 }
350             }
351 
352             // Solve LY = b
353             for (int col = 0; col < m; col++) {
354                 final double[] bpCol = bp[col];
355                 for (int i = col + 1; i < m; i++) {
356                     final double[] bpI = bp[i];
357                     final double luICol = lu[i][col];
358                     for (int j = 0; j < nColB; j++) {
359                         bpI[j] -= bpCol[j] * luICol;
360                     }
361                 }
362             }
363 
364             // Solve UX = Y
365             for (int col = m - 1; col >= 0; col--) {
366                 final double[] bpCol = bp[col];
367                 final double luDiag = lu[col][col];
368                 for (int j = 0; j < nColB; j++) {
369                     bpCol[j] /= luDiag;
370                 }
371                 for (int i = 0; i < col; i++) {
372                     final double[] bpI = bp[i];
373                     final double luICol = lu[i][col];
374                     for (int j = 0; j < nColB; j++) {
375                         bpI[j] -= bpCol[j] * luICol;
376                     }
377                 }
378             }
379 
380             return new Array2DRowRealMatrix(bp, false);
381         }
382 
383         /**
384          * Get the inverse of the decomposed matrix.
385          *
386          * @return the inverse matrix.
387          * @throws SingularMatrixException if the decomposed matrix is singular.
388          */
389         @Override
390         public RealMatrix getInverse() {
391             return solve(MatrixUtils.createRealIdentityMatrix(pivot.length));
392         }
393     }
394 }