/*
Copyright (C) 2009 Richard Gomes
This source code is release under the BSD License.
This file is part of JQuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://jquantlib.org/
JQuantLib is free software: you can redistribute it and/or modify it
under the terms of the JQuantLib license. You should have received a
copy of the license along with this program; if not, please email
. The license is also available online at
.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
JQuantLib is based on QuantLib. http://quantlib.org/
When applicable, the original copyright notice follows this notice.
*/
/*
Copyright (C) 2003, 2004 Ferdinando Ametrano
This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/
QuantLib is free software: you can redistribute it and/or modify it
under the terms of the QuantLib license. You should have received a
copy of the license along with this program; if not, please email
. The license is also available online at
.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
*/
package org.jquantlib.math.matrixutilities;
import org.jquantlib.QL;
import org.jquantlib.lang.annotation.QualityAssurance;
import org.jquantlib.lang.annotation.QualityAssurance.Quality;
import org.jquantlib.lang.annotation.QualityAssurance.Version;
import org.jquantlib.lang.exceptions.LibraryException;
/**
* Cholesky Decomposition.
*
* For a symmetric, positive definite matrix A, the Cholesky decomposition is an lower triangular matrix L so that A = L*L'.
*
* If the matrix is not symmetric or positive definite, the constructor returns a partial decomposition and sets an internal flag
* that may be queried by the isSPD() method.
*
* @note This class is adapted from JAMA
* @see JAMA
*
* @author Richard Gomes
*/
// FIXME: http://bugs.jquantlib.org/view.php?id=340
@QualityAssurance(quality = Quality.Q1_TRANSLATION, version = Version.OTHER, reviewers = { "Richard Gomes" })
public class CholeskyDecomposition {
private final static String MATRIX_IS_NOT_SIMMETRIC_POSITIVE = "Matrix is not symmetric positive definite.";
//
// private fields
//
/** Row and column dimension (square matrix). */
private final int n;
/** Matrix for internal storage of decomposition. */
private final Matrix L;
/** Symmetric and positive definite flag. */
private boolean isspd;
//
// public constructors
//
/**
* Cholesky algorithm for symmetric and positive definite matrix.
*
* @param A is a square, symmetric matrix.
* @return Structure to access L and isspd flag.
*/
public CholeskyDecomposition(final Matrix A) {
QL.require(A.rows() == A.cols(), Matrix.MATRIX_MUST_BE_SQUARE); // QA:[RG]::verified
this.n = A.rows();
this.L = new Matrix(n, n);
this.isspd = (A.rows() == A.cols());
// Main loop.
for (int j = 0; j < n; j++) {
double d = 0.0;
for (int k = 0; k < j; k++) {
double s = 0.0;
for (int i = 0; i < k; i++) {
s += L.$[L.addr.op(k, i)] * L.$[L.addr.op(j, i)];
}
L.$[L.addr.op(j, k)] = s = (A.$[A.addr.op(j, k)] - s) / L.$[L.addr.op(k, k)];
d = d + s * s;
isspd = isspd & (A.$[A.addr.op(k, j)] == A.$[A.addr.op(j, k)]);
}
d = A.$[A.addr.op(j, j)] - d;
isspd = isspd && (d > 0.0); //FINDBUGS:: NS_DANGEROUS_NON_SHORT_CIRCUIT (solved)
L.$[L.addr.op(j, j)] = Math.sqrt(Math.max(d, 0.0));
for (int k = j + 1; k < n; k++) {
L.$[L.addr.op(j, k)] = 0.0;
}
}
}
//
// public methods
//
/**
* Is the matrix symmetric and positive definite?
*
* @return true if A is symmetric and positive definite.
*/
public boolean isSPD() {
return isspd;
}
/**
* Return triangular factor.
*
* @return L
*/
public Matrix L() {
return L.clone();
}
/**
* Solve A*X = B
*
* @param m a Matrix with as many rows as A and any number of columns.
* @return X so that L*L'*X = B
* @exception IllegalArgumentException Matrix row dimensions must agree.
* @exception LibraryException Matrix is not symmetric positive definite.
*/
public Matrix solve(final Matrix B) {
QL.require(B.rows() == this.n, Matrix.MATRIX_IS_INCOMPATIBLE); // QA:[RG]::verified
if (!this.isSPD())
throw new LibraryException(MATRIX_IS_NOT_SIMMETRIC_POSITIVE);
// Copy right hand side.
final int nx = B.cols();
final Matrix X = B.clone();
// Solve L*Y = B;
for (int k = 0; k < n; k++) {
for (int j = 0; j < nx; j++) {
for (int i = 0; i < k; i++) {
X.$[X.addr.op(k, j)] -= X.$[X.addr.op(i, j)] * L.$[L.addr.op(k, i)];
}
X.$[X.addr.op(k, j)] /= L.$[L.addr.op(k, k)];
}
}
// Solve L'*X = Y;
for (int k = n - 1; k >= 0; k--) {
for (int j = 0; j < nx; j++) {
for (int i = k + 1; i < n; i++) {
X.$[X.addr.op(k, j)] -= X.$[X.addr.op(i, j)] * L.$[L.addr.op(i, k)];
}
X.$[X.addr.op(k, j)] /= L.$[L.addr.op(k, k)];
}
}
return X;
}
}
Ads help maintain this website.