Skip to content

Commit

Permalink
Support truncated prior distributions (#430)
Browse files Browse the repository at this point in the history
  • Loading branch information
gowerc authored Feb 3, 2025
1 parent 0d7148a commit 5a76e76
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 72 deletions.
63 changes: 50 additions & 13 deletions R/Prior.R
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ setValidity(
return(return_message)
}
}
if (length(object@limits) != 2) {
return("Limits must be a vector of length 2")
}
if (object@limits[1] >= object@limits[2]) {
return("Lower limit must be less than upper limit")
}
if (length(object@repr_model) != 1 || !is.character(object@repr_model)) {
return("Model representation must be length 1 string")
}
return(TRUE)
}
)
Expand All @@ -113,6 +122,7 @@ setValidity(
#' @export
set_limits.Prior <- function(object, lower = -Inf, upper = Inf) {
object@limits <- c(lower, upper)
validObject(object)
return(object)
}

Expand All @@ -127,10 +137,32 @@ as.character.Prior <- function(x, ...) {

parameters_rounded <- lapply(x@parameters, round, 5)

do.call(
display_string <- do.call(
glue::glue,
append(x@display, parameters_rounded)
)
display_limits <- render_stan_limits(x@limits)
if (display_limits != "" && display_string != "" && display_string != "<None>") {
display_string <- paste0(display_string, display_limits)
}
return(display_string)
}


#' Creates Stan Syntax for Truncated distributions
#' @description
#' This function creates the Stan syntax for truncated distributions
#' @param limits (`numeric`)\cr the lower and upper limits for a truncated distribution
#' @keywords internal
#' @return (`character`)\cr the Stan syntax for truncated distributions
render_stan_limits <- function(limits) {
l_bound <- if (limits[[1]] > -Inf) limits[[1]] else ""
u_bound <- if (limits[[2]] < Inf) limits[[2]] else ""
string <- ""
if (l_bound != "" || u_bound != "") {
string <- glue::glue(" T[{l_bound}, {u_bound}]", l_bound = l_bound, u_bound = u_bound)
}
return(string)
}


Expand All @@ -157,12 +189,17 @@ setMethod(
#' @family as.StanModule
#' @export
as.StanModule.Prior <- function(object, name, ...) {
trunctation <- if (object@repr_model != "") {
paste0(render_stan_limits(object@limits), ";")
} else {
""
}
string <- paste(
"data {{",
paste0(" ", object@repr_data, collapse = "\n"),
"}}",
"model {{",
paste0(" ", object@repr_model, collapse = "\n"),
paste0(" ", object@repr_model, trunctation),
"}}",
sep = "\n"
)
Expand Down Expand Up @@ -233,7 +270,7 @@ prior_normal <- function(mu, sigma) {
Prior(
parameters = list(mu = mu, sigma = sigma),
display = "normal(mu = {mu}, sigma = {sigma})",
repr_model = "{name} ~ normal(prior_mu_{name}, prior_sigma_{name});",
repr_model = "{name} ~ normal(prior_mu_{name}, prior_sigma_{name})",
repr_data = c(
"real prior_mu_{name};",
"real<lower=0> prior_sigma_{name};"
Expand All @@ -257,7 +294,7 @@ prior_std_normal <- function() {
Prior(
parameters = list(),
display = "std_normal()",
repr_model = "{name} ~ std_normal();",
repr_model = "{name} ~ std_normal()",
repr_data = "",
centre = 0,
sample = \(n) local_rnorm(n),
Expand All @@ -276,7 +313,7 @@ prior_cauchy <- function(mu, sigma) {
Prior(
parameters = list(mu = mu, sigma = sigma),
display = "cauchy(mu = {mu}, sigma = {sigma})",
repr_model = "{name} ~ cauchy(prior_mu_{name}, prior_sigma_{name});",
repr_model = "{name} ~ cauchy(prior_mu_{name}, prior_sigma_{name})",
repr_data = c(
"real prior_mu_{name};",
"real<lower=0> prior_sigma_{name};"
Expand All @@ -301,7 +338,7 @@ prior_cauchy <- function(mu, sigma) {
prior_gamma <- function(alpha, beta) {
Prior(
parameters = list(alpha = alpha, beta = beta),
repr_model = "{name} ~ gamma(prior_alpha_{name}, prior_beta_{name});",
repr_model = "{name} ~ gamma(prior_alpha_{name}, prior_beta_{name})",
display = "gamma(alpha = {alpha}, beta = {beta})",
repr_data = c(
"real<lower=0> prior_alpha_{name};",
Expand All @@ -327,7 +364,7 @@ prior_lognormal <- function(mu, sigma) {
Prior(
parameters = list(mu = mu, sigma = sigma),
display = "lognormal(mu = {mu}, sigma = {sigma})",
repr_model = "{name} ~ lognormal(prior_mu_{name}, prior_sigma_{name});",
repr_model = "{name} ~ lognormal(prior_mu_{name}, prior_sigma_{name})",
repr_data = c(
"real prior_mu_{name};",
"real<lower=0> prior_sigma_{name};"
Expand All @@ -352,7 +389,7 @@ prior_beta <- function(a, b) {
Prior(
parameters = list(a = a, b = b),
display = "beta(a = {a}, b = {b})",
repr_model = "{name} ~ beta(prior_a_{name}, prior_b_{name});",
repr_model = "{name} ~ beta(prior_a_{name}, prior_b_{name})",
repr_data = c(
"real<lower=0> prior_a_{name};",
"real<lower=0> prior_b_{name};"
Expand Down Expand Up @@ -408,7 +445,7 @@ prior_uniform <- function(alpha, beta) {
Prior(
parameters = list(alpha = alpha, beta = beta),
display = "uniform(alpha = {alpha}, beta = {beta})",
repr_model = "{name} ~ uniform(prior_alpha_{name}, prior_beta_{name});",
repr_model = "{name} ~ uniform(prior_alpha_{name}, prior_beta_{name})",
repr_data = c(
"real prior_alpha_{name};",
"real prior_beta_{name};"
Expand Down Expand Up @@ -439,7 +476,7 @@ prior_student_t <- function(nu, mu, sigma) {
sigma = sigma
),
display = "student_t(nu = {nu}, mu = {mu}, sigma = {sigma})",
repr_model = "{name} ~ student_t(prior_nu_{name}, prior_mu_{name}, prior_sigma_{name});",
repr_model = "{name} ~ student_t(prior_nu_{name}, prior_mu_{name}, prior_sigma_{name})",
repr_data = c(
"real<lower=0> prior_nu_{name};",
"real prior_mu_{name};",
Expand Down Expand Up @@ -471,7 +508,7 @@ prior_logistic <- function(mu, sigma) {
sigma = sigma
),
display = "logistic(mu = {mu}, sigma = {sigma})",
repr_model = "{name} ~ logistic(prior_mu_{name}, prior_sigma_{name});",
repr_model = "{name} ~ logistic(prior_mu_{name}, prior_sigma_{name})",
repr_data = c(
"real prior_mu_{name};",
"real<lower=0> prior_sigma_{name};"
Expand Down Expand Up @@ -500,7 +537,7 @@ prior_loglogistic <- function(alpha, beta) {
beta = beta
),
display = "loglogistic(alpha = {alpha}, beta = {beta})",
repr_model = "{name} ~ loglogistic(prior_alpha_{name}, prior_beta_{name});",
repr_model = "{name} ~ loglogistic(prior_alpha_{name}, prior_beta_{name})",
repr_data = c(
"real<lower=0> prior_alpha_{name};",
"real<lower=0> prior_beta_{name};"
Expand Down Expand Up @@ -531,7 +568,7 @@ prior_invgamma <- function(alpha, beta) {
beta = beta
),
display = "inv_gamma(alpha = {alpha}, beta = {beta})",
repr_model = "{name} ~ inv_gamma(prior_alpha_{name}, prior_beta_{name});",
repr_model = "{name} ~ inv_gamma(prior_alpha_{name}, prior_beta_{name})",
repr_data = c(
"real<lower=0> prior_alpha_{name};",
"real<lower=0> prior_beta_{name};"
Expand Down
18 changes: 18 additions & 0 deletions man/render_stan_limits.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 19 additions & 19 deletions tests/testthat/_snaps/JointModel.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@
Survival:
Weibull-PH Survival Model with parameters:
sm_weibull_ph_lambda ~ gamma(alpha = 2, beta = 0.5)
sm_weibull_ph_gamma ~ gamma(alpha = 2, beta = 0.5)
sm_weibull_ph_lambda ~ gamma(alpha = 2, beta = 0.5) T[0, ]
sm_weibull_ph_gamma ~ gamma(alpha = 2, beta = 0.5) T[0, ]
beta_os_cov ~ normal(mu = 0, sigma = 2)
Longitudinal:
Random Slope Longitudinal Model with parameters:
lm_rs_intercept ~ normal(mu = 30, sigma = 10)
lm_rs_slope_mu ~ normal(mu = 1, sigma = 3)
lm_rs_slope_sigma ~ lognormal(mu = 0, sigma = 1.5)
lm_rs_sigma ~ lognormal(mu = 0, sigma = 1.5)
lm_rs_slope_sigma ~ lognormal(mu = 0, sigma = 1.5) T[0, ]
lm_rs_sigma ~ lognormal(mu = 0, sigma = 1.5) T[0, ]
lm_rs_ind_rnd_slope ~ <None>
Link:
Expand All @@ -38,16 +38,16 @@
Survival:
Weibull-PH Survival Model with parameters:
sm_weibull_ph_lambda ~ gamma(alpha = 2, beta = 0.5)
sm_weibull_ph_gamma ~ gamma(alpha = 2, beta = 0.5)
sm_weibull_ph_lambda ~ gamma(alpha = 2, beta = 0.5) T[0, ]
sm_weibull_ph_gamma ~ gamma(alpha = 2, beta = 0.5) T[0, ]
beta_os_cov ~ normal(mu = 0, sigma = 2)
Longitudinal:
Random Slope Longitudinal Model with parameters:
lm_rs_intercept ~ normal(mu = 30, sigma = 10)
lm_rs_slope_mu ~ normal(mu = 1, sigma = 3)
lm_rs_slope_sigma ~ lognormal(mu = 0, sigma = 1.5)
lm_rs_sigma ~ lognormal(mu = 0, sigma = 1.5)
lm_rs_slope_sigma ~ lognormal(mu = 0, sigma = 1.5) T[0, ]
lm_rs_sigma ~ lognormal(mu = 0, sigma = 1.5) T[0, ]
lm_rs_ind_rnd_slope ~ <None>
Link:
Expand All @@ -66,8 +66,8 @@
Survival:
Weibull-PH Survival Model with parameters:
sm_weibull_ph_lambda ~ gamma(alpha = 2, beta = 0.5)
sm_weibull_ph_gamma ~ gamma(alpha = 2, beta = 0.5)
sm_weibull_ph_lambda ~ gamma(alpha = 2, beta = 0.5) T[0, ]
sm_weibull_ph_gamma ~ gamma(alpha = 2, beta = 0.5) T[0, ]
beta_os_cov ~ normal(mu = 0, sigma = 2)
Longitudinal:
Expand All @@ -94,11 +94,11 @@
lm_gsf_mu_ks ~ normal(mu = -0.69315, sigma = 1)
lm_gsf_mu_kg ~ normal(mu = -1.20397, sigma = 1)
lm_gsf_mu_phi ~ normal(mu = 0, sigma = 1)
lm_gsf_omega_bsld ~ lognormal(mu = -1.60944, sigma = 1)
lm_gsf_omega_ks ~ lognormal(mu = -1.60944, sigma = 1)
lm_gsf_omega_kg ~ lognormal(mu = -1.60944, sigma = 1)
lm_gsf_omega_phi ~ lognormal(mu = -1.60944, sigma = 1)
lm_gsf_sigma ~ lognormal(mu = -2.30259, sigma = 1)
lm_gsf_omega_bsld ~ lognormal(mu = -1.60944, sigma = 1) T[0, ]
lm_gsf_omega_ks ~ lognormal(mu = -1.60944, sigma = 1) T[0, ]
lm_gsf_omega_kg ~ lognormal(mu = -1.60944, sigma = 1) T[0, ]
lm_gsf_omega_phi ~ lognormal(mu = -1.60944, sigma = 1) T[0, ]
lm_gsf_sigma ~ lognormal(mu = -2.30259, sigma = 1) T[0, ]
lm_gsf_eta_tilde_bsld ~ std_normal()
lm_gsf_eta_tilde_ks ~ std_normal()
lm_gsf_eta_tilde_kg ~ std_normal()
Expand All @@ -119,16 +119,16 @@
Survival:
Weibull-PH Survival Model with parameters:
sm_weibull_ph_lambda ~ gamma(alpha = 2, beta = 0.5)
sm_weibull_ph_gamma ~ gamma(alpha = 2, beta = 0.5)
sm_weibull_ph_lambda ~ gamma(alpha = 2, beta = 0.5) T[0, ]
sm_weibull_ph_gamma ~ gamma(alpha = 2, beta = 0.5) T[0, ]
beta_os_cov ~ normal(mu = 0, sigma = 2)
Longitudinal:
Random Slope Longitudinal Model with parameters:
lm_rs_intercept ~ normal(mu = 30, sigma = 10)
lm_rs_slope_mu ~ normal(mu = 1, sigma = 3)
lm_rs_slope_sigma ~ lognormal(mu = 0, sigma = 1.5)
lm_rs_sigma ~ lognormal(mu = 0, sigma = 1.5)
lm_rs_slope_sigma ~ lognormal(mu = 0, sigma = 1.5) T[0, ]
lm_rs_sigma ~ lognormal(mu = 0, sigma = 1.5) T[0, ]
lm_rs_ind_rnd_slope ~ <None>
Link:
Expand Down
20 changes: 10 additions & 10 deletions tests/testthat/_snaps/LongitudinalClaretBruno.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
lm_clbr_mu_g ~ normal(mu = 0, sigma = 0.5)
lm_clbr_mu_c ~ normal(mu = -0.91629, sigma = 0.5)
lm_clbr_mu_p ~ normal(mu = 0.69315, sigma = 0.5)
lm_clbr_omega_b ~ lognormal(mu = -1.60944, sigma = 0.5)
lm_clbr_omega_g ~ lognormal(mu = -1.60944, sigma = 0.5)
lm_clbr_omega_c ~ lognormal(mu = -1.60944, sigma = 0.5)
lm_clbr_omega_p ~ lognormal(mu = -1.60944, sigma = 0.5)
lm_clbr_sigma ~ lognormal(mu = -2.30259, sigma = 0.5)
lm_clbr_omega_b ~ lognormal(mu = -1.60944, sigma = 0.5) T[0, ]
lm_clbr_omega_g ~ lognormal(mu = -1.60944, sigma = 0.5) T[0, ]
lm_clbr_omega_c ~ lognormal(mu = -1.60944, sigma = 0.5) T[0, ]
lm_clbr_omega_p ~ lognormal(mu = -1.60944, sigma = 0.5) T[0, ]
lm_clbr_sigma ~ lognormal(mu = -2.30259, sigma = 0.5) T[0, ]
lm_clbr_eta_b ~ std_normal()
lm_clbr_eta_g ~ std_normal()
lm_clbr_eta_c ~ std_normal()
Expand All @@ -34,11 +34,11 @@
lm_clbr_mu_g ~ gamma(alpha = 2, beta = 1)
lm_clbr_mu_c ~ normal(mu = -0.91629, sigma = 0.5)
lm_clbr_mu_p ~ normal(mu = 0.69315, sigma = 0.5)
lm_clbr_omega_b ~ lognormal(mu = -1.60944, sigma = 0.5)
lm_clbr_omega_g ~ lognormal(mu = -1.60944, sigma = 0.5)
lm_clbr_omega_c ~ lognormal(mu = -1.60944, sigma = 0.5)
lm_clbr_omega_p ~ lognormal(mu = -1.60944, sigma = 0.5)
lm_clbr_sigma ~ normal(mu = 0, sigma = 1)
lm_clbr_omega_b ~ lognormal(mu = -1.60944, sigma = 0.5) T[0, ]
lm_clbr_omega_g ~ lognormal(mu = -1.60944, sigma = 0.5) T[0, ]
lm_clbr_omega_c ~ lognormal(mu = -1.60944, sigma = 0.5) T[0, ]
lm_clbr_omega_p ~ lognormal(mu = -1.60944, sigma = 0.5) T[0, ]
lm_clbr_sigma ~ normal(mu = 0, sigma = 1) T[0, ]
lm_clbr_eta_b ~ std_normal()
lm_clbr_eta_g ~ std_normal()
lm_clbr_eta_c ~ std_normal()
Expand Down
20 changes: 10 additions & 10 deletions tests/testthat/_snaps/LongitudinalGSF.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
lm_gsf_mu_ks ~ normal(mu = -0.69315, sigma = 1)
lm_gsf_mu_kg ~ normal(mu = -1.20397, sigma = 1)
lm_gsf_mu_phi ~ normal(mu = 0, sigma = 1)
lm_gsf_omega_bsld ~ lognormal(mu = -1.60944, sigma = 1)
lm_gsf_omega_ks ~ lognormal(mu = -1.60944, sigma = 1)
lm_gsf_omega_kg ~ lognormal(mu = -1.60944, sigma = 1)
lm_gsf_omega_phi ~ lognormal(mu = -1.60944, sigma = 1)
lm_gsf_sigma ~ lognormal(mu = -2.30259, sigma = 1)
lm_gsf_omega_bsld ~ lognormal(mu = -1.60944, sigma = 1) T[0, ]
lm_gsf_omega_ks ~ lognormal(mu = -1.60944, sigma = 1) T[0, ]
lm_gsf_omega_kg ~ lognormal(mu = -1.60944, sigma = 1) T[0, ]
lm_gsf_omega_phi ~ lognormal(mu = -1.60944, sigma = 1) T[0, ]
lm_gsf_sigma ~ lognormal(mu = -2.30259, sigma = 1) T[0, ]
lm_gsf_eta_tilde_bsld ~ std_normal()
lm_gsf_eta_tilde_ks ~ std_normal()
lm_gsf_eta_tilde_kg ~ std_normal()
Expand All @@ -33,11 +33,11 @@
lm_gsf_mu_ks ~ normal(mu = -0.69315, sigma = 1)
lm_gsf_mu_kg ~ gamma(alpha = 2, beta = 1)
lm_gsf_mu_phi ~ normal(mu = 0, sigma = 1)
lm_gsf_omega_bsld ~ lognormal(mu = -1.60944, sigma = 1)
lm_gsf_omega_ks ~ lognormal(mu = -1.60944, sigma = 1)
lm_gsf_omega_kg ~ lognormal(mu = -1.60944, sigma = 1)
lm_gsf_omega_phi ~ lognormal(mu = -1.60944, sigma = 1)
lm_gsf_sigma ~ normal(mu = 0, sigma = 1)
lm_gsf_omega_bsld ~ lognormal(mu = -1.60944, sigma = 1) T[0, ]
lm_gsf_omega_ks ~ lognormal(mu = -1.60944, sigma = 1) T[0, ]
lm_gsf_omega_kg ~ lognormal(mu = -1.60944, sigma = 1) T[0, ]
lm_gsf_omega_phi ~ lognormal(mu = -1.60944, sigma = 1) T[0, ]
lm_gsf_sigma ~ normal(mu = 0, sigma = 1) T[0, ]
lm_gsf_eta_tilde_bsld ~ std_normal()
lm_gsf_eta_tilde_ks ~ std_normal()
lm_gsf_eta_tilde_kg ~ std_normal()
Expand Down
8 changes: 4 additions & 4 deletions tests/testthat/_snaps/LongitudinalRandomSlope.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
Random Slope Longitudinal Model with parameters:
lm_rs_intercept ~ normal(mu = 30, sigma = 10)
lm_rs_slope_mu ~ normal(mu = 1, sigma = 3)
lm_rs_slope_sigma ~ lognormal(mu = 0, sigma = 1.5)
lm_rs_sigma ~ lognormal(mu = 0, sigma = 1.5)
lm_rs_slope_sigma ~ lognormal(mu = 0, sigma = 1.5) T[0, ]
lm_rs_sigma ~ lognormal(mu = 0, sigma = 1.5) T[0, ]
lm_rs_ind_rnd_slope ~ <None>

Expand All @@ -24,8 +24,8 @@
Random Slope Longitudinal Model with parameters:
lm_rs_intercept ~ normal(mu = 0, sigma = 1)
lm_rs_slope_mu ~ normal(mu = 1, sigma = 3)
lm_rs_slope_sigma ~ lognormal(mu = 0, sigma = 1.5)
lm_rs_sigma ~ gamma(alpha = 2, beta = 1)
lm_rs_slope_sigma ~ lognormal(mu = 0, sigma = 1.5) T[0, ]
lm_rs_sigma ~ gamma(alpha = 2, beta = 1) T[0, ]
lm_rs_ind_rnd_slope ~ <None>

Loading

0 comments on commit 5a76e76

Please sign in to comment.