May also be worth exploring Andrew’s suggestion here to implement a unit CAR prior, and then multiply \phi by a scale parameter:
functions {
/**
* Return log probability of a unit-scale proper conditional autoregressive
* (CAR) prior with a sparse representation for the adjacency matrix
*
* @param phi Vector containing the parameters with a CAR prior
* @param alpha Dependence (usually spatial) parameter for the CAR prior (real)
* @param W_sparse Sparse representation of adjacency matrix (int array)
* @param n Length of phi (int)
* @param W_n Number of adjacent pairs (int)
* @param D_sparse Number of neighbors for each location (vector)
* @param lambda Eigenvalues of D^{-1/2}*W*D^{-1/2} (vector)
*
* @return Log probability density of CAR prior up to additive constant
*/
real sparse_car_lpdf(vector phi, real alpha,
int[,] W_sparse, vector D_sparse, vector lambda, int n_site, int W_n) {
row_vector[n_site] phit_D; // phi' * D
row_vector[n_site] phit_W; // phi' * W
vector[n_site] ldet_terms;
phit_D = (phi .* D_sparse)';
phit_W = rep_row_vector(0, n_site);
for (i in 1:W_n) {
phit_W[W_sparse[i, 1]] += phi[W_sparse[i, 2]];
phit_W[W_sparse[i, 2]] += phi[W_sparse[i, 1]];
}
for (i in 1:n_site) ldet_terms[i] = log1m(alpha * lambda[i]);
return 0.5 * (sum(ldet_terms)
- (phit_D * phi - alpha * (phit_W * phi)));
}
}
data {
// site-level occupancy covariates
int<lower = 1> n_site;
int<lower = 1> m_psi;
matrix[n_site, m_psi] X_psi;
// survey-level detection covariates
int<lower = 1> total_surveys;
int<lower = 1> m_p;
matrix[total_surveys, m_p] X_p;
// survey level information
int<lower = 1, upper = n_site> site[total_surveys];
int<lower = 0, upper = 1> y[total_surveys];
int<lower = 0, upper = total_surveys> start_idx[n_site];
int<lower = 0, upper = total_surveys> end_idx[n_site];
// summary of whether species is known to be present at each site
int<lower = 0, upper = 1> any_seen[n_site];
// number of surveys at each site
int<lower = 0> n_survey[n_site];
//adjacency data
matrix<lower = 0, upper = 1>[n_site, n_site] W_tct; //adjacency matrix tract
int W_n; //number of adjacent pairs
}
transformed data {
int W_sparse[W_n, 2]; // adjacency pairs
vector[n_site] D_sparse; // diagonal of D (number of neigbors for each site)
vector[n_site] lambda; // eigenvalues of invsqrtD * W * invsqrtD
{ // generate sparse representation for W
int counter;
counter = 1;
// loop over upper triangular part of W to identify neighbor pairs
for (i in 1:(n_site - 1)) {
for (j in (i + 1):n_site) {
if (W_tct[i, j] == 1) {
W_sparse[counter, 1] = i;
W_sparse[counter, 2] = j;
counter += 1;
}
}
}
}
for (i in 1:n_site) D_sparse[i] = sum(W_tct[i]);
{
vector[n_site] invsqrtD;
for (i in 1:n_site) {
invsqrtD[i] = 1 / sqrt(D_sparse[i]);
}
lambda = eigenvalues_sym(quad_form(W_tct, diag_matrix(invsqrtD)));
}
}
parameters {
vector[n_site] phi;
vector[m_psi] beta_psi;
vector[m_p] beta_p;
real<lower = 0, upper =1> alpha;
real<lower = 0> sigma;
}
transformed parameters {
vector[total_surveys] logit_p = X_p * beta_p;
vector[n_site] logit_psi = X_psi * beta_psi + phi * sigma;
}
model {
vector[n_site] log_psi = log_inv_logit(logit_psi);
vector[n_site] log1m_psi = log1m_inv_logit(logit_psi);
phi ~ sparse_car(alpha, W_sparse, D_sparse, lambda, n_site, W_n);
sigma ~ normal(0, 1);
beta_psi ~ normal(0, 1);
beta_p ~ normal(0, 1);
for (i in 1:n_site) {
if (n_survey[i] > 0) {
if (any_seen[i]) {
// site is occupied
target += log_psi[i]
+ bernoulli_logit_lpmf(y[start_idx[i]:end_idx[i]] |
logit_p[start_idx[i]:end_idx[i]]);
} else {
// site may or may not be occupied
target += log_sum_exp(
log_psi[i] + bernoulli_logit_lpmf(y[start_idx[i]:end_idx[i]] |
logit_p[start_idx[i]:end_idx[i]]),
log1m_psi[i]
);
}
}
}
}