//
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
//
// Copyright (c) 2009-2013 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using IStation.Numerics.LinearAlgebra;
using IStation.Numerics.LinearAlgebra.Double;
using IStation.Numerics.LinearAlgebra.Factorization;
using IStation.Numerics.Random;
namespace IStation.Numerics.Distributions
{
///
/// Multivariate Wishart distribution. This distribution is
/// parameterized by the degrees of freedom nu and the scale matrix S. The Wishart distribution
/// is the conjugate prior for the precision (inverse covariance) matrix of the multivariate
/// normal distribution.
/// Wikipedia - Wishart distribution.
///
public class Wishart : IDistribution
{
System.Random _random;
///
/// The degrees of freedom for the Wishart distribution.
///
readonly double _degreesOfFreedom;
///
/// The scale matrix for the Wishart distribution.
///
readonly Matrix _scale;
///
/// Caches the Cholesky factorization of the scale matrix.
///
readonly Cholesky _chol;
///
/// Initializes a new instance of the class.
///
/// The degrees of freedom (n) for the Wishart distribution.
/// The scale matrix (V) for the Wishart distribution.
public Wishart(double degreesOfFreedom, Matrix scale)
{
if (Control.CheckDistributionParameters && !IsValidParameterSet(degreesOfFreedom, scale))
{
throw new ArgumentException("Invalid parametrization for the distribution.");
}
_random = SystemRandomSource.Default;
_degreesOfFreedom = degreesOfFreedom;
_scale = scale;
_chol = _scale.Cholesky();
}
///
/// Initializes a new instance of the class.
///
/// The degrees of freedom (n) for the Wishart distribution.
/// The scale matrix (V) for the Wishart distribution.
/// The random number generator which is used to draw random samples.
public Wishart(double degreesOfFreedom, Matrix scale, System.Random randomSource)
{
if (Control.CheckDistributionParameters && !IsValidParameterSet(degreesOfFreedom, scale))
{
throw new ArgumentException("Invalid parametrization for the distribution.");
}
_random = randomSource ?? SystemRandomSource.Default;
_degreesOfFreedom = degreesOfFreedom;
_scale = scale;
_chol = _scale.Cholesky();
}
///
/// Tests whether the provided values are valid parameters for this distribution.
///
/// The degrees of freedom (n) for the Wishart distribution.
/// The scale matrix (V) for the Wishart distribution.
public static bool IsValidParameterSet(double degreesOfFreedom, Matrix scale)
{
if (scale.RowCount != scale.ColumnCount)
{
return false;
}
for (var i = 0; i < scale.RowCount; i++)
{
if (scale.At(i, i) <= 0.0)
{
return false;
}
}
if (degreesOfFreedom <= 0.0 || double.IsNaN(degreesOfFreedom))
{
return false;
}
return true;
}
///
/// Gets or sets the degrees of freedom (n) for the Wishart distribution.
///
public double DegreesOfFreedom => _degreesOfFreedom;
///
/// Gets or sets the scale matrix (V) for the Wishart distribution.
///
public Matrix Scale => _scale;
///
/// A string representation of the distribution.
///
/// a string representation of the distribution.
public override string ToString()
{
return $"Wishart(DegreesOfFreedom = {_degreesOfFreedom}, Rows = {_scale.RowCount}, Columns = {_scale.ColumnCount})";
}
///
/// Gets or sets the random number generator which is used to draw random samples.
///
public System.Random RandomSource
{
get => _random;
set => _random = value ?? SystemRandomSource.Default;
}
///
/// Gets the mean of the distribution.
///
/// The mean of the distribution.
public Matrix Mean => _degreesOfFreedom*_scale;
///
/// Gets the mode of the distribution.
///
/// The mode of the distribution.
public Matrix Mode => (_degreesOfFreedom - _scale.RowCount - 1.0)*_scale;
///
/// Gets the variance of the distribution.
///
/// The variance of the distribution.
public Matrix Variance
{
get
{
return Matrix.Build.Dense(_scale.RowCount, _scale.ColumnCount,
(i, j) => _degreesOfFreedom*((_scale.At(i, j)*_scale.At(i, j)) + (_scale.At(i, i)*_scale.At(j, j))));
}
}
///
/// Evaluates the probability density function for the Wishart distribution.
///
/// The matrix at which to evaluate the density at.
/// If the argument does not have the same dimensions as the scale matrix.
/// the density at .
public double Density(Matrix x)
{
var p = _scale.RowCount;
if (x.RowCount != p || x.ColumnCount != p)
{
throw Matrix.DimensionsDontMatch(x, _scale, "x");
}
var dX = x.Determinant();
var siX = _chol.Solve(x);
// Compute the multivariate Gamma function.
var gp = Math.Pow(Constants.Pi, p*(p - 1.0)/4.0);
for (var j = 1; j <= p; j++)
{
gp *= SpecialFunctions.Gamma((_degreesOfFreedom + 1.0 - j)/2.0);
}
return Math.Pow(dX, (_degreesOfFreedom - p - 1.0)/2.0)
*Math.Exp(-0.5*siX.Trace())
/Math.Pow(2.0, _degreesOfFreedom*p/2.0)
/Math.Pow(_chol.Determinant, _degreesOfFreedom/2.0)
/gp;
}
///
/// Samples a Wishart distributed random variable using the method
/// Algorithm AS 53: Wishart Variate Generator
/// W. B. Smith and R. R. Hocking
/// Applied Statistics, Vol. 21, No. 3 (1972), pp. 341-345
///
/// A random number from this distribution.
public Matrix Sample()
{
return DoSample(RandomSource, _degreesOfFreedom, _scale, _chol);
}
///
/// Samples a Wishart distributed random variable using the method
/// Algorithm AS 53: Wishart Variate Generator
/// W. B. Smith and R. R. Hocking
/// Applied Statistics, Vol. 21, No. 3 (1972), pp. 341-345
///
/// The random number generator to use.
/// The degrees of freedom (n) for the Wishart distribution.
/// The scale matrix (V) for the Wishart distribution.
/// a sequence of samples from the distribution.
public static Matrix Sample(System.Random rnd, double degreesOfFreedom, Matrix scale)
{
if (Control.CheckDistributionParameters && !IsValidParameterSet(degreesOfFreedom, scale))
{
throw new ArgumentException("Invalid parametrization for the distribution.");
}
return DoSample(rnd, degreesOfFreedom, scale, scale.Cholesky());
}
///
/// Samples the distribution.
///
/// The random number generator to use.
/// The degrees of freedom (n) for the Wishart distribution.
/// The scale matrix (V) for the Wishart distribution.
/// The cholesky decomposition to use.
/// a random number from the distribution.
static Matrix DoSample(System.Random rnd, double degreesOfFreedom, Matrix scale, Cholesky chol)
{
var count = scale.RowCount;
// First generate a lower triangular matrix with Sqrt(Chi-Squares) on the diagonal
// and normal distributed variables in the lower triangle.
var a = new DenseMatrix(count, count);
for (var d = 0; d < count; d++)
{
a.At(d, d, Math.Sqrt(Gamma.Sample(rnd, (degreesOfFreedom - d)/2.0, 0.5)));
}
for (var i = 1; i < count; i++)
{
for (var j = 0; j < i; j++)
{
a.At(i, j, Normal.Sample(rnd, 0.0, 1.0));
}
}
var factor = chol.Factor;
return factor*a*a.Transpose()*factor.Transpose();
}
}
}