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

cern.colt.matrix.tfloat.algo.decomposition.DenseFloatLUDecompositionQuick Maven / Gradle / Ivy

Go to download

Parallel Colt is a multithreaded version of Colt - a library for high performance scientific computing in Java. It contains efficient algorithms for data analysis, linear algebra, multi-dimensional arrays, Fourier transforms, statistics and histogramming.

The newest version!
/*
Copyright (C) 1999 CERN - European Organization for Nuclear Research.
Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose 
is hereby granted without fee, provided that the above copyright notice appear in all copies and 
that both that copyright notice and this permission notice appear in supporting documentation. 
CERN makes no representations about the suitability of this software for any purpose. 
It is provided "as is" without expressed or implied warranty.
 */
package cern.colt.matrix.tfloat.algo.decomposition;

import cern.colt.matrix.tfloat.FloatMatrix1D;
import cern.colt.matrix.tfloat.FloatMatrix2D;
import cern.colt.matrix.tfloat.algo.DenseFloatAlgebra;
import cern.colt.matrix.tfloat.algo.FloatProperty;

/**
 * A low level version of {@link DenseFloatLUDecomposition}, avoiding
 * unnecessary memory allocation and copying. The input to decompose
 * methods is overriden with the result (LU). The input to solve
 * methods is overriden with the result (X). In addition to
 * LUDecomposition, this class also includes a faster variant of the
 * decomposition, specialized for tridiagonal (and hence also diagonal)
 * matrices, as well as a solver tuned for vectors. Its disadvantage is that it
 * is a bit more difficult to use than LUDecomposition. Thus, you may
 * want to disregard this class and come back later, if a need for speed arises.
 * 

* An instance of this class remembers the result of its last decomposition. * Usage pattern is as follows: Create an instance of this class, call a * decompose method, then retrieve the decompositions, determinant, and/or solve * as many equation problems as needed. Once another matrix needs to be * LU-decomposed, you need not create a new instance of this class. Start again * by calling a decompose method, then retrieve the decomposition and/or solve * your equations, and so on. In case a LU matrix is already available, * call method setLU instead of decompose and proceed with * solving et al. *

* If a matrix shall not be overriden, use matrix.copy() and hand the * the copy to methods. *

* For an m x n matrix A with m >= n, the LU * decomposition is an m x n unit lower triangular matrix L, * an n x 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 x m and U is * m x n. *

* The LU decomposition with pivoting always exists, even if the matrix is * singular, so the decompose methods will never fail. The primary use of the LU * decomposition is in the solution of square systems of simultaneous linear * equations. Attempting to solve such a system will throw an exception if * isNonsingular() returns false. *

*/ public class DenseFloatLUDecompositionQuick implements java.io.Serializable { static final long serialVersionUID = 1020; /** * Array for internal storage of decomposition. * * @serial internal array storage. */ protected FloatMatrix2D LU; /** * pivot sign. * * @serial pivot sign. */ protected int pivsign; /** * Internal storage of pivot vector. * * @serial pivot vector. */ protected int[] piv; protected boolean isNonSingular; protected DenseFloatAlgebra algebra; transient protected float[] workFloat; transient protected int[] work1; transient protected int[] work2; /** * Constructs and returns a new LU Decomposition object with default * tolerance 1.0E-9 for singularity detection. */ public DenseFloatLUDecompositionQuick() { this(FloatProperty.DEFAULT.tolerance()); } /** * Constructs and returns a new LU Decomposition object which uses the given * tolerance for singularity detection; */ public DenseFloatLUDecompositionQuick(float tolerance) { this.algebra = new DenseFloatAlgebra(tolerance); } /** * Decomposes matrix A into L and U (in-place). * Upon return A is overridden with the result LU, such * that L*U = A. Uses a "left-looking", dot-product, * Crout/Doolittle algorithm. * * @param A * any matrix. */ public void decompose(FloatMatrix2D A) { final int CUT_OFF = 10; // setup LU = A; int m = A.rows(); int n = A.columns(); // setup pivot vector if (this.piv == null || this.piv.length != m) this.piv = new int[m]; for (int i = m; --i >= 0;) piv[i] = i; pivsign = 1; if (m * n == 0) { setLU(LU); return; // nothing to do } // precompute and cache some views to avoid regenerating them time and // again FloatMatrix1D[] LUrows = new FloatMatrix1D[m]; for (int i = 0; i < m; i++) LUrows[i] = LU.viewRow(i); cern.colt.list.tint.IntArrayList nonZeroIndexes = new cern.colt.list.tint.IntArrayList(); // sparsity FloatMatrix1D LUcolj = LU.viewColumn(0).like(); // blocked column j cern.jet.math.tfloat.FloatMult multFunction = cern.jet.math.tfloat.FloatMult.mult(0); // Outer loop. for (int j = 0; j < n; j++) { // blocking (make copy of j-th column to localize references) LUcolj.assign(LU.viewColumn(j)); // sparsity detection int maxCardinality = m / CUT_OFF; // == heuristic depending on // speedup LUcolj.getNonZeros(nonZeroIndexes, null, maxCardinality); int cardinality = nonZeroIndexes.size(); boolean sparse = (cardinality < maxCardinality); // Apply previous transformations. for (int i = 0; i < m; i++) { int kmax = Math.min(i, j); float s; if (sparse) { s = LUrows[i].zDotProduct(LUcolj, 0, kmax, nonZeroIndexes); } else { s = LUrows[i].zDotProduct(LUcolj, 0, kmax); } float before = LUcolj.getQuick(i); float after = before - s; LUcolj.setQuick(i, after); // LUcolj is a copy LU.setQuick(i, j, after); // this is the original if (sparse) { if (before == 0 && after != 0) { // nasty bug fixed! int pos = nonZeroIndexes.binarySearch(i); pos = -pos - 1; nonZeroIndexes.beforeInsert(pos, i); } if (before != 0 && after == 0) { nonZeroIndexes.remove(nonZeroIndexes.binarySearch(i)); } } } // Find pivot and exchange if necessary. int p = j; if (p < m) { float max = Math.abs(LUcolj.getQuick(p)); for (int i = j + 1; i < m; i++) { float v = Math.abs(LUcolj.getQuick(i)); if (v > max) { p = i; max = v; } } } if (p != j) { LUrows[p].swap(LUrows[j]); int k = piv[p]; piv[p] = piv[j]; piv[j] = k; pivsign = -pivsign; } // Compute multipliers. float jj; if (j < m && (jj = LU.getQuick(j, j)) != 0.0) { multFunction.multiplicator = 1 / jj; LU.viewColumn(j).viewPart(j + 1, m - (j + 1)).assign(multFunction); } } setLU(LU); } /** * Decomposes the banded and square matrix A into L and * U (in-place). Upon return A is overridden with the * result LU, such that L*U = A. Currently supports * diagonal and tridiagonal matrices, all other cases fall through to * {@link #decompose(FloatMatrix2D)}. * * @param semiBandwidth * == 1 --> A is diagonal, == 2 --> A is tridiagonal. * @param A * any matrix. */ public void decompose(FloatMatrix2D A, int semiBandwidth) { if (!algebra.property().isSquare(A) || semiBandwidth < 0 || semiBandwidth > 2) { decompose(A); return; } // setup LU = A; int m = A.rows(); int n = A.columns(); // setup pivot vector if (this.piv == null || this.piv.length != m) this.piv = new int[m]; for (int i = m; --i >= 0;) piv[i] = i; pivsign = 1; if (m * n == 0) { setLU(A); return; // nothing to do } // if (semiBandwidth == 1) { // A is diagonal; nothing to do if (semiBandwidth == 2) { // A is tridiagonal // currently no pivoting ! if (n > 1) A.setQuick(1, 0, A.getQuick(1, 0) / A.getQuick(0, 0)); for (int i = 1; i < n; i++) { float ei = A.getQuick(i, i) - A.getQuick(i, i - 1) * A.getQuick(i - 1, i); A.setQuick(i, i, ei); if (i < n - 1) A.setQuick(i + 1, i, A.getQuick(i + 1, i) / ei); } } setLU(A); } /** * Returns the determinant, det(A). * * @exception IllegalArgumentException * if A.rows() != A.columns() (Matrix must be * square). */ public float det() { int m = m(); int n = n(); if (m != n) throw new IllegalArgumentException("Matrix must be square."); if (!isNonsingular()) return 0; // avoid rounding errors float det = pivsign; for (int j = 0; j < n; j++) { det *= LU.getQuick(j, j); } return det; } /** * Returns pivot permutation vector as a one-dimensional float array * * @return (float) piv */ protected float[] getFloatPivot() { int m = m(); float[] vals = new float[m]; for (int i = 0; i < m; i++) { vals[i] = piv[i]; } return vals; } /** * Returns the lower triangular factor, L. * * @return L */ public FloatMatrix2D getL() { return lowerTriangular(LU.copy()); } /** * Returns a copy of the combined lower and upper triangular factor, * LU. * * @return LU */ public FloatMatrix2D getLU() { return LU.copy(); } /** * Returns the pivot permutation vector (not a copy of it). * * @return piv */ public int[] getPivot() { return piv; } /** * Returns the upper triangular factor, U. * * @return U */ public FloatMatrix2D getU() { return upperTriangular(LU.copy()); } /** * Returns whether the matrix is nonsingular (has an inverse). * * @return true if U, and hence A, is nonsingular; false * otherwise. */ public boolean isNonsingular() { return isNonSingular; } /** * Returns whether the matrix is nonsingular. * * @return true if matrix is nonsingular; false otherwise. */ protected boolean isNonsingular(FloatMatrix2D matrix) { int m = matrix.rows(); int n = matrix.columns(); float epsilon = algebra.property().tolerance(); // consider // numerical // instability for (int j = Math.min(n, m); --j >= 0;) { // if (matrix.getQuick(j,j) == 0) return false; if (Math.abs(matrix.getQuick(j, j)) <= epsilon) return false; } return true; } /** * Modifies the matrix to be a lower triangular matrix. *

* Examples: *

* * * * * * * * * * * * * * * * * * * * *
3 x 5 matrix:
* 9, 9, 9, 9, 9
* 9, 9, 9, 9, 9
* 9, 9, 9, 9, 9
triang.Upper
* ==>
3 x 5 matrix:
* 9, 9, 9, 9, 9
* 0, 9, 9, 9, 9
* 0, 0, 9, 9, 9
5 x 3 matrix:
* 9, 9, 9
* 9, 9, 9
* 9, 9, 9
* 9, 9, 9
* 9, 9, 9
triang.Upper
* ==>
5 x 3 matrix:
* 9, 9, 9
* 0, 9, 9
* 0, 0, 9
* 0, 0, 0
* 0, 0, 0
3 x 5 matrix:
* 9, 9, 9, 9, 9
* 9, 9, 9, 9, 9
* 9, 9, 9, 9, 9
triang.Lower
* ==>
3 x 5 matrix:
* 1, 0, 0, 0, 0
* 9, 1, 0, 0, 0
* 9, 9, 1, 0, 0
5 x 3 matrix:
* 9, 9, 9
* 9, 9, 9
* 9, 9, 9
* 9, 9, 9
* 9, 9, 9
triang.Lower
* ==>
5 x 3 matrix:
* 1, 0, 0
* 9, 1, 0
* 9, 9, 1
* 9, 9, 9
* 9, 9, 9
* * @return A (for convenience only). * @see #triangulateUpper(FloatMatrix2D) */ protected FloatMatrix2D lowerTriangular(FloatMatrix2D A) { int rows = A.rows(); int columns = A.columns(); int min = Math.min(rows, columns); for (int r = min; --r >= 0;) { for (int c = min; --c >= 0;) { if (r < c) A.setQuick(r, c, 0); else if (r == c) A.setQuick(r, c, 1); } } if (columns > rows) A.viewPart(0, min, rows, columns - min).assign(0); return A; } /** * */ protected int m() { return LU.rows(); } /** * */ protected int n() { return LU.columns(); } /** * Sets the combined lower and upper triangular factor, LU. The * parameter is not checked; make sure it is indeed a proper LU * decomposition. */ public void setLU(FloatMatrix2D LU) { this.LU = LU; this.isNonSingular = isNonsingular(LU); } /** * Solves the system of equations A*X = B (in-place). Upon return * B is overridden with the result X, such that * L*U*X = B(piv). * * @param B * A vector with B.size() == A.rows(). * @exception IllegalArgumentException * if B.size() != A.rows(). * @exception IllegalArgumentException * if A is singular, that is, if !isNonsingular(). * @exception IllegalArgumentException * if A.rows() < A.columns(). */ public void solve(FloatMatrix1D B) { algebra.property().checkRectangular(LU); int m = m(); int n = n(); if (B.size() != m) throw new IllegalArgumentException("Matrix dimensions must agree."); if (!this.isNonsingular()) throw new IllegalArgumentException("Matrix is singular."); // right hand side with pivoting // Matrix Xmat = B.getMatrix(piv,0,nx-1); if (this.workFloat == null || this.workFloat.length < m) this.workFloat = new float[m]; algebra.permute(B, this.piv, this.workFloat); if (m * n == 0) return; // nothing to do // Solve L*Y = B(piv,:) for (int k = 0; k < n; k++) { float f = B.getQuick(k); if (f != 0) { for (int i = k + 1; i < n; i++) { // B[i] -= B[k]*LU[i][k]; float v = LU.getQuick(i, k); if (v != 0) B.setQuick(i, B.getQuick(i) - f * v); } } } // Solve U*B = Y; for (int k = n - 1; k >= 0; k--) { // B[k] /= LU[k,k] B.setQuick(k, B.getQuick(k) / LU.getQuick(k, k)); float f = B.getQuick(k); if (f != 0) { for (int i = 0; i < k; i++) { // B[i] -= B[k]*LU[i][k]; float v = LU.getQuick(i, k); if (v != 0) B.setQuick(i, B.getQuick(i) - f * v); } } } } /** * Solves the system of equations A*X = B (in-place). Upon return * B is overridden with the result X, such that * L*U*X = B(piv,:). * * @param B * A matrix with as many rows as A and any number of * columns. * @exception IllegalArgumentException * if B.rows() != A.rows(). * @exception IllegalArgumentException * if A is singular, that is, if !isNonsingular(). * @exception IllegalArgumentException * if A.rows() < A.columns(). */ public void solve(FloatMatrix2D B) { final int CUT_OFF = 10; algebra.property().checkRectangular(LU); int m = m(); int n = n(); if (B.rows() != m) throw new IllegalArgumentException("Matrix row dimensions must agree."); if (!this.isNonsingular()) throw new IllegalArgumentException("Matrix is singular."); // right hand side with pivoting // Matrix Xmat = B.getMatrix(piv,0,nx-1); if (this.work1 == null || this.work1.length < m) this.work1 = new int[m]; // if (this.work2 == null || this.work2.length < m) this.work2 = new // int[m]; algebra.permuteRows(B, this.piv, this.work1); if (m * n == 0) return; // nothing to do int nx = B.columns(); // precompute and cache some views to avoid regenerating them time and // again FloatMatrix1D[] Brows = new FloatMatrix1D[n]; for (int k = 0; k < n; k++) Brows[k] = B.viewRow(k); // transformations cern.jet.math.tfloat.FloatMult div = cern.jet.math.tfloat.FloatMult.div(0); cern.jet.math.tfloat.FloatPlusMultSecond minusMult = cern.jet.math.tfloat.FloatPlusMultSecond.minusMult(0); cern.colt.list.tint.IntArrayList nonZeroIndexes = new cern.colt.list.tint.IntArrayList(); // sparsity FloatMatrix1D Browk = cern.colt.matrix.tfloat.FloatFactory1D.dense.make(nx); // blocked // row // k // Solve L*Y = B(piv,:) for (int k = 0; k < n; k++) { // blocking (make copy of k-th row to localize references) Browk.assign(Brows[k]); // sparsity detection int maxCardinality = nx / CUT_OFF; // == heuristic depending on // speedup Browk.getNonZeros(nonZeroIndexes, null, maxCardinality); int cardinality = nonZeroIndexes.size(); boolean sparse = (cardinality < maxCardinality); for (int i = k + 1; i < n; i++) { // for (int j = 0; j < nx; j++) B[i][j] -= B[k][j]*LU[i][k]; // for (int j = 0; j < nx; j++) B.set(i,j, B.get(i,j) - // B.get(k,j)*LU.get(i,k)); minusMult.multiplicator = -LU.getQuick(i, k); if (minusMult.multiplicator != 0) { if (sparse) { Brows[i].assign(Browk, minusMult, nonZeroIndexes); } else { Brows[i].assign(Browk, minusMult); } } } } // Solve U*B = Y; for (int k = n - 1; k >= 0; k--) { // for (int j = 0; j < nx; j++) B[k][j] /= LU[k][k]; // for (int j = 0; j < nx; j++) B.set(k,j, B.get(k,j) / // LU.get(k,k)); div.multiplicator = 1 / LU.getQuick(k, k); Brows[k].assign(div); // blocking if (Browk == null) Browk = cern.colt.matrix.tfloat.FloatFactory1D.dense.make(B.columns()); Browk.assign(Brows[k]); // sparsity detection int maxCardinality = nx / CUT_OFF; // == heuristic depending on // speedup Browk.getNonZeros(nonZeroIndexes, null, maxCardinality); int cardinality = nonZeroIndexes.size(); boolean sparse = (cardinality < maxCardinality); // Browk.getNonZeros(nonZeroIndexes,null); // boolean sparse = nonZeroIndexes.size() < nx/10; for (int i = 0; i < k; i++) { // for (int j = 0; j < nx; j++) B[i][j] -= B[k][j]*LU[i][k]; // for (int j = 0; j < nx; j++) B.set(i,j, B.get(i,j) - // B.get(k,j)*LU.get(i,k)); minusMult.multiplicator = -LU.getQuick(i, k); if (minusMult.multiplicator != 0) { if (sparse) { Brows[i].assign(Browk, minusMult, nonZeroIndexes); } else { Brows[i].assign(Browk, minusMult); } } } } } /** * Solves A*X = B. * * @param B * A matrix with as many rows as A and any number of * columns. * @return X so that L*U*X = B(piv,:). * @exception IllegalArgumentException * if B.rows() != A.rows(). * @exception IllegalArgumentException * if A is singular, that is, if * !this.isNonsingular(). * @exception IllegalArgumentException * if A.rows() < A.columns(). */ private void solveOld(FloatMatrix2D B) { algebra.property().checkRectangular(LU); int m = m(); int n = n(); if (B.rows() != m) throw new IllegalArgumentException("Matrix row dimensions must agree."); if (!this.isNonsingular()) throw new IllegalArgumentException("Matrix is singular."); // Copy right hand side with pivoting int nx = B.columns(); if (this.work1 == null || this.work1.length < m) this.work1 = new int[m]; // if (this.work2 == null || this.work2.length < m) this.work2 = new // int[m]; algebra.permuteRows(B, this.piv, this.work1); // Solve L*Y = B(piv,:) --> Y (Y is modified B) for (int k = 0; k < n; k++) { for (int i = k + 1; i < n; i++) { float mult = LU.getQuick(i, k); if (mult != 0) { for (int j = 0; j < nx; j++) { // B[i][j] -= B[k][j]*LU[i,k]; B.setQuick(i, j, B.getQuick(i, j) - B.getQuick(k, j) * mult); } } } } // Solve U*X = Y; --> X (X is modified B) for (int k = n - 1; k >= 0; k--) { float mult = 1 / LU.getQuick(k, k); if (mult != 1) { for (int j = 0; j < nx; j++) { // B[k][j] /= LU[k][k]; B.setQuick(k, j, B.getQuick(k, j) * mult); } } for (int i = 0; i < k; i++) { mult = LU.getQuick(i, k); if (mult != 0) { for (int j = 0; j < nx; j++) { // B[i][j] -= B[k][j]*LU[i][k]; B.setQuick(i, j, B.getQuick(i, j) - B.getQuick(k, j) * mult); } } } } } /** * Returns a String with (propertyName, propertyValue) pairs. Useful for * debugging or to quickly get the rough picture. For example, * *
     * 	 rank          : 3
     * 	 trace         : 0
     * 
     * 
*/ public String toString() { StringBuffer buf = new StringBuffer(); String unknown = "Illegal operation or error: "; buf.append("-----------------------------------------------------------------------------\n"); buf.append("LUDecompositionQuick(A) --> isNonSingular(A), det(A), pivot, L, U, inverse(A)\n"); buf.append("-----------------------------------------------------------------------------\n"); buf.append("isNonSingular = "); try { buf.append(String.valueOf(this.isNonsingular())); } catch (IllegalArgumentException exc) { buf.append(unknown + exc.getMessage()); } buf.append("\ndet = "); try { buf.append(String.valueOf(this.det())); } catch (IllegalArgumentException exc) { buf.append(unknown + exc.getMessage()); } buf.append("\npivot = "); try { buf.append(String.valueOf(new cern.colt.list.tint.IntArrayList(this.getPivot()))); } catch (IllegalArgumentException exc) { buf.append(unknown + exc.getMessage()); } buf.append("\n\nL = "); try { buf.append(String.valueOf(this.getL())); } catch (IllegalArgumentException exc) { buf.append(unknown + exc.getMessage()); } buf.append("\n\nU = "); try { buf.append(String.valueOf(this.getU())); } catch (IllegalArgumentException exc) { buf.append(unknown + exc.getMessage()); } buf.append("\n\ninverse(A) = "); FloatMatrix2D identity = cern.colt.matrix.tfloat.FloatFactory2D.dense.identity(LU.rows()); try { this.solve(identity); buf.append(String.valueOf(identity)); } catch (IllegalArgumentException exc) { buf.append(unknown + exc.getMessage()); } return buf.toString(); } /** * Modifies the matrix to be an upper triangular matrix. * * @return A (for convenience only). * @see #triangulateLower(FloatMatrix2D) */ protected FloatMatrix2D upperTriangular(FloatMatrix2D A) { int rows = A.rows(); int columns = A.columns(); int min = Math.min(rows, columns); for (int r = min; --r >= 0;) { for (int c = min; --c >= 0;) { if (r > c) A.setQuick(r, c, 0); } } if (columns < rows) A.viewPart(min, 0, rows - min, columns).assign(0); return A; } /** * Returns pivot permutation vector as a one-dimensional float array * * @return (float) piv */ private float[] xgetFloatPivot() { int m = m(); float[] vals = new float[m]; for (int i = 0; i < m; i++) { vals[i] = piv[i]; } return vals; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy