FieldLUDecomposition.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.  *      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. package org.apache.commons.math4.legacy.field.linalg;

  18. import org.apache.commons.numbers.field.Field;
  19. import org.apache.commons.math4.legacy.linear.SingularMatrixException;

  20. /**
  21.  * Calculates the LUP-decomposition of a square matrix.
  22.  *
  23.  * <p>The LUP-decomposition of a matrix A consists of three matrices
  24.  * L, U and P that satisfy: PA = LU, L is lower triangular, and U is
  25.  * upper triangular and P is a permutation matrix. All matrices are
  26.  * m&times;m.</p>
  27.  *
  28.  * <p>Since {@link Field field} elements do not provide an ordering
  29.  * operator, the permutation matrix is computed here only in order to
  30.  * avoid a zero pivot element, no attempt is done to get the largest
  31.  * pivot element.</p>
  32.  *
  33.  * @param <T> Type of the field elements.
  34.  *
  35.  * @see <a href="http://mathworld.wolfram.com/LUDecomposition.html">MathWorld</a>
  36.  * @see <a href="http://en.wikipedia.org/wiki/LU_decomposition">Wikipedia</a>
  37.  *
  38.  * @since 4.0
  39.  */
  40. public final class FieldLUDecomposition<T> {
  41.     /** Field to which the elements belong. */
  42.     private final Field<T> field;
  43.     /** Entries of LU decomposition. */
  44.     private final FieldDenseMatrix<T> mLU;
  45.     /** Pivot permutation associated with LU decomposition. */
  46.     private final int[] pivot;
  47.     /** Singularity indicator. */
  48.     private final boolean isSingular;
  49.     /** Parity of the permutation associated with the LU decomposition. */
  50.     private final boolean isEven;

  51.     /**
  52.      * Calculates the LU-decomposition of the given {@code matrix}.
  53.      *
  54.      * @param matrix Matrix to decompose.
  55.      * @throws IllegalArgumentException if the matrix is not square.
  56.      */
  57.     private FieldLUDecomposition(FieldDenseMatrix<T> matrix) {
  58.         matrix.checkMultiply(matrix);

  59.         field = matrix.getField();
  60.         final int m = matrix.getRowDimension();
  61.         pivot = new int[m];

  62.         // Initialize permutation array and parity.
  63.         for (int row = 0; row < m; row++) {
  64.             pivot[row] = row;
  65.         }
  66.         mLU = matrix.copy();

  67.         boolean even = true;
  68.         boolean singular = false;
  69.         // Loop over columns.
  70.         for (int col = 0; col < m; col++) {
  71.             T sum = field.zero();

  72.             // Upper.
  73.             for (int row = 0; row < col; row++) {
  74.                 sum = mLU.get(row, col);
  75.                 for (int i = 0; i < row; i++) {
  76.                     sum = field.subtract(sum,
  77.                                          field.multiply(mLU.get(row, i),
  78.                                                         mLU.get(i, col)));
  79.                 }
  80.                 mLU.set(row, col, sum);
  81.             }

  82.             // Lower.
  83.             int nonZero = col; // Permutation row.
  84.             for (int row = col; row < m; row++) {
  85.                 sum = mLU.get(row, col);
  86.                 for (int i = 0; i < col; i++) {
  87.                     sum = field.subtract(sum,
  88.                                          field.multiply(mLU.get(row, i),
  89.                                                         mLU.get(i, col)));
  90.                 }
  91.                 mLU.set(row, col, sum);

  92.                 if (mLU.get(nonZero, col).equals(field.zero())) {
  93.                     // try to select a better permutation choice
  94.                     ++nonZero;
  95.                 }
  96.             }

  97.             // Singularity check.
  98.             if (nonZero >= m) {
  99.                 singular = true;
  100.             } else {
  101.                 // Pivot if necessary.
  102.                 if (nonZero != col) {
  103.                     T tmp = field.zero();
  104.                     for (int i = 0; i < m; i++) {
  105.                         tmp = mLU.get(nonZero, i);
  106.                         mLU.set(nonZero, i, mLU.get(col, i));
  107.                         mLU.set(col, i, tmp);
  108.                     }
  109.                     int temp = pivot[nonZero];
  110.                     pivot[nonZero] = pivot[col];
  111.                     pivot[col] = temp;
  112.                     even = !even;
  113.                 }

  114.                 // Divide the lower elements by the "winning" diagonal element.
  115.                 final T luDiag = mLU.get(col, col);
  116.                 for (int row = col + 1; row < m; row++) {
  117.                     mLU.set(row, col, field.divide(mLU.get(row, col),
  118.                                                    luDiag));
  119.                 }
  120.             }
  121.         }

  122.         isSingular = singular;
  123.         isEven = even;
  124.     }

  125.     /**
  126.      * Factory method.
  127.      *
  128.      * @param <T> Type of the field elements.
  129.      * @param m Matrix to decompose.
  130.      * @return a new instance.
  131.      */
  132.     public static <T> FieldLUDecomposition<T> of(FieldDenseMatrix<T> m) {
  133.         return new FieldLUDecomposition<>(m);
  134.     }

  135.     /**
  136.      * @return {@code true} if the matrix is singular.
  137.      */
  138.     public boolean isSingular() {
  139.         return isSingular;
  140.     }

  141.     /**
  142.      * Builds the "L" matrix of the decomposition.
  143.      *
  144.      * @return the lower triangular matrix.
  145.      * @throws SingularMatrixException if the matrix is singular.
  146.      */
  147.     public FieldDenseMatrix<T> getL() {
  148.         if (isSingular) {
  149.             throw new SingularMatrixException();
  150.         }

  151.         final int m = pivot.length;
  152.         final FieldDenseMatrix<T> mL = FieldDenseMatrix.zero(field, m, m);
  153.         for (int i = 0; i < m; i++) {
  154.             for (int j = 0; j < i; j++) {
  155.                 mL.set(i, j, mLU.get(i, j));
  156.             }
  157.             mL.set(i, i, field.one());
  158.         }

  159.         return mL;
  160.     }

  161.     /**
  162.      * Builds the "U" matrix of the decomposition.
  163.      *
  164.      * @return the upper triangular matrix.
  165.      * @throws SingularMatrixException if the matrix is singular.
  166.      */
  167.     public FieldDenseMatrix<T> getU() {
  168.         if (isSingular) {
  169.             throw new SingularMatrixException();
  170.         }

  171.         final int m = pivot.length;
  172.         final FieldDenseMatrix<T> mU = FieldDenseMatrix.zero(field, m, m);
  173.         for (int i = 0; i < m; i++) {
  174.             for (int j = i; j < m; j++) {
  175.                 mU.set(i, j, mLU.get(i, j));
  176.             }
  177.         }

  178.         return mU;
  179.     }

  180.     /**
  181.      * Builds the "P" matrix.
  182.      *
  183.      * <p>P is a matrix with exactly one element set to {@link Field#one() one} in
  184.      * each row and each column, all other elements being set to {@link Field#zero() zero}.
  185.      * The positions of the "one" elements are given by the {@link #getPivot()
  186.      * pivot permutation vector}.</p>
  187.      * @return the "P" rows permutation matrix.
  188.      * @throws SingularMatrixException if the matrix is singular.
  189.      *
  190.      * @see #getPivot()
  191.      */
  192.     public FieldDenseMatrix<T> getP() {
  193.         if (isSingular) {
  194.             throw new SingularMatrixException();
  195.         }

  196.         final int m = pivot.length;
  197.         final FieldDenseMatrix<T> mP = FieldDenseMatrix.zero(field, m, m);

  198.         for (int i = 0; i < m; i++) {
  199.             mP.set(i, pivot[i], field.one());
  200.         }

  201.         return mP;
  202.     }

  203.     /**
  204.      * Gets the pivot permutation vector.
  205.      *
  206.      * @return the pivot permutation vector.
  207.      *
  208.      * @see #getP()
  209.      */
  210.     public int[] getPivot() {
  211.         return pivot.clone();
  212.     }

  213.     /**
  214.      * Return the determinant of the matrix.
  215.      * @return determinant of the matrix
  216.      */
  217.     public T getDeterminant() {
  218.         if (isSingular) {
  219.             return field.zero();
  220.         } else {
  221.             final int m = pivot.length;
  222.             T determinant = isEven ?
  223.                 field.one() :
  224.                 field.negate(field.one());

  225.             for (int i = 0; i < m; i++) {
  226.                 determinant = field.multiply(determinant,
  227.                                              mLU.get(i, i));
  228.             }

  229.             return determinant;
  230.         }
  231.     }

  232.     /**
  233.      * Creates a solver for finding the solution {@code X} of the linear
  234.      * system of equations {@code A X = B}.
  235.      *
  236.      * @return a solver.
  237.      * @throws SingularMatrixException if the matrix is singular.
  238.      */
  239.     public FieldDecompositionSolver<T> getSolver() {
  240.         if (isSingular) {
  241.             throw new SingularMatrixException();
  242.         }

  243.         return new Solver<>(mLU, pivot);
  244.     }

  245.     /**
  246.      * Specialized solver.
  247.      *
  248.      * @param <T> Type of the field elements.
  249.      */
  250.     private static final class Solver<T> implements FieldDecompositionSolver<T> {
  251.         /** Field to which the elements belong. */
  252.         private final Field<T> field;
  253.         /** LU decomposition. */
  254.         private final FieldDenseMatrix<T> mLU;
  255.         /** Pivot permutation associated with LU decomposition. */
  256.         private final int[] pivot;

  257.         /**
  258.          * Builds a solver from a LU-decomposed matrix.
  259.          *
  260.          * @param mLU LU matrix.
  261.          * @param pivot Pivot permutation associated with the decomposition.
  262.          */
  263.         private Solver(final FieldDenseMatrix<T> mLU,
  264.                        final int[] pivot) {
  265.             field = mLU.getField();
  266.             this.mLU = mLU.copy();
  267.             this.pivot = pivot.clone();
  268.         }

  269.         /** {@inheritDoc} */
  270.         @Override
  271.         public FieldDenseMatrix<T> solve(final FieldDenseMatrix<T> b) {
  272.             mLU.checkMultiply(b);

  273.             final FieldDenseMatrix<T> bp = b.copy();
  274.             final int nColB = b.getColumnDimension();
  275.             final int m = pivot.length;

  276.             // Apply permutations.
  277.             for (int row = 0; row < m; row++) {
  278.                 final int pRow = pivot[row];
  279.                 for (int col = 0; col < nColB; col++) {
  280.                     bp.set(row, col,
  281.                            b.get(row, col));
  282.                 }
  283.             }

  284.             // Solve LY = b
  285.             for (int col = 0; col < m; col++) {
  286.                 for (int i = col + 1; i < m; i++) {
  287.                     for (int j = 0; j < nColB; j++) {
  288.                         bp.set(i, j,
  289.                                field.subtract(bp.get(i, j),
  290.                                               field.multiply(bp.get(col, j),
  291.                                                              mLU.get(i, col))));
  292.                     }
  293.                 }
  294.             }

  295.             // Solve UX = Y
  296.             for (int col = m - 1; col >= 0; col--) {
  297.                 for (int j = 0; j < nColB; j++) {
  298.                     bp.set(col, j,
  299.                            field.divide(bp.get(col, j),
  300.                                         mLU.get(col, col)));
  301.                 }
  302.                 for (int i = 0; i < col; i++) {
  303.                     for (int j = 0; j < nColB; j++) {
  304.                         bp.set(i, j,
  305.                                field.subtract(bp.get(i, j),
  306.                                               field.multiply(bp.get(col, j),
  307.                                                              mLU.get(i, col))));
  308.                     }
  309.                 }
  310.             }

  311.             return bp;
  312.         }

  313.         /** {@inheritDoc} */
  314.         @Override
  315.         public FieldDenseMatrix<T> getInverse() {
  316.             return solve(FieldDenseMatrix.identity(field, pivot.length));
  317.         }
  318.     }
  319. }