|
8 | 8 | from theano.tensor import dot, cast, eye, diag, eq, le, ge, gt, all
|
9 | 9 | from theano.printing import Print
|
10 | 10 |
|
11 |
| -__all__ = ['MvNormal', 'Dirichlet', 'Multinomial', 'Wishart', 'LKJCorr'] |
| 11 | +__all__ = ['MvNormal', 'Dirichlet', 'Multinomial', 'Wishart', 'WishartBartlett', 'LKJCorr'] |
12 | 12 |
|
13 | 13 | class MvNormal(Continuous):
|
14 | 14 | """
|
@@ -187,6 +187,62 @@ def logp(self, X):
|
187 | 187 | eq(X, X.T)
|
188 | 188 | )
|
189 | 189 |
|
| 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 | + |
190 | 246 |
|
191 | 247 | class LKJCorr(Continuous):
|
192 | 248 | """
|
|
0 commit comments