LKJCholesky and AutoDelta MAP sampling issues

Hi, I’m trying to fit a covariance matrix based on data collected from a relatively simple model using a LKJCholesky prior distribution. However, when I’m doing MAP using the AutoDelta guide the samples generated by the prior are always the identity matrix. I’ve attached my code here as well. Is there something that I’m missing to cause the samples to always be the identity matrix?
Thanks

import pyro
import pyro.distributions as dist
import torch
from pyro.infer import Trace_ELBO, SVI
from pyro.infer.autoguide import AutoDelta


def get_beam_size(k, s):
    d = 1.0
    L = 1.0
    return (1 + d * L * k) ** 2 * s[0] + 2.0 * d * (
            1 + d * L * k) * s[1] + (d ** 2) * s[2]


def model(train_k, train_y=None):
    # Vector of variances for each of the d variables
    theta = pyro.sample(
        "theta", dist.LogNormal(2.0*torch.ones(2), 2.0*torch.ones(2)).to_event(1)
    )
    # distribution over correlation matrices
    L_omega = pyro.sample(
        "L_omega", dist.LKJCholesky(2, concentration=1.0)
    )
    # Lower cholesky factor of the covariance matrix
    L_Omega = torch.mm(theta.sqrt().diag_embed(), L_omega)
    # For inference with SVI, one might prefer to use
    # torch.bmm(theta.sqrt().diag_embed(), L_omega)

    # Vector of expectations
    beam_matrix = L_Omega.T @ L_Omega

    s11 = beam_matrix[0, 0]
    s12 = beam_matrix[1, 0]
    s22 = beam_matrix[1, 1]
    mean_y = get_beam_size(train_k, torch.tensor([s11, s12, s22]))
    sigma = 10.0 * torch.ones(())

    with pyro.plate("data", len(train_k)):
        return pyro.sample(
            "obs", dist.Normal(mean_y, sigma), obs=train_y
        )


def train(model, guide, train_k, train_y, lr=0.001, n_steps=201):
    pyro.clear_param_store()
    adam = pyro.optim.Adam({"lr": lr})
    svi = SVI(model, guide, adam, loss=Trace_ELBO())

    for step in range(n_steps):
        loss = svi.step(train_k, train_y)
        if step % 50 == 0:
            print('[iter {}]  loss: {:.4f}'.format(step, loss))


def main():
    train_k = torch.linspace(-10, 10, 5)
    train_y = get_beam_size(train_k, torch.tensor([2.0, 0.9, 2.0]))
    guide = AutoDelta(model)

    # do map estimation
    train(model, guide, train_k, train_y, n_steps=2000)

    for name, val in pyro.get_param_store().items():
        print(f"{name}:{val}")


if __name__ == '__main__':
    main()

this probably kills gradients. i don’t believe you can repackage individual tensor elements like that into a new tensor without cutting the gradient flow

note if you just passed the whole beam_matrix and then accessed individual entries within get_beam_size it would be fine

Ah yes, of course. Thanks for the prompt response

One other question, when using AutoMultivariateNormal as a guide, pyro seems to treat L_omega as a shape [1,1] tensor. Is this because there is only one off diagonal element in the (2x2) correlation matrix?