LUDecomposition.java

  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.  *      https://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.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */

  21. package org.hipparchus.linear;

  22. import org.hipparchus.exception.LocalizedCoreFormats;
  23. import org.hipparchus.exception.MathIllegalArgumentException;
  24. import org.hipparchus.util.FastMath;

  25. /**
  26.  * Calculates the LUP-decomposition of a square matrix.
  27.  * <p>The LUP-decomposition of a matrix A consists of three matrices L, U and
  28.  * P that satisfy: P&times;A = L&times;U. L is lower triangular (with unit
  29.  * diagonal terms), U is upper triangular and P is a permutation matrix. All
  30.  * matrices are m&times;m.</p>
  31.  * <p>As shown by the presence of the P matrix, this decomposition is
  32.  * implemented using partial pivoting.</p>
  33.  * <p>This class is based on the class with similar name from the
  34.  * <a href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library.</p>
  35.  * <ul>
  36.  *   <li>a {@link #getP() getP} method has been added,</li>
  37.  *   <li>the {@code det} method has been renamed as {@link #getDeterminant()
  38.  *   getDeterminant},</li>
  39.  *   <li>the {@code getDoublePivot} method has been removed (but the int based
  40.  *   {@link #getPivot() getPivot} method has been kept),</li>
  41.  *   <li>the {@code solve} and {@code isNonSingular} methods have been replaced
  42.  *   by a {@link #getSolver() getSolver} method and the equivalent methods
  43.  *   provided by the returned {@link DecompositionSolver}.</li>
  44.  * </ul>
  45.  *
  46.  * @see <a href="http://mathworld.wolfram.com/LUDecomposition.html">MathWorld</a>
  47.  * @see <a href="http://en.wikipedia.org/wiki/LU_decomposition">Wikipedia</a>
  48.  */
  49. public class LUDecomposition {
  50.     /** Default bound to determine effective singularity in LU decomposition. */
  51.     private static final double DEFAULT_TOO_SMALL = 1e-11;
  52.     /** Entries of LU decomposition. */
  53.     private final double[][] lu;
  54.     /** Pivot permutation associated with LU decomposition. */
  55.     private final int[] pivot;
  56.     /** Parity of the permutation associated with the LU decomposition. */
  57.     private boolean even;
  58.     /** Singularity indicator. */
  59.     private boolean singular;
  60.     /** Cached value of L. */
  61.     private RealMatrix cachedL;
  62.     /** Cached value of U. */
  63.     private RealMatrix cachedU;
  64.     /** Cached value of P. */
  65.     private RealMatrix cachedP;

  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 MathIllegalArgumentException if matrix is not square.
  73.      */
  74.     public LUDecomposition(RealMatrix matrix) {
  75.         this(matrix, DEFAULT_TOO_SMALL);
  76.     }

  77.     /**
  78.      * Calculates the LU-decomposition of the given matrix.
  79.      * @param matrix The matrix to decompose.
  80.      * @param singularityThreshold threshold (based on partial row norm)
  81.      * under which a matrix is considered singular
  82.      * @throws MathIllegalArgumentException if matrix is not square
  83.      */
  84.     public LUDecomposition(RealMatrix matrix, double singularityThreshold) {
  85.         if (!matrix.isSquare()) {
  86.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NON_SQUARE_MATRIX,
  87.                                                    matrix.getRowDimension(), matrix.getColumnDimension());
  88.         }

  89.         final int m = matrix.getColumnDimension();
  90.         lu = matrix.getData();
  91.         pivot = new int[m];
  92.         cachedL = null;
  93.         cachedU = null;
  94.         cachedP = null;

  95.         // Initialize permutation array and parity
  96.         for (int row = 0; row < m; row++) {
  97.             pivot[row] = row;
  98.         }
  99.         even     = true;
  100.         singular = false;

  101.         // Loop over columns
  102.         for (int col = 0; col < m; col++) {

  103.             // upper
  104.             for (int row = 0; row < col; row++) {
  105.                 final double[] luRow = lu[row];
  106.                 double sum = luRow[col];
  107.                 for (int i = 0; i < row; i++) {
  108.                     sum -= luRow[i] * lu[i][col];
  109.                 }
  110.                 luRow[col] = sum;
  111.             }

  112.             // lower
  113.             int max = col; // permutation row
  114.             double largest = Double.NEGATIVE_INFINITY;
  115.             for (int row = col; row < m; row++) {
  116.                 final double[] luRow = lu[row];
  117.                 double sum = luRow[col];
  118.                 for (int i = 0; i < col; i++) {
  119.                     sum -= luRow[i] * lu[i][col];
  120.                 }
  121.                 luRow[col] = sum;

  122.                 // maintain best permutation choice
  123.                 if (FastMath.abs(sum) > largest) {
  124.                     largest = FastMath.abs(sum);
  125.                     max = row;
  126.                 }
  127.             }

  128.             // Singularity check
  129.             if (FastMath.abs(lu[max][col]) < singularityThreshold) {
  130.                 singular = true;
  131.                 return;
  132.             }

  133.             // Pivot if necessary
  134.             if (max != col) {
  135.                 final double[] luMax = lu[max];
  136.                 final double[] luCol = lu[col];
  137.                 for (int i = 0; i < m; i++) {
  138.                     final double tmp = luMax[i];
  139.                     luMax[i] = luCol[i];
  140.                     luCol[i] = tmp;
  141.                 }
  142.                 int temp = pivot[max];
  143.                 pivot[max] = pivot[col];
  144.                 pivot[col] = temp;
  145.                 even = !even;
  146.             }

  147.             // Divide the lower elements by the "winning" diagonal elt.
  148.             final double luDiag = lu[col][col];
  149.             for (int row = col + 1; row < m; row++) {
  150.                 lu[row][col] /= luDiag;
  151.             }
  152.         }
  153.     }

  154.     /**
  155.      * Returns the matrix L of the decomposition.
  156.      * <p>L is a lower-triangular matrix</p>
  157.      * @return the L matrix (or null if decomposed matrix is singular)
  158.      */
  159.     public RealMatrix getL() {
  160.         if ((cachedL == null) && !singular) {
  161.             final int m = pivot.length;
  162.             cachedL = MatrixUtils.createRealMatrix(m, m);
  163.             for (int i = 0; i < m; ++i) {
  164.                 final double[] luI = lu[i];
  165.                 for (int j = 0; j < i; ++j) {
  166.                     cachedL.setEntry(i, j, luI[j]);
  167.                 }
  168.                 cachedL.setEntry(i, i, 1.0);
  169.             }
  170.         }
  171.         return cachedL;
  172.     }

  173.     /**
  174.      * Returns the matrix U of the decomposition.
  175.      * <p>U is an upper-triangular matrix</p>
  176.      * @return the U matrix (or null if decomposed matrix is singular)
  177.      */
  178.     public RealMatrix getU() {
  179.         if ((cachedU == null) && !singular) {
  180.             final int m = pivot.length;
  181.             cachedU = MatrixUtils.createRealMatrix(m, m);
  182.             for (int i = 0; i < m; ++i) {
  183.                 final double[] luI = lu[i];
  184.                 for (int j = i; j < m; ++j) {
  185.                     cachedU.setEntry(i, j, luI[j]);
  186.                 }
  187.             }
  188.         }
  189.         return cachedU;
  190.     }

  191.     /**
  192.      * Returns the P rows permutation matrix.
  193.      * <p>P is a sparse matrix with exactly one element set to 1.0 in
  194.      * each row and each column, all other elements being set to 0.0.</p>
  195.      * <p>The positions of the 1 elements are given by the {@link #getPivot()
  196.      * pivot permutation vector}.</p>
  197.      * @return the P rows permutation matrix (or null if decomposed matrix is singular)
  198.      * @see #getPivot()
  199.      */
  200.     public RealMatrix getP() {
  201.         if ((cachedP == null) && !singular) {
  202.             final int m = pivot.length;
  203.             cachedP = MatrixUtils.createRealMatrix(m, m);
  204.             for (int i = 0; i < m; ++i) {
  205.                 cachedP.setEntry(i, pivot[i], 1.0);
  206.             }
  207.         }
  208.         return cachedP;
  209.     }

  210.     /**
  211.      * Returns the pivot permutation vector.
  212.      * @return the pivot permutation vector
  213.      * @see #getP()
  214.      */
  215.     public int[] getPivot() {
  216.         return pivot.clone();
  217.     }

  218.     /**
  219.      * Return the determinant of the matrix
  220.      * @return determinant of the matrix
  221.      */
  222.     public double getDeterminant() {
  223.         if (singular) {
  224.             return 0;
  225.         } else {
  226.             final int m = pivot.length;
  227.             double determinant = even ? 1 : -1;
  228.             for (int i = 0; i < m; i++) {
  229.                 determinant *= lu[i][i];
  230.             }
  231.             return determinant;
  232.         }
  233.     }

  234.     /**
  235.      * Get a solver for finding the A &times; X = B solution in exact linear
  236.      * sense.
  237.      * @return a solver
  238.      */
  239.     public DecompositionSolver getSolver() {
  240.         return new Solver();
  241.     }

  242.     /** Specialized solver. */
  243.     private class Solver implements DecompositionSolver {

  244.         /** {@inheritDoc} */
  245.         @Override
  246.         public boolean isNonSingular() {
  247.             return !singular;
  248.         }

  249.         /** {@inheritDoc} */
  250.         @Override
  251.         public RealVector solve(RealVector b) {
  252.             final int m = pivot.length;
  253.             if (b.getDimension() != m) {
  254.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  255.                                                        b.getDimension(), m);
  256.             }
  257.             if (singular) {
  258.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.SINGULAR_MATRIX);
  259.             }

  260.             final double[] bp = new double[m];

  261.             // Apply permutations to b
  262.             for (int row = 0; row < m; row++) {
  263.                 bp[row] = b.getEntry(pivot[row]);
  264.             }

  265.             // Solve LY = b
  266.             for (int col = 0; col < m; col++) {
  267.                 final double bpCol = bp[col];
  268.                 for (int i = col + 1; i < m; i++) {
  269.                     bp[i] -= bpCol * lu[i][col];
  270.                 }
  271.             }

  272.             // Solve UX = Y
  273.             for (int col = m - 1; col >= 0; col--) {
  274.                 bp[col] /= lu[col][col];
  275.                 final double bpCol = bp[col];
  276.                 for (int i = 0; i < col; i++) {
  277.                     bp[i] -= bpCol * lu[i][col];
  278.                 }
  279.             }

  280.             return new ArrayRealVector(bp, false);
  281.         }

  282.         /** {@inheritDoc} */
  283.         @Override
  284.         public RealMatrix solve(RealMatrix b) {

  285.             final int m = pivot.length;
  286.             if (b.getRowDimension() != m) {
  287.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  288.                                                        b.getRowDimension(), m);
  289.             }
  290.             if (singular) {
  291.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.SINGULAR_MATRIX);
  292.             }

  293.             final int nColB = b.getColumnDimension();

  294.             // Apply permutations to b
  295.             final double[][] bp = new double[m][nColB];
  296.             for (int row = 0; row < m; row++) {
  297.                 final double[] bpRow = bp[row];
  298.                 final int pRow = pivot[row];
  299.                 for (int col = 0; col < nColB; col++) {
  300.                     bpRow[col] = b.getEntry(pRow, col);
  301.                 }
  302.             }

  303.             // Solve LY = b
  304.             for (int col = 0; col < m; col++) {
  305.                 final double[] bpCol = bp[col];
  306.                 for (int i = col + 1; i < m; i++) {
  307.                     final double[] bpI = bp[i];
  308.                     final double luICol = lu[i][col];
  309.                     for (int j = 0; j < nColB; j++) {
  310.                         bpI[j] -= bpCol[j] * luICol;
  311.                     }
  312.                 }
  313.             }

  314.             // Solve UX = Y
  315.             for (int col = m - 1; col >= 0; col--) {
  316.                 final double[] bpCol = bp[col];
  317.                 final double luDiag = lu[col][col];
  318.                 for (int j = 0; j < nColB; j++) {
  319.                     bpCol[j] /= luDiag;
  320.                 }
  321.                 for (int i = 0; i < col; i++) {
  322.                     final double[] bpI = bp[i];
  323.                     final double luICol = lu[i][col];
  324.                     for (int j = 0; j < nColB; j++) {
  325.                         bpI[j] -= bpCol[j] * luICol;
  326.                     }
  327.                 }
  328.             }

  329.             return new Array2DRowRealMatrix(bp, false);
  330.         }

  331.         /**
  332.          * Get the inverse of the decomposed matrix.
  333.          *
  334.          * @return the inverse matrix.
  335.          * @throws MathIllegalArgumentException if the decomposed matrix is singular.
  336.          */
  337.         @Override
  338.         public RealMatrix getInverse() {
  339.             return solve(MatrixUtils.createRealIdentityMatrix(pivot.length));
  340.         }

  341.         /** {@inheritDoc} */
  342.         @Override
  343.         public int getRowDimension() {
  344.             return lu.length;
  345.         }

  346.         /** {@inheritDoc} */
  347.         @Override
  348.         public int getColumnDimension() {
  349.             return lu[0].length;
  350.         }

  351.     }

  352. }