using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace VisualMath.Accord.Math.Decompositions { using System; /// /// Cholesky Decomposition of a symmetric, positive definite matrix. /// /// /// /// For a symmetric, positive definite matrix A, the Cholesky decomposition is a /// 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 two internal variables that can be queried using the /// and properties. /// /// Any square matrix A with non-zero pivots can be written as the product of a /// lower triangular matrix L and an upper triangular matrix U; this is called /// the LU decomposition. However, if A is symmetric and positive definite, we /// can choose the factors such that U is the transpose of L, and this is called /// the Cholesky decomposition. Both the LU and the Cholesky decomposition are /// used to solve systems of linear equations. /// /// When it is applicable, the Cholesky decomposition is twice as efficient /// as the LU decomposition. /// /// public sealed class CholeskyDecomposition : ICloneable { private double[,] L; private bool symmetric; private bool positiveDefinite; /// Constructs a Cholesky Decomposition. public CholeskyDecomposition(double[,] value) { if (value == null) { throw new ArgumentNullException("value", "Matrix cannot be null."); } if (value.GetLength(0) != value.GetLength(1)) { throw new ArgumentException("Matrix is not square.", "value"); } int dimension = value.GetLength(0); L = new double[dimension, dimension]; double[,] a = value; this.positiveDefinite = true; this.symmetric = true; unsafe { fixed (double* l = L) { for (int j = 0; j < dimension; j++) { double* Lrowj = l + j * dimension; double d = 0.0; for (int k = 0; k < j; k++) { double* Lrowk = l + k * dimension; double s = 0.0; for (int i = 0; i < k; i++) { s += Lrowk[i] * Lrowj[i]; } Lrowj[k] = s = (a[j, k] - s) / Lrowk[k]; d = d + s * s; this.symmetric = this.symmetric & (a[k, j] == a[j, k]); } d = a[j, j] - d; this.positiveDefinite = this.positiveDefinite & (d > 0.0); Lrowj[j] = System.Math.Sqrt(System.Math.Max(d, 0.0)); for (int k = j + 1; k < dimension; k++) { Lrowj[k] = 0.0; } } } } } /// Returns if the matrix is symmetric. public bool Symmetric { get { return this.symmetric; } } /// Returns if the matrix is positive definite. public bool PositiveDefinite { get { return this.positiveDefinite; } } /// Returns the left triangular factor L so that A = L * L'. public double[,] LeftTriangularFactor { get { return this.L; } } /// Solves a set of equation systems of type A * X = B. /// Right hand side matrix with as many rows as A and any number of columns. /// Matrix X so that L * L' * X = B. /// Matrix dimensions do not match. /// Matrix is not symmetric and positive definite. public double[,] Solve(double[,] value) { if (value == null) { throw new ArgumentNullException("value"); } if (value.GetLength(0) != L.GetLength(0)) { throw new ArgumentException("Matrix dimensions do not match."); } if (!this.symmetric) { throw new InvalidOperationException("Matrix is not symmetric."); } if (!this.positiveDefinite) { throw new InvalidOperationException("Matrix is not positive definite."); } int dimension = L.GetLength(0); int count = value.GetLength(1); double[,] B = (double[,])value.Clone(); // Solve L*Y = B; for (int k = 0; k < dimension; k++) { for (int j = 0; j < count; j++) { for (int i = 0; i < k; i++) { B[k, j] -= B[i, j] * L[k, i]; } B[k, j] /= L[k, k]; } } // Solve L'*X = Y; for (int k = dimension - 1; k >= 0; k--) { for (int j = 0; j < count; j++) { for (int i = k + 1; i < dimension; i++) { B[k, j] -= B[i, j] * L[i, k]; } B[k, j] /= L[k, k]; } } return B; } /// Solves a set of equation systems of type A * x = b. /// Right hand side column vector with as many rows as A. /// Vector x so that L * L' * x = b. /// Matrix dimensions do not match. /// Matrix is not symmetric and positive definite. public double[] Solve(double[] value) { if (value == null) { throw new ArgumentNullException("value"); } if (value.Length != L.GetLength(0)) { throw new ArgumentException("Matrix dimensions do not match."); } if (!this.symmetric) { throw new InvalidOperationException("Matrix is not symmetric."); } if (!this.positiveDefinite) { throw new InvalidOperationException("Matrix is not positive definite."); } int dimension = L.GetLength(0); double[] B = (double[])value.Clone(); // Solve L*Y = B; for (int k = 0; k < dimension; k++) { for (int i = 0; i < k; i++) B[k] -= B[i] * L[k, i]; B[k] /= L[k, k]; } // Solve L'*X = Y; for (int k = dimension - 1; k >= 0; k--) { for (int i = k + 1; i < dimension; i++) B[k] -= B[i] * L[i, k]; B[k] /= L[k, k]; } return B; } #region ICloneable Members private CholeskyDecomposition() { } /// /// Creates a new object that is a copy of the current instance. /// /// /// A new object that is a copy of this instance. /// public object Clone() { var clone = new CholeskyDecomposition(); clone.L = (double[,])L.Clone(); clone.positiveDefinite = positiveDefinite; clone.symmetric = symmetric; return clone; } #endregion } }