1- # Wishart distribution
2- #
3- # following the Wikipedia parameterization
4- #
51"""
62 Wishart(ν, S)
73```julia
8- ν::Real degrees of freedom (greater than p - 1)
4+ ν::Real degrees of freedom (whole number or a real number greater than p - 1)
95S::AbstractPDMat p x p scale matrix
106```
117The [Wishart distribution](http://en.wikipedia.org/wiki/Wishart_distribution)
12- generalizes the gamma distribution to ``p\\ times p`` real, positive definite
13- matrices ``\\ mathbf{H}``. If ``\\ mathbf{H}\\ sim \\ textrm{W}_p(\\ nu,\\ mathbf{S})``,
14- then its probability density function is
8+ generalizes the gamma distribution to ``p\\ times p`` real, positive semidefinite
9+ matrices ``\\ mathbf{H}``.
10+
11+ If ``\\ nu>p-1``, then ``\\ mathbf{H}\\ sim \\ textrm{W}_p(\\ nu, \\ mathbf{S})``
12+ has rank ``p`` and its probability density function is
1513
1614```math
1715f(\\ mathbf{H};\\ nu,\\ mathbf{S}) = \\ frac{1}{2^{\\ nu p/2} \\ left|\\ mathbf{S}\\ right|^{\\ nu/2} \\ Gamma_p\\ left(\\ frac {\\ nu}{2}\\ right ) }{\\ left|\\ mathbf{H}\\ right|}^{(\\ nu-p-1)/2} e^{-(1/2)\\ operatorname{tr}(\\ mathbf{S}^{-1}\\ mathbf{H})}.
1816```
1917
20- If ``\\ nu`` is an integer, then a random matrix ``\\ mathbf{H}`` given by
18+ If ``\\ nu\\ leq p-1``, then ``\\ mathbf{H}`` is rank ``\\ nu`` and it has
19+ a density with respect to a suitably chosen volume element on the space of
20+ positive semidefinite matrices. See [here](https://doi.org/10.1214/aos/1176325375).
21+
22+ For integer ``\\ nu``, a random matrix given by
2123
2224```math
23- \\ mathbf{H} = \\ mathbf{X}\\ mathbf{X}^{\\ rm{T}}, \\ quad\\ mathbf{X} \\ sim \\ textrm{MN}_{p,\\ nu}(\\ mathbf{0}, \\ mathbf{S}, \\ mathbf{I}_{\\ nu})
25+ \\ mathbf{H} = \\ mathbf{X}\\ mathbf{X}^{\\ rm{T}},
26+ \\ quad\\ mathbf{X} \\ sim \\ textrm{MN}_{p,\\ nu}(\\ mathbf{0}, \\ mathbf{S}, \\ mathbf{I}_{\\ nu})
2427```
2528
26- has ``\\ mathbf{H}\\ sim \\ textrm{W}_p(\\ nu, \\ mathbf{S})``. For non-integer degrees of freedom,
27- Wishart matrices can be generated via the [Bartlett decomposition](https://en.wikipedia.org/wiki/Wishart_distribution#Bartlett_decomposition).
29+ has ``\\ mathbf{H}\\ sim \\ textrm{W}_p(\\ nu, \\ mathbf{S})``.
30+ For non-integer ``\\ nu``, Wishart matrices can be generated via the
31+ [Bartlett decomposition](https://en.wikipedia.org/wiki/Wishart_distribution#Bartlett_decomposition).
2832"""
29- struct Wishart{T<: Real , ST<: AbstractPDMat } <: ContinuousMatrixDistribution
30- df:: T # degree of freedom
31- S:: ST # the scale matrix
32- logc0:: T # the logarithm of normalizing constant in pdf
33+ struct Wishart{T<: Real , ST<: AbstractPDMat , R<: Integer } <: ContinuousMatrixDistribution
34+ df:: T # degree of freedom
35+ S:: ST # the scale matrix
36+ logc0:: T # the logarithm of normalizing constant in pdf
37+ rank:: R # rank of a sample
38+ singular:: Bool # singular of nonsingular wishart?
3339end
3440
3541# -----------------------------------------------------------------------------
3642# Constructors
3743# -----------------------------------------------------------------------------
3844
39- function Wishart (df:: T , S:: AbstractPDMat{T} ) where T<: Real
45+ function Wishart (df:: T , S:: AbstractPDMat{T} , warn:: Bool = true ) where T<: Real
46+ df > 0 || throw (ArgumentError (" df must be positive. got $(df) ." ))
4047 p = dim (S)
41- df > p - 1 || throw (ArgumentError (" df should be greater than dim - 1." ))
42- logc0 = wishart_logc0 (df, S)
48+ rnk = p
49+ singular = df <= p - 1
50+ if singular
51+ isinteger (df) || throw (ArgumentError (" singular df must be an integer. got $(df) ." ))
52+ rnk = convert (Integer, df)
53+ warn && @warn (" got df <= dim - 1; returning a singular Wishart" )
54+ end
55+ logc0 = wishart_logc0 (df, S, rnk)
4356 R = Base. promote_eltype (T, logc0)
4457 prom_S = convert (AbstractArray{T}, S)
45- Wishart {R, typeof(prom_S)} (R (df), prom_S, R (logc0))
58+ Wishart {R, typeof(prom_S), typeof(rnk) } (R (df), prom_S, R (logc0), rnk, singular )
4659end
4760
48- function Wishart (df:: Real , S:: AbstractPDMat )
61+ function Wishart (df:: Real , S:: AbstractPDMat , warn :: Bool = true )
4962 T = Base. promote_eltype (df, S)
50- Wishart (T (df), convert (AbstractArray{T}, S))
63+ Wishart (T (df), convert (AbstractArray{T}, S), warn )
5164end
5265
53- Wishart (df:: Real , S:: Matrix ) = Wishart (df, PDMat (S))
54-
55- Wishart (df:: Real , S:: Cholesky ) = Wishart (df, PDMat (S))
66+ Wishart (df:: Real , S:: Matrix , warn:: Bool = true ) = Wishart (df, PDMat (S), warn)
67+ Wishart (df:: Real , S:: Cholesky , warn:: Bool = true ) = Wishart (df, PDMat (S), warn)
5668
5769# -----------------------------------------------------------------------------
5870# REPL display
@@ -66,23 +78,30 @@ show(io::IO, d::Wishart) = show_multline(io, d, [(:df, d.df), (:S, Matrix(d.S))]
6678
6779function convert (:: Type{Wishart{T}} , d:: Wishart ) where T<: Real
6880 P = convert (AbstractArray{T}, d. S)
69- Wishart {T, typeof(P)} (T (d. df), P, T (d. logc0))
81+ Wishart {T, typeof(P), typeof(d.rank) } (T (d. df), P, T (d. logc0), d . rank, d . singular )
7082end
71- function convert (:: Type{Wishart{T}} , df, S:: AbstractPDMat , logc0) where T<: Real
83+ function convert (:: Type{Wishart{T}} , df, S:: AbstractPDMat , logc0, rnk, singular ) where T<: Real
7284 P = convert (AbstractArray{T}, S)
73- Wishart {T, typeof(P)} (T (df), P, T (logc0))
85+ Wishart {T, typeof(P), typeof(rnk) } (T (df), P, T (logc0), rnk, singular )
7486end
7587
7688# -----------------------------------------------------------------------------
7789# Properties
7890# -----------------------------------------------------------------------------
7991
80- insupport (:: Type{Wishart} , X:: Matrix ) = isposdef (X)
81- insupport (d:: Wishart , X:: Matrix ) = size (X) == size (d) && isposdef (X)
92+ insupport (:: Type{Wishart} , X:: AbstractMatrix ) = ispossemdef (X)
93+ function insupport (d:: Wishart , X:: AbstractMatrix )
94+ size (X) == size (d) || return false
95+ if d. singular
96+ return ispossemdef (X, rank (d))
97+ else
98+ return isposdef (X)
99+ end
100+ end
82101
83102dim (d:: Wishart ) = dim (d. S)
84103size (d:: Wishart ) = (p = dim (d); (p, p))
85- rank (d:: Wishart ) = dim (d)
104+ rank (d:: Wishart ) = d . rank
86105params (d:: Wishart ) = (d. df, d. S)
87106@inline partype (d:: Wishart{T} ) where {T<: Real } = T
88107
@@ -95,6 +114,7 @@ function mode(d::Wishart)
95114end
96115
97116function meanlogdet (d:: Wishart )
117+ d. singular && return - Inf
98118 p = dim (d)
99119 df = d. df
100120 v = logdet (d. S) + p * logtwo
@@ -105,6 +125,7 @@ function meanlogdet(d::Wishart)
105125end
106126
107127function entropy (d:: Wishart )
128+ d. singular && throw (ArgumentError (" entropy not defined for singular Wishart." ))
108129 p = dim (d)
109130 df = d. df
110131 - d. logc0 - 0.5 * (df - p - 1 ) * meanlogdet (d) + 0.5 * df * p
@@ -125,13 +146,44 @@ end
125146# Evaluation
126147# -----------------------------------------------------------------------------
127148
128- function wishart_logc0 (df:: Real , S:: AbstractPDMat )
129- h_df = df / 2
149+ function wishart_logc0 (df:: Real , S:: AbstractPDMat , rnk:: Integer )
130150 p = dim (S)
131- - h_df * (logdet (S) + p * typeof (df)(logtwo)) - logmvgamma (p, h_df)
151+ if df <= p - 1
152+ return singular_wishart_logc0 (p, df, S, rnk)
153+ else
154+ return nonsingular_wishart_logc0 (p, df, S)
155+ end
132156end
133157
134158function logkernel (d:: Wishart , X:: AbstractMatrix )
159+ if d. singular
160+ return singular_wishart_logkernel (d, X)
161+ else
162+ return nonsingular_wishart_logkernel (d, X)
163+ end
164+ end
165+
166+ # Singular Wishart pdf: Theorem 6 in Uhlig (1994 AoS)
167+ function singular_wishart_logc0 (p:: Integer , df:: Real , S:: AbstractPDMat , rnk:: Integer )
168+ h_df = df / 2
169+ - h_df * (logdet (S) + p * typeof (df)(logtwo)) - logmvgamma (rnk, h_df) + (rnk* (rnk - p) / 2 )* typeof (df)(logπ)
170+ end
171+
172+ function singular_wishart_logkernel (d:: Wishart , X:: AbstractMatrix )
173+ df = d. df
174+ p = dim (d)
175+ r = rank (d)
176+ L = eigvals (Hermitian (X), (p - r + 1 ): p)
177+ 0.5 * ((df - (p + 1 )) * sum (log .(L)) - tr (d. S \ X))
178+ end
179+
180+ # Nonsingular Wishart pdf
181+ function nonsingular_wishart_logc0 (p:: Integer , df:: Real , S:: AbstractPDMat )
182+ h_df = df / 2
183+ - h_df * (logdet (S) + p * typeof (df)(logtwo)) - logmvgamma (p, h_df)
184+ end
185+
186+ function nonsingular_wishart_logkernel (d:: Wishart , X:: AbstractMatrix )
135187 df = d. df
136188 p = dim (d)
137189 Xcf = cholesky (X)
143195# -----------------------------------------------------------------------------
144196
145197function _rand! (rng:: AbstractRNG , d:: Wishart , A:: AbstractMatrix )
146- _wishart_genA! (rng, dim (d), d. df, A)
198+ if d. singular
199+ A .= zero (eltype (A))
200+ A[:, 1 : rank (d)] = randn (rng, dim (d), rank (d))
201+ else
202+ _wishart_genA! (rng, dim (d), d. df, A)
203+ end
147204 unwhiten! (d. S, A)
148205 A .= A * A'
149206end
179236
180237function _rand_params (:: Type{Wishart} , elty, n:: Int , p:: Int )
181238 n == p || throw (ArgumentError (" dims must be equal for Wishart" ))
182- ν = elty ( n + 1 + abs (10 randn ()) )
239+ ν = elty ( n - 1 + abs (10 randn ()) )
183240 S = (X = 2 rand (elty, n, n) .- 1 ; X * X' )
184241 return ν, S
185242end
0 commit comments