-
-
Notifications
You must be signed in to change notification settings - Fork 48
Description
The pr #979 added the jacobian += and _jacobian function keyword to indicate adding the change of variables correction for a transformed parameter. The jacobian functionality can only be used in the transformed parameters block. That same pr also introduced a warning that _lp type functions will no longer be allowed in the transformed parameters block and will only be supported in the model block. I think the introduction of the jacobian functionality is useful but I believe restricting the _lp to only the model block is a step backwards for Stan.
In merging #979, which users will first see in cmdstan 2.36, any user defined function ending with _lp that is used in the transformed parameters block will receive a warning that this functionality will not be allowed in future releases. The initial reasoning in the discussion for #979 is that allowing functions to access the target was a bug in stanc2 that we continued to support in stanc3. The merged pr is indicating the "fix". However, there are times where it is useful to both add the jacobian correction and to add a log density. I don't believe this is widespread but in matrix parameters I encounter it. For example, below I show the c-vine parameterization of a correlation matrix. I use both the jacobian and the target keywords here. I'm not sure this will compile because I believe it would need to end in _jacobian. I have been using target everywhere where jacobian now is which does compile in previous versions.
vector lb_ub_lp (vector y, real lb, real ub) {
target += log(ub - lb) + log_inv_logit(y) + log1m_inv_logit(y);
return lb + (ub - lb) * inv_logit(y);
}
real lb_ub_lp (real y, real lb, real ub) {
target += log(ub - lb) + log_inv_logit(y) + log1m_inv_logit(y);
return lb + (ub - lb) * inv_logit(y);
}
matrix corr_constrain_cvine_lp (vector col_one_raw, vector off_raw, real eta) {
int K = num_elements(col_one_raw) + 1;
vector[K - 1] z = lb_ub_lp(col_one_raw, -1, 1);
matrix[K, K] C = identity_matrix(K);
matrix[K, K] P;
C[1, 1] = 1;
C[2:K, 1] = z[1:K - 1];
C[1, 2:K] = C[2:K, 1]';
P = C;
int cnt = 1;
target += beta_lpdf(0.5 * (z + 1) | eta + 0.5 * (K - 1), eta + 0.5 * (K - 1));
jacobian += -0.5 * log1m(P[1, 2:K]^2);
for (i in 2:K - 1) {
for (j in i + 1:K) {
P[i, j] = lb_ub_lp(off_raw[cnt], lb, ub);
cnt += 1;
real tem = P[i, j];
jacobian += -0.5 * i * log1m(P[i, j]^2);
target += beta_lpdf(0.5 * (P[i, j] + 1) | eta + 0.5 * (K - 1), eta + 0.5 * (K - 1));
int m = i;
for (k in 1:i - 1) {
m += -1;
tem = P[m, i] * P[m, j] + tem * sqrt( (1 - P[m, i]^2) * (1 - P[m, j]^2) );
}
C[i, j] = tem;
C[j, i] = C[i, j];
}
}
return C;
}The first thing to note is that I often nest _lp functions, here I have lb_ub_lp called within the corr_constrain_cvine_lp function. The next thing is that I'm traversing the lower triangular part of the matrix and I'm adding a beta prior to a transformed parameter created in this function. However, it is an intermediate parameter used to calculate the final correlation matrix, C.
If the deprecation goes through then I will have to have two functions, one for the jacobian that constructs C while also returning a tuple of matrices for both P and C. Then in the model block I'll have to loop through P to increment the log density by the beta_lpdf. I don't even need P and I definitely don't want to do the double loop again!
The current ability of having _lp works great. Let's keep it and, while we're at it, let's make the language a bit more flexible and add the ability for users to use the jacobian keyword in the model block.