Oh, I possibly spoke too soon. I think I see a path via an equivalent SEM representation of the model.
For the both-non-centered, the SEM representation is:
parameters{
row_vector[2] mu ;
row_vector<lower=0>[2] sigma ;
real<lower=0,upper=1> r ;
vector[N] common_latent ;
matrix[N,2] unique_latent ;
...
}
model{
...
common_latent ~ std_normal() ;
to_vector(unique_latent) ~ std_normal() ;
matrix[N,2] group_mean_std_deviate = (
rep_matrix(common_latent,2) .* r
+ (unique_latent .* sqrt(1-square(r)))
) ;
matrix[N,2] group_mean = (
group_mean_std_deviate
.* rep_matrix(sigma,N)
+ rep_matrix(mu,N)
)
...
}
And a quick-and-dirty implementation of the one-centered/one-non-centered is:
model{
...
common_latent ~ std_normal() ;
unique_latent[1] ~ normal(mu[1],sigma[1]) ;
unique_latent[2] ~ std_normal() ;
matrix[N,2] group_mean_std_deviate = (
rep_matrix(common_latent,2) .* r
+ (
append_col(
(unique_latent[1]/sigma[1] - mu[1]) // transforming to standard deviate
, unique_latent[2]
)
.* sqrt(1-square(r))
)
) ;
matrix[N,2] group_mean = (
group_mean_std_deviate
.* rep_matrix(sigma,N)
+ rep_matrix(mu,N)
)
...
}
Which frames things in a format that should be amenable to the approach used in my tutorial to tag individual groups in each of the variables as needing centered or non-centered. (i.e. instead of monolithically expressing the prior for each column in unique_latent
as above, you can express separate priors for those entries that need centered and those that need non-centered, with consequent selective re-transform to standard deviates for the SEM thereafter)
Here’s what that’d look like:
model{
...
common_latent ~ std_normal() ;
unique_latent[var1_id_index_centered_and_scaled,1] ~ normal(mu[1],sigma[1]) ;
unique_latent[var1_id_index_uncentered_and_unscaled,1] ~ std_normal() ;
unique_latent[var2_id_index_centered_and_scaled,2] ~ normal(mu[2],sigma[2]) ;
unique_latent[var2_id_index_uncentered_and_unscaled,2] ~ std_normal() ;
matrix[N,2] unique_latent_for_sem ;
unique_latent_for_sem[var1_id_index_centered_and_scaled,1] = unique_latent[var1_id_index_centered_and_scaled,1]/sigma[1] - mu[1] ;
unique_latent_for_sem[var1_id_index_uncentered_and_unscaled,1] = unique_latent[var1_id_index_uncentered_and_unscaled,1] ;
unique_latent_for_sem[var2_id_index_centered_and_scaled,2] = unique_latent[var2_id_index_centered_and_scaled,2]/sigma[2] - mu[2] ;
unique_latent_for_sem[var2_id_index_uncentered_and_unscaled,2] = unique_latent[var2_id_index_uncentered_and_unscaled,2] ;
matrix[N,2] group_mean_std_deviate = (
rep_matrix(common_latent,2) .* r
+ (
unique_latent_for_sem
.* sqrt(1-square(r))
)
) ;
matrix[N,2] group_mean = (
group_mean_std_deviate
.* rep_matrix(sigma,N)
+ rep_matrix(mu,N)
)
...
}