-
Notifications
You must be signed in to change notification settings - Fork 414
/
wishart.jl
132 lines (106 loc) · 3.18 KB
/
wishart.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# Wishart distribution
#
# following the Wikipedia parameterization
#
"""
Wishart(nu, S)
The [Wishart distribution](http://en.wikipedia.org/wiki/Wishart_distribution) is a
multidimensional generalization of the Chi-square distribution, which is characterized by
a degree of freedom ν, and a base matrix S.
"""
struct Wishart{T<:Real, ST<:AbstractPDMat} <: ContinuousMatrixDistribution
df::T # degree of freedom
S::ST # the scale matrix
c0::T # the logarithm of normalizing constant in pdf
end
#### Constructors
function Wishart(df::T, S::AbstractPDMat{T}) where T<:Real
p = dim(S)
df > p - 1 || error("dpf should be greater than dim - 1.")
c0 = _wishart_c0(df, S)
R = Base.promote_eltype(T, c0)
prom_S = convert(AbstractArray{T}, S)
Wishart{R, typeof(prom_S)}(R(df), prom_S, R(c0))
end
function Wishart(df::Real, S::AbstractPDMat)
T = Base.promote_eltype(df, S)
Wishart(T(df), convert(AbstractArray{T}, S))
end
Wishart(df::Real, S::Matrix) = Wishart(df, PDMat(S))
Wishart(df::Real, S::Cholesky) = Wishart(df, PDMat(S))
function _wishart_c0(df::Real, S::AbstractPDMat)
h_df = df / 2
p = dim(S)
h_df * (logdet(S) + p * typeof(df)(logtwo)) + logmvgamma(p, h_df)
end
#### Properties
insupport(::Type{Wishart}, X::Matrix) = isposdef(X)
insupport(d::Wishart, X::Matrix) = size(X) == size(d) && isposdef(X)
dim(d::Wishart) = dim(d.S)
size(d::Wishart) = (p = dim(d); (p, p))
params(d::Wishart) = (d.df, d.S, d.c0)
@inline partype(d::Wishart{T}) where {T<:Real} = T
### Conversion
function convert(::Type{Wishart{T}}, d::Wishart) where T<:Real
P = AbstractMatrix{T}(d.S)
Wishart{T, typeof(P)}(T(d.df), P, T(d.c0))
end
function convert(::Type{Wishart{T}}, df, S::AbstractPDMat, c0) where T<:Real
P = AbstractMatrix{T}(S)
Wishart{T, typeof(P)}(T(df), P, T(c0))
end
#### Show
show(io::IO, d::Wishart) = show_multline(io, d, [(:df, d.df), (:S, Matrix(d.S))])
#### Statistics
mean(d::Wishart) = d.df * Matrix(d.S)
function mode(d::Wishart)
r = d.df - dim(d) - 1.0
if r > 0.0
return Matrix(d.S) * r
else
error("mode is only defined when df > p + 1")
end
end
function meanlogdet(d::Wishart)
p = dim(d)
df = d.df
v = logdet(d.S) + p * logtwo
for i = 1:p
v += digamma(0.5 * (df - (i - 1)))
end
return v
end
function entropy(d::Wishart)
p = dim(d)
df = d.df
d.c0 - 0.5 * (df - p - 1) * meanlogdet(d) + 0.5 * df * p
end
#### Evaluation
function _logpdf(d::Wishart, X::AbstractMatrix)
df = d.df
p = dim(d)
Xcf = cholesky(X)
0.5 * ((df - (p + 1)) * logdet(Xcf) - tr(d.S \ X)) - d.c0
end
#### Sampling
function rand(d::Wishart)
Z = unwhiten!(d.S, _wishart_genA(dim(d), d.df))
Z * Z'
end
function _wishart_genA(p::Int, df::Real)
# Generate the matrix A in the Bartlett decomposition
#
# A is a lower triangular matrix, with
#
# A(i, j) ~ sqrt of Chisq(df - i + 1) when i == j
# ~ Normal() when i > j
#
A = zeros(p, p)
for i = 1:p
@inbounds A[i,i] = sqrt(rand(Chisq(df - i + 1.0)))
end
for j = 1:p-1, i = j+1:p
@inbounds A[i,j] = randn()
end
return A
end