How to constrain regression parameters (eg. slopes <= 0) in stan_lmer (or in any rstanarm functions)

Hello! I’m trying to add some constraint to the regression slopes in my model below:

stan_lmer(Response ~ Time:Temp + (Time:Temp|Batch), data = d, …)

In this model, I have 3 fixed time slopes (as Temp has 3 levels).
I hope to constrain all those 3 slopes to be <= 0. While this could be easily done in stan parameters block, I am not sure how to do this in rstanarm functions.

I couldn’t figure out how to add this as a constraint, so alternatively I tried to express this constraint as priors to add to stan_lmer, but could not find a relevant choice. I would really appreciate your advice on how to add constraints to the parameters!

Unfortunately there’s no way to do the strict lower bound in rstanarm (you can put informative priors on the “fixed effects” using the prior argument but no strict bound).

If you definitely want the lower bound and don’t want to code it up in Stan yourself then I think it should be possible with the brms R package. I‘ve never tried it myself with brms so I’m not exactly sure how to do it. Maybe via the set_prior() function. If you try to figure that out with brms but can’t I recommend starting a new topic here on the forum in the brms category.

Hi Jonah,
Thanks so much for your response! It’s helpful to confirm that there’s no way to do the lower bound in rstanarm. I had tried informative priors based on the priors supported in rstanarm, but as you noted I could not find a way to impose <= 0 through priors. Is there a way to put uniform prior with (-Inf, 0) in rstanarm? Or maybe express the prior as the negative of a positive distribution?

In the meantime, I will look at brms to see if I can impose constraints as you advised. I may indeed create a new topic in brms for follow up discussions. Thank you again!

In brms, the prior specification would go like this, where ub is the upper bound on the prior:


m = brm(formula = Response ~ Time:Temp + (Time:Temp|Batch), 
        family = gaussian(),
        data = d, 
        prior = prior(normal(0,2), class="b", ub=0),
        backend = "cmdstanr")

I used cmdstanr as the backend (so you’d need to have CmdStan and cmdstanr installed), because setting a bound on the prior with rstan, which is the default backend, crashed R when I tried this on my computer. sample_prior="yes" means that the returned model object will include draws from the prior in addition to the posterior.

Note that when setting a bounded prior as in the example above, you can’t set different priors for the individual parameters, but can only set that one prior and it applies to all of the population-level parameters in the regression formula (except for the intercept). See here for a way to set individual priors for each parameter when using bounded priors, using brms’s non-linear formula syntax.


Not at the moment, unfortunately. The brms version that @joels gave seems promising though.

1 Like

Hi Joels,
Thanks so much for the response! It’s really helpful to know that we can specify upper bound easily in there. brms runs really slow on my PC so I usually went with rstanarm but I think backend = “cmdstanr” might be the solution for that! I am in the process of installing CmdStan and cmdstanr as you advised. I will share any update once I try them out! Looking forward to running your brm script once I install them.

1 Like

Hi Jonah,
Thanks for confirming that there’s no uniform prior available in rstanarm! Yes, I’m in the process of trying out brms code as Joels suggested. 😊

1 Like

Hi @heroyui22

FYI. I noticed my (Windows) computer runs slower if I set the mc.cores() option. I stopped doing that, and my models seem to fit faster.

I will also note I do use options(mc.cores = parallel::detectCores()) on my linux machine, and believe models run faster with it enabled there. :)

Posting a quick reprex, it does not show an issue proper, but documents the reference, and my sessionInfo().

Hope this helps.

