Hi both,
I think Jesse’s solution should work. In fact, if X_cov
is known in advance, you shouldn’t have to mess with making the matrix block diagonal in aesara, but it should just work with scipy.linalg.block_diag
:
block_diag_cov = block_diag(*covs)
where covs
is e.g. a (100, 2, 2)
matrix of your known error terms. Then you should be able to run:
with pm.Model() as model:
chol, corr, stds = pm.LKJCholeskyCov(
"chol", n=2, eta=2.0, sd_dist=pm.Exponential.dist(1.0), compute_corr=True
)
cov = pm.Deterministic("cov", chol.dot(chol.T))
μ = pm.Normal("μ", 0.0, 10, shape=2, testval=X.mean(axis=0))
X_latent = pm.MvNormal("X_latent", μ, chol=chol, shape=(len(X),2))
obs = pm.MvNormal(f"obs", X_latent.reshape((-1,)), block_diag_cov, observed=X.reshape(-1))
I would double check that these ravels work as expected (sometimes it can be confusing to flatten one row vs one column at a time), but I think that’s right.
An alternative would be to just loop over your outcomes:
with pm.Model() as model:
chol, corr, stds = pm.LKJCholeskyCov(
"chol", n=2, eta=2.0, sd_dist=pm.Exponential.dist(1.0), compute_corr=True
)
cov = pm.Deterministic("cov", chol.dot(chol.T))
μ = pm.Normal("μ", 0.0, 10, shape=2, testval=X.mean(axis=0))
X_latent = pm.MvNormal("X_latent", μ, chol=chol, shape=(len(X),2))
for i in range(N):
cur_obs = pm.MvNormal(f"obs_{i}", X_latent[i], Sigmas[i], observed=X)
Where here, Sigmas
is your array of covariance matrices.
It’s worth noting that neither of these options will be efficient for large N: the first will make a covariance matrix that is needlessly big, and the second will loop and create lots of variables. There is probably a way to make this much more efficient. Let us know though if this is good enough for your purposes!