Hi,
Still working on my dissertation model, and I have a question about how best to parameterize the prior for the epidemiological "contact matrix." (It runs now, but it's not at all elegant, and I'm hoping there are nice solutions to do this.)
This type of matrix matrix has entries c(a,b) that are the number of times an individual in group a is in contact with an individual in group b. It is constrained by the fact that contact is symmetric - though groups sizes are not equal. This means c(b,a) = c(a,b)*size(a)/size(b).
As an additional concern, I had earlier changed the contact matrix to be a simplex that was then multiplied by a fixed value, because allowing the total number of contacts to vary was interacting with the estimated spread variable. (More viral flu with fewer contacts looks exactly like a less viral flu with more contacts.) I found (unsurprisingly) that Stan was arriving at implausible contact matrix simplex posteriors. Using a static prior contact matrix was much faster than having Stan estimate the values, but I'd prefer to allow it to estimate the values based on the provided values as priors.
So, I am attempting to figure out how I can parameterize the model to include a simplex that obeys a constraint. To do this, I use a simplex for the upper triangular part of the matrix, then enforce the constraint on the other half, and then normalize the entire thing all together.
The relevant parts of my attempt (excluding the actual infectious disease model parts,) which seems to work but is very kludge-y:
Data:
real<lower=0> contact_matrix[groups,groups]; // These are unknown, but depend on the group types. This (approximate) matrix here will be used in the prior.
Transformed Data:
real Upper_Contact_Matrix[groups*(groups+1)/2];
{
int upper_counter;
upper_counter <- 1;
Contact_Matrix_Normalizer <- 0.0;
for (i in 1:groups) {
for (j in 1:groups) {
if (i<=j){
Upper_Contact_Matrix[upper_counter]<-contact_matrix[i,j];
upper_counter <- upper_counter + 1;
}
Contact_Matrix_Normalizer <- Contact_Matrix_Normalizer + contact_matrix[i,j]; //This is based on the total
}
}
}
Parameters:
simplex[groups*(groups+1)/2] contact_matrix_vector; //Upper triangular portion only.
Transformed Parameters:
real upper_simplex_pct; //The percentage of the total in the upper simplex - used for normalizing later.
real<lower=0> Lower_Triangular_Simplex[groups*(groups-1)/2];
{
real contact_matrix_temp[groups,groups];
int upper_counter;
int lower_counter;
upper_counter <- 1;
lower_counter <- 1;
for (i in 1:groups) {
for (j in 1:groups) {
real inverse_value;
inverse_value <- (-1); //If my logic is bad, this will violate constraints later.
if (i>j) {
contact_matrix_temp[i,j] <- contact_matrix_temp[j,i]*pop_sizes[j]/pop_sizes[i]; //This creates the contact matrix identity.
Lower_Triangular_Simplex[lower_counter] <- contact_matrix_temp[i,j];
lower_counter <- lower_counter + 1;
}
if(i<=j) {
contact_matrix_temp[i,j] <- contact_matrix_vector[upper_counter];
upper_counter <- upper_counter + 1;
}
}
}
upper_simplex_pct <- sum(contact_matrix_vector)/(sum(Lower_Triangular_Simplex)+sum(contact_matrix_vector));
} //End contact matrix creation.
model:
{
real contact_matrix_temp[groups,groups];
int theta_counter;
int matrix_counter;
theta_counter <- 1;
matrix_counter <- 1;
for (i in 1:groups) {
for (j in 1:groups) {
contact_matrix_temp[i,j] <- if_else(i<=j,contact_matrix_vector[matrix_counter],contact_matrix_temp[j,i]*pop_sizes[j]/pop_sizes[i]);
if (i<=j){
matrix_counter <- matrix_counter + 1;
}
theta[y, theta_counter] <- contact_matrix_temp[i,j]*Contact_Matrix_Normalizer;
theta_counter<-theta_counter+1;
}
}
}
contact_matrix_vector ~ dirichlet(to_vector(Upper_Contact_Matrix)/(Contact_Matrix_Normalizer*upper_simplex_pct));