Having thought about this for a bit:
Isn’t the problem that in backwards sampling, the logp for LKJCorr in no way selects for positive semidefiniteness. The interval transform only transforms all the values to within [-1,1] range and then LKJCorr logp applies the determinant-based distribution which already assumes a semi-definite matrix, but neither of these really nudges the numbers to form a semi-definite matrix.
This is consistent with failures increasing as n gets larger, as PSD matrices become increasingly less likely. As for why the probability does not drop off a cliff after 2 or 3, my guess is that the interval transform steers away from values close to -1 and 1 in favor of more middling values which lets the ones on the diagonal dominate for a while.
This is also consistent with the sampling failing if instead of chol=chol_corr you use cov=corr in the MvNormal parametrization. Same reason - it ends up proposing a non-PSD matrix. In this case, the error message contains the initial value though, so it is easy to check:
vec = [-0.39718353, 0.94680841, -0.41832789, 0.67318716, 0.74000446,
-0.91103117, 0.17496013, 0.05499186, -0.60507651, -0.37989516,
0.00281214, -0.83842367, 0.04520434, 0.51635372, -0.26586628,
0.96277075, 0.66443148, -0.46217671, 0.77692162, -0.20452818,
0.99873618, -0.23998811, 0.65304258, 0.98352337, 0.64847332,
-0.08343583, 0.52405396, 0.15585091, 0.31938468, 0.90564176,
-0.35090953, -0.43284892, -0.21282992, -0.48646797, -0.50581992,
-0.06065079, -0.57660361, 0.90999947, 0.07498183, -0.01768012,
-0.54075488, 0.8193952 , -0.13612187, 0.29677601, 0.07794663]
n=10
corr = np.zeros((n, n))
corr[np.triu_indices(n, k=1)] = vec
corr = corr + corr.T + np.eye(n)
print(np.linalg.eig(corr)[1].min())
indeed returns -0.7785569784890056 i.e. a negative eigenvalue => not PSD
So it looks like LKJCorr as it currently stands is fundamentally broken, i.e. it does not sample from the LKJ distribution as it does not respect the PSD requirement.
I think the way to fix this would be to move to sampling cholesky decomposition (as in LKJCholeskyCorr) as the existance of it already implies the matrix is PSD hence selecting for it.
Although - at that point - maybe it makes sense to just rewrite LKJCorr as a thin wrapper around LKJCholeskyCov that just returns the ‘corr’ part?
Thoughts @ricardoV94 @jessegrabowski ?