How to constrain some correlations to 0?

Suppose you have a 3x3 correlation matrix of variables A, B and C. Let´s say that you are building a covariance matrix like this:

parameters {
corr_matrix[3] corr;
vector<lower=0>[3] var;
}

transformed_parameters{
cov_matrix[3] varcov;
varcov=quad_form_diag(corr, var);
}

model{
corr ~ lkj_corr(1);
var ~ cauchy(0,5)
}

So everything looks great up until now. However, what if we want to constrain the correlation between the variables B and C to be 0? I am trying to find out how to build a matrix like that but I don’t understand enough about the underlying mechanisms of matrices in Stan. Any help would be appreciated!

One way to achieve this is to define an intermediate variable that’s a copy of the correlations then zero the entries you want constrained:

parameters {
	corr_matrix[3] corr;
	vector<lower=0>[3] var;
}
transformed parameters{
	matrix[3] corr_constrained = corr;
	corr_constrained[2,3] = 0 ;
	corr_constrained[3,2] = 0 ;
	cov_matrix[3] varcov;
	varcov = quad_form_diag(corr_constrained, var);
}
model{
	corr ~ lkj_corr(1);
	var ~ cauchy(0,5)
}
3 Likes

I think this works great but in my case I’ve had to write

matrix[3,3]

instead of

matrix[3]

Ah, right, typo on my part, good catch!

1 Like

Mike, I’m not sure that this solution will always work. For example, I think the lkj could possibly give you a positive definite matrix with correlations of .9 everywhere. Then, if you change one of those correlations to zero, the matrix is no longer positive definite.

This probably does not matter for some applications, but it might matter if you are trying to define a prior distribution.

2 Likes

Good point. Really highlights the restricted utility of the multivariate normal as a structure for achieving inference on relationships. I’ve started doing more SEM stuff lately, which give much better control over things.

You can try this code Correlation matrix with positively constrained off-diagonals - #3 by spinkney. Don’t put the lower bound on y_raw. And only declare as many non-zero off diagonal elements that you need.

In the transformed parameters block you’d create a new y_raw; where you’d place the zeroes where you want (keeping track of which vector index corresponds to the matrix index in the correlation matrix). Then pass that to matrix[K, K] y = cholesky_corr_constrain_lp(y_raw_new, K);

3 Likes

Hello, thank you very much for your helpful piece of code and I apologize for coming back to this only after so much time. I would just like to ask a few questions to better understand the code.

When you say to only declare as many non-zero off diagonal elements as I need, to which part of the code does that refer to? Should I modify the indices of the for loops in the functions block so that the elements which should be zero are skipped, or does this refer to some other part of the code?

Next question, if I am creating the y_raw_new in the transformed parameters block, should I remove y_raw from the parameters block? What purpose does y_raw serve at that point?

Thank you very much for your help.

Furthermore, I’m not fully sure I understand the whole idea around this approach. I have been trying out some code that I am unsure of and I have successfuly constrained some elements of the Choleksy matrix to be 0. However, whether or not that constraint will also be translated to the correlation matrix depends on the other elements of the Cholesky matrix due to the rules of the matrix algebra.

Could you please just confirm that I have understood something wrong and that the method that you describe can also be used to constrain correlations in the correlation matrix to zero, and not just the Choleksy factors?

I don’t know why I wrote that. It’s a bit more difficult because the cholesky factor of a sparse correlation matrix is not necessarily sparse. You can set values of the correlation matrix to 0 and work from there. The issue being that that matrix may not be PD. You can get this to work but it’s all a bit more involved. There’s some rough code at this PR fixed coupla_defination.stan of zero_constrain by yamikarajput546 · Pull Request #69 · spinkney/helpful_stan_functions · GitHub.

I’ve been meaning to write up exactly how to do this but not sure when I’ll get time.

3 Likes

I have posted some code / ideas that will let you do this in a few different threads, the best overview probably starts here: Partial-pooling of correlation (or covariance) matrices? - #4 by BenH
To fix the correlation to zero you just need to ensure the relevant value of the vector you pass in to the constraincorsqrt function is zero.

2 Likes

Thank you very much, this looks really helpful! I would just like to ask when it comes to ensuring that the relevant elements of the vector passed to the function are zero, would it suffice to put e.g. rawcor[1] = 0; and rawcor[3] = 0; in the transformed parameters block?

yep, something like that.

1 Like