Skip to content

Commit d3f68d2

Browse files
committed
ENH Add Wishart with Bartlett decomposition.
1 parent 1d68c53 commit d3f68d2

File tree

1 file changed

+57
-1
lines changed

1 file changed

+57
-1
lines changed

pymc3/distributions/multivariate.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from theano.tensor import dot, cast, eye, diag, eq, le, ge, gt, all
99
from theano.printing import Print
1010

11-
__all__ = ['MvNormal', 'Dirichlet', 'Multinomial', 'Wishart', 'LKJCorr']
11+
__all__ = ['MvNormal', 'Dirichlet', 'Multinomial', 'Wishart', 'WishartBartlett', 'LKJCorr']
1212

1313
class MvNormal(Continuous):
1414
"""
@@ -187,6 +187,62 @@ def logp(self, X):
187187
eq(X, X.T)
188188
)
189189

190+
def WishartBartlett(name, S, nu, is_cholesky=False, return_cholesky=False):
191+
"""
192+
Bartlett decomposition of the Wishart distribution. As the Wishart
193+
distribution requires the matrix to be symmetric positive semi-definite
194+
it is impossible for MCMC to ever propose acceptable matrices.
195+
196+
Instead, we can use the Barlett decomposition which samples a lower
197+
diagonal matrix. Specifically:
198+
199+
If L ~ [[sqrt(c_1), 0, ...],
200+
[z_21, sqrt(c_1), 0, ...],
201+
[z_31, z32, sqrt(c3), ...]]
202+
with c_i ~ Chi²(n-i+1) and n_ij ~ N(0, 1), then
203+
L * A * A.T * L.T ~ Wishart(L * L.T, nu)
204+
205+
See http://en.wikipedia.org/wiki/Wishart_distribution#Bartlett_decomposition
206+
for more information.
207+
208+
:Parameters:
209+
S : ndarray
210+
p x p positive definite matrix
211+
Or:
212+
p x p lower-triangular matrix that is the Cholesky factor
213+
of the covariance matrix.
214+
nu : int
215+
Degrees of freedom, > dim(S).
216+
is_cholesky : bool (default=False)
217+
Input matrix S is already Cholesky decomposed as S.T * S
218+
return_cholesky : bool (default=False)
219+
Only return the Cholesky decomposed matrix.
220+
221+
:Note:
222+
This is not a standard Distribution class but follows a similar
223+
interface. Besides the Wishart distribution, it will add RVs
224+
c and z to your model which make up the matrix.
225+
"""
226+
227+
L = S if is_cholesky else scipy.linalg.cholesky(S)
228+
229+
diag_idx = np.diag_indices_from(S)
230+
tril_idx = np.tril_indices_from(S, k=-1)
231+
n_diag = len(diag_idx[0])
232+
n_tril = len(tril_idx[0])
233+
c = T.sqrt(pm.ChiSquared('c', nu - np.arange(2, 2+n_diag), shape=n_diag))
234+
z = pm.Normal('z', 0, 1, shape=n_tril)
235+
# Construct A matrix
236+
A = T.zeros(S.shape, dtype=np.float32)
237+
A = T.set_subtensor(A[diag_idx], c)
238+
A = T.set_subtensor(A[tril_idx], z)
239+
240+
# L * A * A.T * L.T ~ Wishart(L*L.T, nu)
241+
if return_cholesky:
242+
return pm.Deterministic(name, T.dot(L, A))
243+
else:
244+
return pm.Deterministic(name, T.dot(T.dot(T.dot(L, A), A.T), L.T))
245+
190246

191247
class LKJCorr(Continuous):
192248
"""

0 commit comments

Comments
 (0)