#> Loading required package: Rcpp
#> This is rstanarm version 2.26.1
#> - See for changes to default priors!
#> - Default priors may change, so it's safest to specify priors, even if equivalent to the defaults.
#> - For execution on a local, multicore CPU with excess RAM we recommend calling
#>   options(mc.cores = parallel::detectCores())
#> R version 4.2.3 (2023-03-15 ucrt)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 10 x64 (build 19044)
#> Matrix products: default
#> locale:
#> [1] LC_COLLATE=English_United States.utf8 
#> [2] LC_CTYPE=English_United States.utf8   
#> [3] LC_MONETARY=English_United States.utf8
#> [4] LC_NUMERIC=C                          
#> [5] LC_TIME=English_United States.utf8    
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> other attached packages:
#> [1] rstanarm_2.26.1 Rcpp_1.0.11    
#> loaded via a namespace (and not attached):
#>   [1] nlme_3.1-162         matrixStats_1.0.0    fs_1.6.3            
#>   [4] xts_0.13.1           threejs_0.3.3        rstan_2.26.23       
#>   [7] tensorA_0.36.2       R.cache_0.16.0       backports_1.4.1     
#>  [10] tools_4.2.3          utf8_1.2.3           R6_2.5.1            
#>  [13] DT_0.29              colorspace_2.1-0     withr_2.5.0         
#>  [16] tidyselect_1.2.0     gridExtra_2.3        prettyunits_1.1.1   
#>  [19] processx_3.8.2       compiler_4.2.3       cli_3.6.1           
#>  [22] shinyjs_2.1.0        posterior_1.4.1      colourpicker_1.3.0  
#>  [25] checkmate_2.2.0      scales_1.2.1         dygraphs_1.1.1.6    
#>  [28] callr_3.7.3          QuickJSR_1.0.6       stringr_1.5.0       
#>  [31] digest_0.6.33        StanHeaders_2.26.27  minqa_1.2.5         
#>  [34] rmarkdown_2.24       R.utils_2.12.2       base64enc_0.1-3     
#>  [37] pkgconfig_2.0.3      htmltools_0.5.6      lme4_1.1-34         
#>  [40] styler_1.10.2        fastmap_1.1.1        htmlwidgets_1.6.2   
#>  [43] rlang_1.1.1          rstudioapi_0.15.0    shiny_1.7.5         
#>  [46] farver_2.1.1         generics_0.1.3       zoo_1.8-12          
#>  [49] jsonlite_1.8.7       crosstalk_1.2.0      gtools_3.9.4        
#>  [52] distributional_0.3.2 dplyr_1.1.2          R.oo_1.25.0         
#>  [55] inline_0.3.19        magrittr_2.0.3       loo_2.6.0           
#>  [58] bayesplot_1.10.0     Matrix_1.5-3         munsell_0.5.0       
#>  [61] fansi_1.0.4          abind_1.4-5          lifecycle_1.0.3     
#>  [64] R.methodsS3_1.8.2    stringi_1.7.12       yaml_2.3.7          
#>  [67] MASS_7.3-58.2        pkgbuild_1.4.2       plyr_1.8.8          
#>  [70] grid_4.2.3           parallel_4.2.3       promises_1.2.1      
#>  [73] crayon_1.5.2         miniUI_0.1.1.1       lattice_0.20-45     
#>  [76] splines_4.2.3        knitr_1.43           ps_1.7.5            
#>  [79] pillar_1.9.0         igraph_1.5.1         boot_1.3-28.1       
#>  [82] markdown_1.8         shinystan_2.6.0      reshape2_1.4.4      
#>  [85] codetools_0.2-19     stats4_4.2.3         rstantools_2.3.1.1  
#>  [88] reprex_2.0.2         glue_1.6.2           evaluate_0.21       
#>  [91] RcppParallel_5.1.7   nloptr_2.0.3         vctrs_0.6.3         
#>  [94] httpuv_1.6.11        gtable_0.3.4         purrr_1.0.2         
#>  [97] ggplot2_3.4.3        xfun_0.40            mime_0.12           
#> [100] xtable_1.8-4         later_1.3.1          survival_3.5-3      
#> [103] tibble_3.2.1         shinythemes_1.2.0    ellipsis_0.3.2

Created on 2023-12-17 with reprex v2.0.2