I have written a tutorial on how to expose user-defined Stan functions to R using CmdStanR and Rcpp. You can read the tutorial here .
I have refactored the expose_cmdstanr_functions()
R function that I previously posted on Discourse a few times. The code can be found here . It is also linked and included in the tutorial
Any comments or suggestions are welcome. You can comment here or on the repository’s issue tracker .
11 Likes
Thanks so much. I LOVE THIS!!! So great to test and apply functions directly with cmdstanr (rather than rstan).
One question is about compiling. Right now it looks like the expose command recompiles the Stan code (which takes time). Is there anyway after compiling once to keep the compiled code in the same directory with Stan code and then subsequently just load the compiled code? This would be directly analogous to what we can do with full Stan code. We compile it once and the next time it can just be loaded (and cmdstanr tells us the model executable is up to date).
1 Like
Thanks for the kind words.
Yes, I think that should be doable. Would you mind posting this request on the issue tracker so I dont forget? Might be able to get to this over the weekend.
1 Like
@rok_cesnovar
Thank you very much for expose_cmdstanr_functions()
. It is very useful and helpful. Let me ask you one question.
I get an error when I compile a function that contains ode_bdf
in the attached R and Stan code. If I use ode_rk45
instead of ode_bdf
, no error occurs and I can use the time_dev
normally in R. I think this is not due to expose_cmdstanr_functions.R
, but I have no idea where to report it. Any suggestions?
test_model.stan (705 Bytes)
run-test_model.R (283 Bytes)
Environment: Ubuntu 20.04, R 4.2.1, cmdstan 2.31.0, cmdstanr 0.5.3
Error messages (sorry for some Japanese):
/usr/bin/ld: /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/sundials_6.1.1/lib/libsundials_cvodes.a(cvodes.o): relocation R_X86_64_PC32 against symbol `stderr@@GLIBC_2.2.5' can not be used when making a shared object。 -fPIC を付けて再コンパイルしてください。
/usr/bin/ld: 最終リンクに失敗しました: bad value
collect2: error: ld returned 1 exit status
make: *** [/usr/share/R/share/make/shlib.mk:10: sourceCpp_4.so] エラー 1
g++ -std=gnu++14 -I"/usr/share/R/include" -DNDEBUG -I"/home/username/R/x86_64-pc-linux-gnu-library/4.2/Rcpp/include" -I"/home/username/R/x86_64-pc-linux-gnu-library/4.2/RcppEigen/include" -I"/tmp/RtmpTVrvKe/sourceCpp-x86_64-pc-linux-gnu-1.0.10" -std=c++1y -pthread -D_REENTRANT -Wno-sign-compare -Wno-ignored-attributes -I /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/tbb_2020.3/include -O3 -I /home/username/.cmdstan/cmdstan-2.31.0/src -I /home/username/.cmdstan/cmdstan-2.31.0/stan/src -I /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/rapidjson_1.1.0/ -I /home/username/.cmdstan/cmdstan-2.31.0/lib/CLI11-1.9.1/ -I /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/ -I /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/eigen_3.3.9 -I /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/boost_1.78.0 -I /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/sundials_6.1.1/include -I /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/sundials_6.1.1/src/sundials -fpic -g -O2 -fdebug-prefix-map=/build/r-base-zYgbYq/r-base-4.2.1=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -c file11f0ca68826bb8.cpp -o file11f0ca68826bb8.o
g++ -std=gnu++14 -shared -L/usr/lib/R/lib -Wl,-Bsymbolic-functions -Wl,-z,relro -o sourceCpp_4.so file11f0ca68826bb8.o -Wl,-L,/home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/tbb -Wl,-rpath,/home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/tbb /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/sundials_6.1.1/lib/libsundials_nvecserial.a /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/sundials_6.1.1/lib/libsundials_cvodes.a /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/sundials_6.1.1/lib/libsundials_idas.a /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/sundials_6.1.1/lib/libsundials_kinsol.a /home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/tbb/libtbb.so.2 -Wl,-L,/home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/tbb -Wl,-rpath,/home/username/.cmdstan/cmdstan-2.31.0/stan/lib/stan_math/lib/tbb -L/usr/lib/R/lib -lR
Rcpp::sourceCpp(code = code, env = env) でエラー:
Error 1 occurred building shared library.
1 Like
This functionality is now available in the development version of cmdstanr itself. Just install from github:
remotes::install_github("stan-dev/cmdstanr")
Then you can request the model functions be compiled in your cmdstan_model()
call:
mod <- cmdstan_model(stan_file = stanfile, compile_standalone = TRUE)
Your model’s functions will then be available in the functions
member of the model object:
mod$functions$your_function()
You can make the functions available in your global R environment using expose_functions(global = TRUE
):
mod$expose_functions(global = TRUE)
your_function()
You can also see the tests for more examples
5 Likes
@andrjohns
Thank you very much for the reply. I can use compile_standalone = TRUE
option and ode_rk45
. It is very easy to use! But, I got the same error when I used ode_bdf
.
It looks like either a lack of compile options or a namespace issue when using ode_bdf
…
Apologies for the delay in responding, but I’ve just checked the test_model.stan
file you posted using the latest cmdstanr
github version with cmdstan
2.31.0, and all compiled without issue.
Would you be able to double-check your cmdstan
version and the code you’re using?
If I try to expose functions I get this error:
Exporting standalone functions is not possible with a pre-compiled Stan model!
It seems that the only way to get around this is to force re-compilation. Is there a way to make this work without re-compilation?
Thanks,
Karim
cmdstanr version 0.8.0.9000
cmdstan version 2.35
Yes the next release (not the one later today) will support exposing functions with a precompiled model - as long as the Stan code is available
4 Likes
karimn
June 7, 2024, 3:42pm
10
Would I be able to pick this update from github?
remotes::install_github("stan-dev/cmdstanr")
I’m transition my workflow to {targets} and I rely on exposed functions to prepare my analysis data. Forcing the model to be recompiled every time throws a wrench in things.
Right now, with {targets}, a model compiled with compile_standalone = TRUE
and loaded from disk has a nil pointer that seems to mess things up.
.Call(<pointer: (nil)>, pfs, death_week, n_patient_tumors, n_measures,
t_measure)
Sorry to bug you about this @andrjohns , but I’m happy to test any fix for you! Just let me know where I can pick up the change. Is this a cmdstanr fix or something in the Stan compiler?
wds15
June 11, 2024, 9:14am
11
Maybe this code in this issue can help you from below. I am heavily using it and it is super useful in my workflow which sounds related. The idea is to create a dummy R package which has only the Stan functions inside. The dummy package takes care of those annoying details like the pointers for random generators, etc. Caching of the generated binaries works like a charm with this.
opened 09:28AM - 02 Nov 23 UTC
feature
I am using Stan defined functions extensively in my R workflow and as such I wan… t to cache the compiled C++ codes in order to avoid any re-compilation whenever R starts again. I would only want the functions to be recompiled if they actually change.
Here is a possible implementation, which does what I want - and it works for me on Linux and macOS just fine. The code uses an internal `cmdstanr` function and it would thus be great to support this behaviour directly in `cmdstanr`.
This approach lifts the current restriction to expose functions from models with pre-compiled model binaries in addition. This would be very helpful for brms models as well, since for these the use of pre-compiled models (enabled via the use of `mdstanr_write_stan_file_dir`) is very convenient, but `expose_functions` does not work at the moment in this case.
``` r
suppressPackageStartupMessages(library(cmdstanr))
suppressPackageStartupMessages(library(here))
## utility function works with cmdstanr 0.6.0 which exports via
## cmdstanr (and Stan 2.32 at leat) the functions from Stan to R
cmdstan_expose_functions <- function(...) {
pseudo_model_code <- paste(c("functions {", ..., "}"), collapse="\n")
functions_hash <- rlang::hash(pseudo_model_code)
model_name <- paste0("model-functions-", functions_hash)
## note: cmdstanr somehow only compiles standalone functions
## whenever one is compiling the model (and not allowing to export
## the functions if one is not compiling it). This is why
## force_compile=TRUE is a save option
##pseudo_model <- cmdstanr::cmdstan_model(cmdstanr::write_stan_file(pseudo_model_code), compile_standalone=TRUE, force_compile=TRUE, stanc_options=list(name=paste0("model-functions-", functions_hash)))
##pseudo_model$functions
## but things seem to work ok if we abuse a bit the internals... tested with cmdstanr 0.6.1
## note that we have to set the model name manually to a
## determinstic string (depending only on the stan functions being
## compiled)
stan_file <- cmdstanr::write_stan_file(pseudo_model_code)
pseudo_model <- cmdstanr::cmdstan_model(stan_file, stanc_options=list(name=model_name))
pseudo_model$functions$existing_exe <- FALSE
pseudo_model$functions$external <- FALSE
stancflags_standalone <- c("--standalone-functions", paste0("--name=", model_name))
pseudo_model$functions$hpp_code <- cmdstanr:::get_standalone_hpp(stan_file, stancflags_standalone)
pseudo_model$expose_functions(FALSE, FALSE) ## will return the functions in an environment
pseudo_model$functions
}
```
example Stan function
``` r
stan_function <- "
real heavy_work(real m) {
return m * m;
}
"
```
we want to cache the binaries need to created the Stan functions in the R session.
<U+00A0>For this to work we need to
- get cmdstanr to write Stan files always to the same persistent directory
- setup Rcpp to also use a caching directory persistently
cmdstanr and Rcpp use by default caching directories in the
temporary R directory, which gets wiped out everytime we restart R
``` r
options(cmdstanr_write_stan_file_dir=here("brms-cache"))
```
create cache directory if not yet available
``` r
dir.create(here("brms-cache"), FALSE)
```
cache exposed Stan functions
``` r
options(rcpp.cache.dir=here("rcpp-cache"))
dir.create(here("rcpp-cache"), FALSE)
```
compile functions upon the first time
``` r
system.time(funs <- cmdstan_expose_functions(stan_function))
#> ld: warning: duplicate -rpath '/Users/weberse2/.cmdstanr/cmdstan-2.32.2/stan/lib/stan_math/lib/tbb' ignored
#> Compiling standalone functions...
#> user system elapsed
#> 6.459 1.134 8.958
```
do it again<U+2026> now this is a lot faster as now more compilation takes place
``` r
system.time(funs_cached <- cmdstan_expose_functions(stan_function))
#> Compiling standalone functions...
#> user system elapsed
#> 0.295 0.308 0.930
ls(funs)
#> [1] "compiled" "existing_exe" "external" "fun_names" "heavy_work"
#> [6] "hpp_code"
ls(funs_cached)
#> [1] "compiled" "existing_exe" "external" "fun_names" "heavy_work"
#> [6] "hpp_code"
funs$heavy_work(10)
#> [1] 100
funs_cached$heavy_work(10)
#> [1] 100
```
# Session Info
``` r
sessionInfo()
#> R version 4.1.0 (2021-05-18)
#> Platform: aarch64-apple-darwin20 (64-bit)
#> Running under: macOS 13.6.1
#>
#> Matrix products: default
#> BLAS: /Library/Frameworks/R.framework/Versions/4.1-arm64/Resources/lib/libRblas.dylib
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.1-arm64/Resources/lib/libRlapack.dylib
#>
#> locale:
#> [1] C
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] here_1.0.1 cmdstanr_0.6.1
#>
#> loaded via a namespace (and not attached):
#> [1] Rcpp_1.0.11 pillar_1.8.1 compiler_4.1.0
#> [4] highr_0.9 tools_4.1.0 digest_0.6.30
#> [7] lattice_0.20-45 evaluate_0.17 lifecycle_1.0.3
#> [10] tibble_3.1.8 checkmate_2.1.0 gtable_0.3.1
#> [13] pkgconfig_2.0.3 rlang_1.1.1 Matrix_1.3-3
#> [16] reprex_2.0.2 DBI_1.1.3 cli_3.4.1
#> [19] yaml_2.3.6 xfun_0.37 fastmap_1.1.0
#> [22] dplyr_1.0.10 withr_2.5.0 styler_1.5.1
#> [25] stringr_1.4.1 knitr_1.40 generics_0.1.3
#> [28] fs_1.5.2 vctrs_0.5.0 rprojroot_2.0.3
#> [31] tidyselect_1.2.0 grid_4.1.0 glue_1.6.2
#> [34] R6_2.5.1 processx_3.7.0 fansi_1.0.3
#> [37] distributional_0.3.2 rmarkdown_2.20 tensorA_0.36.2
#> [40] purrr_0.3.5 farver_2.1.1 ggplot2_3.4.2
#> [43] posterior_1.4.1 magrittr_2.0.3 ps_1.7.1
#> [46] backports_1.4.1 scales_1.2.1 htmltools_0.5.3
#> [49] assertthat_0.2.1 abind_1.4-5 colorspace_2.0-3
#> [52] utf8_1.2.2 stringi_1.7.8 munsell_0.5.0
#> [55] RcppEigen_0.3.3.9.3
```
<sup>Created on 2023-11-02 with [reprex v2.0.2](https://reprex.tidyve
5 Likes