All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.helger.matrix.LUDecomposition Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2014-2024 Philip Helger (www.helger.com)
 * philip[at]helger[dot]com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.helger.matrix;

import java.util.Arrays;

import javax.annotation.Nonnull;

import com.helger.commons.annotation.ReturnsMutableCopy;
import com.helger.commons.math.MathHelper;

/**
 * LU Decomposition.
 * 

* For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n unit * lower triangular matrix L, an n-by-n upper triangular matrix U, and a * permutation vector piv of length m so that A(piv,:) = L*U. If m < n, then * L is m-by-m and U is m-by-n. *

*

* The LU decompostion with pivoting always exists, even if the matrix is * singular, so the constructor will never fail. The primary use of the LU * decomposition is in the solution of square systems of simultaneous linear * equations. This will fail if isNonsingular() returns false. *

*/ public class LUDecomposition { /** * Array for internal storage of decomposition. * * @serial internal array storage. */ private final double [] [] m_aLU; /** * Row and column dimensions, and pivot sign. * * @serial column dimension. * @serial row dimension. * @serial pivot sign. */ private final int m_nRows; private final int m_nCols; private final int m_nPivSign; /** * Internal storage of pivot vector. * * @serial pivot vector. */ private final int [] m_aPivot; /** * LU Decomposition Structure to access L, U and piv. * * @param aMatrix * Rectangular matrix */ public LUDecomposition (@Nonnull final Matrix aMatrix) { // Use a "left-looking", dot-product, Crout/Doolittle algorithm. m_aLU = aMatrix.getArrayCopy (); m_nRows = aMatrix.getRowDimension (); m_nCols = aMatrix.getColumnDimension (); m_aPivot = new int [m_nRows]; for (int i = 0; i < m_nRows; i++) m_aPivot[i] = i; int nPivSign = 1; double [] aLUrowi; final double [] aLUcolj = new double [m_nRows]; // Outer loop. for (int j = 0; j < m_nCols; j++) { // Make a copy of the j-th column to localize references. for (int i = 0; i < m_nRows; i++) aLUcolj[i] = m_aLU[i][j]; // Apply previous transformations. for (int i = 0; i < m_nRows; i++) { aLUrowi = m_aLU[i]; // Most of the time is spent in the following dot product. final int kmax = Math.min (i, j); double s = 0.0; for (int k = 0; k < kmax; k++) s += aLUrowi[k] * aLUcolj[k]; aLUrowi[j] = aLUcolj[i] -= s; } // Find pivot and exchange if necessary. int p = j; for (int i = j + 1; i < m_nRows; i++) if (MathHelper.abs (aLUcolj[i]) > MathHelper.abs (aLUcolj[p])) p = i; final double [] aLUj = m_aLU[j]; if (p != j) { final double [] aLUp = m_aLU[p]; for (int k = 0; k < m_nCols; k++) { final double t = aLUp[k]; aLUp[k] = aLUj[k]; aLUj[k] = t; } final int k = m_aPivot[p]; m_aPivot[p] = m_aPivot[j]; m_aPivot[j] = k; nPivSign = -nPivSign; } // Compute multipliers. if (j < m_nRows && aLUj[j] != 0.0) for (int i = j + 1; i < m_nRows; i++) m_aLU[i][j] /= aLUj[j]; } m_nPivSign = nPivSign; } /* * ------------------------ Temporary, experimental code. * ------------------------ *\ \** LU Decomposition, computed by Gaussian * elimination.

This constructor computes L and U with the "daxpy"-based * elimination algorithm used in LINPACK and MATLAB. In Java, we suspect the * dot-product, Crout algorithm will be faster. We have temporarily included * this constructor until timing experiments confirm this suspicion.

* @param A Rectangular matrix * @param linpackflag Use Gaussian elimination. Actual value ignored. * @return Structure to access L, U and piv.\ public LUDecomposition (Matrix * A, int linpackflag) { // Initialize. LU = A.getArrayCopy(); m = * A.getRowDimension(); n = A.getColumnDimension(); piv = new int[m]; for (int * i = 0; i < m; i++) { piv[i] = i; } pivsign = 1; // Main loop. for (int k = * 0; k < n; k++) { // Find pivot. int p = k; for (int i = k+1; i < m; i++) { * if (MathHelper.abs(LU[i][k]) > MathHelper.abs(LU[p][k])) { p = i; } } // * Exchange if necessary. if (p != k) { for (int j = 0; j < n; j++) { double t * = LU[p][j]; LU[p][j] = LU[k][j]; LU[k][j] = t; } int t = piv[p]; piv[p] = * piv[k]; piv[k] = t; pivsign = -pivsign; } // Compute multipliers and * eliminate k-th column. if (LU[k][k] != 0.0) { for (int i = k+1; i < m; i++) * { LU[i][k] /= LU[k][k]; for (int j = k+1; j < n; j++) { LU[i][j] -= * LU[i][k]*LU[k][j]; } } } } } \* ------------------------ End of temporary * code. ------------------------ */ /** * Is the matrix nonsingular? * * @return true if U, and hence A, is nonsingular. */ public boolean isNonsingular () { for (int j = 0; j < m_nCols; j++) if (m_aLU[j][j] == 0) return false; return true; } /** * Return lower triangular factor * * @return L */ @Nonnull @ReturnsMutableCopy public Matrix getL () { final Matrix aNewMatrix = new Matrix (m_nRows, m_nCols); final double [] [] aNewArray = aNewMatrix.internalGetArray (); for (int nRow = 0; nRow < m_nRows; nRow++) { final double [] aSrcRow = m_aLU[nRow]; final double [] aDstRow = aNewArray[nRow]; for (int nCol = 0; nCol < m_nCols; nCol++) if (nRow > nCol) aDstRow[nCol] = aSrcRow[nCol]; else if (nRow == nCol) aDstRow[nCol] = 1.0; else aDstRow[nCol] = 0.0; } return aNewMatrix; } /** * Return upper triangular factor * * @return U */ @Nonnull public Matrix getU () { final Matrix aNewMatrix = new Matrix (m_nCols, m_nCols); final double [] [] aNewArray = aNewMatrix.internalGetArray (); for (int nRow = 0; nRow < m_nCols; nRow++) { final double [] aSrcRow = m_aLU[nRow]; final double [] aDstRow = aNewArray[nRow]; for (int nCol = 0; nCol < m_nCols; nCol++) if (nRow <= nCol) aDstRow[nCol] = aSrcRow[nCol]; else aDstRow[nCol] = 0.0; } return aNewMatrix; } /** * Return pivot permutation vector * * @return piv */ @Nonnull public int [] getPivot () { return Arrays.copyOf (m_aPivot, m_nRows); } /** * Return pivot permutation vector as a one-dimensional double array * * @return (double) piv */ @Nonnull public double [] getDoublePivot () { final double [] vals = new double [m_nRows]; for (int i = 0; i < m_nRows; i++) vals[i] = m_aPivot[i]; return vals; } /** * Determinant * * @return det(A) * @exception IllegalArgumentException * Matrix must be square */ public double det () { if (m_nRows != m_nCols) throw new IllegalArgumentException ("Matrix must be square."); double d = m_nPivSign; for (int j = 0; j < m_nCols; j++) d *= m_aLU[j][j]; return d; } /** * Solve A*X = B * * @param aMatrix * A Matrix with as many rows as A and any number of columns. * @return X so that L*U*X = B(piv,:) * @exception IllegalArgumentException * Matrix row dimensions must agree. * @exception RuntimeException * Matrix is singular. */ @Nonnull @ReturnsMutableCopy public Matrix solve (@Nonnull final Matrix aMatrix) { if (aMatrix.getRowDimension () != m_nRows) throw new IllegalArgumentException ("Matrix row dimensions must agree."); if (!isNonsingular ()) throw new IllegalStateException ("Matrix is singular."); // Copy right hand side with pivoting final int nCols = aMatrix.getColumnDimension (); final Matrix aNewMatrix = aMatrix.getMatrix (m_aPivot, 0, nCols - 1); final double [] [] aNewArray = aNewMatrix.internalGetArray (); // Solve L*Y = B(piv,:) for (int k = 0; k < m_nCols; k++) { final double [] aNewk = aNewArray[k]; for (int i = k + 1; i < m_nCols; i++) { final double [] aLUi = m_aLU[i]; final double [] aNewi = aNewArray[i]; for (int j = 0; j < nCols; j++) aNewi[j] -= aNewk[j] * aLUi[k]; } } // Solve U*X = Y; for (int k = m_nCols - 1; k >= 0; k--) { final double [] aLUk = m_aLU[k]; final double [] aNewk = aNewArray[k]; for (int j = 0; j < nCols; j++) aNewk[j] /= aLUk[k]; for (int i = 0; i < k; i++) { final double [] aLUi = m_aLU[i]; final double [] aNewi = aNewArray[i]; for (int j = 0; j < nCols; j++) aNewi[j] -= aNewk[j] * aLUi[k]; } } return aNewMatrix; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy