Back to my question...I would appreciate any general feedback about my code, but I think I found a reason why my model tends to run slowly; specially, I am not sure if I am handling missing test score data values efficiently. Specifically, I have been filling in missing test score data values with parameter values as necessary. Please see lines 153 to 167 for specific details. I borrowed some code from Ben Goodrich to do this, but I am not sure if there is a better way of handling missing values given that Stan has been updated regularly over the past year. I will also say that I am trying to estimate this model using a large, district data set.
theta ~ multi_normal(zeros, Gamma);
theta <- L * z_theta;
z_theta ~ normal(0,1); // implies theta ~ multi_normal_cholesky(zeros, L);
Back to my question...I would appreciate any general feedback about my code, but I think I found a reason why my model tends to run slowly; specially, I am not sure if I am handling missing test score data values efficiently.
theta6[1] ~ normal(0,0.001);
Also, it looks as if you forgot to translate this linetheta6[1] ~ normal(0,0.001);
from BUGS. In Stan, the second argument to normal() is the standard deviation, not the precision.
The missing data handling is OK.
The covariance matrices are very slow. Models are much faster (and more
robust arithmetically) using Cholesky factors. We don't have the (inverse) Wishart
implemented for Cholesky factors because we use the LKJ prior on correlation
matrices and scale it. I think you'll find that more flexible in terms of prior
(choose degree of concentration for correlation matrix around the unit vector and
flexible scale modeling). It'll also be a whole lot faster.
You also want to vectorize the multi_normal calls.
for(i in 2:Ntch[5]) {
theta5[i] ~ multi_normal(rep_vector(0,ih_l[5]), Gamma5);
}
is more efficient as
theta5[2:Ntch[5]] ~ multi_normal(rep_vector(0,ih_l[5]), Gamma5);
because you only need to solve (invert and determinant) Gamma5 once.
I find using Gamma5 and Gamma05 as variables very confusing!
There are a lot of these --- it'll make a huge difference in speed.
Also vectorize this:
for(i in 1:N) {
...
y ~ multi_normal(u[i], Sigma);
/* Fill in missing test score data values with parameter values as necessary
* Code borrowed from Ben Goodrich's post on Stan users list:
* https://groups.google.com/d/msg/stan-users/y9BalO9ILF0/XmpNOwIVsEcJ
*/
count <- 1;
for(i in 1:N) {
vector[Nyear] y;
for(j in 1:Nyear) {
if(R[i,j] == 1)
y[j] <- Z[i,j];
else {
y[j] <- Z_miss[count];
count <- count + 1;
}
}
/*
* Specify the likelihood for the test scores: In transformed parameters, we
* specified the expected values conditional on theta.
* The conditional distribution is normal with covariance matrix Sigma.
* Note that "y", as it appears here, is student i's vector of test scores.
*/
y ~ multi_normal(u[i], Sigma);
}
Also things like rep_vector(0,ih_l[5]) can be declared as transformed
data --- it's a bit more efficient than creating and destroying it each
time it's used (but with just data, it's not too bad).
The loops like these can be made more efficient
for (i in 1:N) {
u[i,1] <- mu[1] + theta1[tchid[i,1],1];
as
u[, 1] <- mu[1] + theta1[tchid[, 1], 1];
but that's not going to work until I fix the includes for our next release.
But you can do this:
u[1:N, 1] <- mu[1] + theta1[tchid[1:N, 1], 1];
Or, you can transpose the represntation of u and tchid and just do
u[1] <- mu[1] + theta1[tchid[1], 1];
u[1:N,1] <- mu[1] + theta1[tchid[i,1],1];
u[1:N,2] <- mu[2] + theta1[tchid[i,1],2] + theta2[tchid[i,2],1];
u[1:N,3] <- mu[3] + theta1[tchid[i,1],3] + theta2[tchid[i,2],2] +
theta3[tchid[i,3],1];
u[1:N,4] <- mu[4] + theta1[tchid[i,1],4] + theta2[tchid[i,2],3] +
theta3[tchid[i,3],2] + theta4[tchid[i,4],1];
u[1:N,5] <- mu[5] + theta1[tchid[i,1],5] + theta2[tchid[i,2],4] +
theta3[tchid[i,3],3] + theta4[tchid[i,4],2] +
theta5[tchid[i,5],1];
u[1:N,6] <- mu[6] + theta1[tchid[i,1],6] + theta2[tchid[i,2],5] +
theta3[tchid[i,3],4] + theta4[tchid[i,4],3] +
theta5[tchid[i,5],2] + theta6[tchid[i,6]]; /*
* Specify the priors for the grade means, Normal 0, variance 50
* Assume normal 0, variance 50 for grade means.
* NOTE: In Stan, specify the standard deviation.
*/
mu[1:Nyear] ~ normal(mu_p[1:Nyear], sqrt(50)); for(i in 1:Nyear) {
mu[i] ~ normal(mu_p[i], sqrt(50));
} parameters {
vector[K] mu; // topic mean
corr_matrix[K] Omega; // correlation matrix
vector<lower=0>[K] sigma; // scales
vector[K] eta[M]; // logit topic dist for doc m
simplex[V] phi[K]; // word dist for topic k
}
transformed parameters {
... eta as above ...
cov_matrix[K] Sigma; // covariance matrix
for (m in 1:K)
Sigma[m,m] <- sigma[m] * sigma[m] * Omega[m,m];
for (m in 1:(K-1)) {
for (n in (m+1):K) {
Sigma[m,n] <- sigma[m] * sigma[n] * Omega[m,n];
Sigma[n,m] <- Sigma[m,n];
}
}
}
model {
mu ~ normal(0,5); // vectorized, diffuse
Omega ~ lkj_corr(2.0); // regularize to unit correlation
sigma ~ cauchy(0,5); // half-Cauchy due to constraint
... words sampled as above ...
} theta1[2:Ntch[1]] <- diag_pre_multiply(scale1, L_Gamma1)*z_theta1[2:Ntch[1]];
theta2[2:Ntch[2]] <- diag_pre_multiply(scale2, L_Gamma2)*z_theta2[2:Ntch[2]];
theta3[2:Ntch[3]] <- diag_pre_multiply(scale3, L_Gamma3)*z_theta3[2:Ntch[3]];
theta4[2:Ntch[4]] <- diag_pre_multiply(scale4, L_Gamma4)*z_theta4[2:Ntch[4]];
theta5[2:Ntch[5]] <- diag_pre_multiply(scale5, L_Gamma5)*z_theta5[2:Ntch[5]]; SYNTAX ERROR, MESSAGE(S) FROM PARSER:
No matches for:
matrix * vector[]
Instead, I have written the code inefficiently as:
for(i in 2:Ntch[1]) {
theta1[i] <- diag_pre_multiply(scale1, L_Gamma1)*z_theta1[i];
}
for(i in 2:Ntch[2]) {
theta2[i] <- diag_pre_multiply(scale2, L_Gamma2)*z_theta2[i];
}
for(i in 2:Ntch[3]) {
theta3[i] <- diag_pre_multiply(scale3, L_Gamma3)*z_theta3[i];
}
for(i in 2:Ntch[4]) {
theta4[i] <- diag_pre_multiply(scale4, L_Gamma4)*z_theta4[i];
}
for(i in 2:Ntch[5]) {
theta5[i] <- diag_pre_multiply(scale5, L_Gamma5)*z_theta5[i];
} u[1:N,1] <- mu[1] + theta1[tchid[1:N,1],1];
u[1:N,2] <- mu[2] + theta1[tchid[1:N,1],2] + theta2[tchid[1:N,2],1];
u[1:N,3] <- mu[3] + theta1[tchid[1:N,1],3] + theta2[tchid[1:N,2],2] +
theta3[tchid[1:N,3],1];
...etc. z_theta1[2:Ntch[1]] ~ normal(0,1); // implies theta1[2:Ntch[1]] ~ multi_normal_cholesky(zeros, L);
z_theta2[2:Ntch[2]] ~ normal(0,1);
z_theta3[2:Ntch[3]] ~ normal(0,1);
z_theta4[2:Ntch[4]] ~ normal(0,1);
z_theta5[2:Ntch[5]] ~ normal(0,1); I have been trying to optimizing my code using all the suggestions given, but I am running into problems using the new R-style indexing.
For example, I want to do the following:theta1[2:Ntch[1]] <- diag_pre_multiply(scale1, L_Gamma1)*z_theta1[2:Ntch[1]];
theta2[2:Ntch[2]] <- diag_pre_multiply(scale2, L_Gamma2)*z_theta2[2:Ntch[2]];
theta3[2:Ntch[3]] <- diag_pre_multiply(scale3, L_Gamma3)*z_theta3[2:Ntch[3]];
theta4[2:Ntch[4]] <- diag_pre_multiply(scale4, L_Gamma4)*z_theta4[2:Ntch[4]];
theta5[2:Ntch[5]] <- diag_pre_multiply(scale5, L_Gamma5)*z_theta5[2:Ntch[5]];but I am getting the following compiling error:SYNTAX ERROR, MESSAGE(S) FROM PARSER:
No matches for:
matrix * vector[]
Instead, I have written the code inefficiently as:
for(i in 2:Ntch[1]) {
theta1[i] <- diag_pre_multiply(scale1, L_Gamma1)*z_theta1[i];
}
for(i in 2:Ntch[2]) {
theta2[i] <- diag_pre_multiply(scale2, L_Gamma2)*z_theta2[i];
}
for(i in 2:Ntch[3]) {
theta3[i] <- diag_pre_multiply(scale3, L_Gamma3)*z_theta3[i];
}
for(i in 2:Ntch[4]) {
theta4[i] <- diag_pre_multiply(scale4, L_Gamma4)*z_theta4[i];
}
for(i in 2:Ntch[5]) {
theta5[i] <- diag_pre_multiply(scale5, L_Gamma5)*z_theta5[i];
}
Similar problem but with real+real[ ] being ill formed when I do the following:u[1:N,1] <- mu[1] + theta1[tchid[1:N,1],1];
u[1:N,2] <- mu[2] + theta1[tchid[1:N,1],2] + theta2[tchid[1:N,2],1];
u[1:N,3] <- mu[3] + theta1[tchid[1:N,1],3] + theta2[tchid[1:N,2],2] +
theta3[tchid[1:N,3],1];
...etc.
Finally, the last block of code that is causing issues is:z_theta1[2:Ntch[1]] ~ normal(0,1); // implies theta1[2:Ntch[1]] ~ multi_normal_cholesky(zeros, L);
z_theta2[2:Ntch[2]] ~ normal(0,1);
z_theta3[2:Ntch[3]] ~ normal(0,1);
z_theta4[2:Ntch[4]] ~ normal(0,1);
z_theta5[2:Ntch[5]] ~ normal(0,1);