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
}
}