diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index d2ecd46e..4a000003 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -216,12 +216,6 @@ jobs: path: ./test_models/ key: ${{ hashFiles('**/*.stan', 'src/*', 'stan/src/stan/version.hpp', 'Makefile') }}-${{ matrix.os }}-v${{ env.CACHE_VERSION }} - # needed for R tests until they have compilation utilities and can set this themselves. - - name: Set up TBB - if: matrix.os == 'windows-latest' - run: | - Add-Content $env:GITHUB_PATH "$(pwd)/stan/lib/stan_math/lib/tbb" - - name: Run tests if: matrix.os != 'windows-latest' run: | @@ -231,6 +225,8 @@ jobs: Rscript -e "devtools::test(reporter = c(\"summary\", \"fail\"))" Rscript -e "install.packages(getwd(), repos=NULL, type=\"source\")" Rscript example.R + env: + BRIDGESTAN: ${{ github.workspace }} - name: Run tests (windows) if: matrix.os == 'windows-latest' @@ -241,6 +237,8 @@ jobs: Rscript -e 'devtools::test(reporter = c(\"summary\", \"fail\"))' Rscript -e 'install.packages(getwd(), repos=NULL, type=\"source\")' Rscript example.R + env: + BRIDGESTAN: ${{ github.workspace }} rust: needs: [build] diff --git a/R/NAMESPACE b/R/NAMESPACE index 0536bef0..07972325 100644 --- a/R/NAMESPACE +++ b/R/NAMESPACE @@ -1,3 +1,5 @@ # Generated by roxygen2: do not edit by hand export(StanModel) +export(compile_model) +export(set_bridgestan_path) diff --git a/R/R/bridgestan.R b/R/R/bridgestan.R index c241e17d..e675a3a5 100755 --- a/R/R/bridgestan.R +++ b/R/R/bridgestan.R @@ -9,12 +9,19 @@ StanModel <- R6::R6Class("StanModel", public = list( #' @description #' Create a Stan Model instance. - #' @param lib A path to a compiled BridgeStan Shared Object file. + #' @param lib A path to a compiled BridgeStan Shared Object file or a .stan file (will be compiled). #' @param data Either a JSON string literal, a path to a data file in JSON format ending in ".json", or the empty string. #' @param seed Seed for the RNG used in constructing the model. + #' @param stanc_args A list of arguments to pass to stanc3 if the model is not already compiled. + #' @param make_args A list of additional arguments to pass to Make if the model is not already compiled. #' @return A new StanModel. - initialize = function(lib, data, seed) { + initialize = function(lib, data, seed, stanc_args = NULL, make_args = NULL) { + if (tools::file_ext(lib) == "stan") { + lib <- compile_model(lib, stanc_args, make_args) + } + if (.Platform$OS.type == "windows"){ + windows_path_setup() lib_old <- lib lib <- paste0(tools::file_path_sans_ext(lib), ".dll") file.copy(from=lib_old, to=lib) @@ -75,7 +82,8 @@ StanModel <- R6::R6Class("StanModel", PACKAGE = private$lib_name )$info_out }, - + #' @description + #' Get the version of BridgeStan used in the compiled model. model_version= function() { .C("bs_version_R", major = as.integer(0), @@ -345,7 +353,7 @@ handle_error <- function(lib_name, err_msg, err_ptr, function_name) { #' StanRNG #' #' RNG object for use with `StanModel$param_constrain()` -#' @field rng The pointer to the RNG object. +#' @field ptr The pointer to the RNG object. #' @keywords internal StanRNG <- R6::R6Class("StanRNG", public = list( diff --git a/R/R/compile.R b/R/R/compile.R new file mode 100644 index 00000000..5948da1b --- /dev/null +++ b/R/R/compile.R @@ -0,0 +1,110 @@ +IS_WINDOWS <- isTRUE(.Platform$OS.type == "windows") +MAKE <- Sys.getenv("MAKE", ifelse(IS_WINDOWS, "mingw32-make", "make")) + + +#' Get the path to BridgeStan. +#' +#' By default this is set to the value of the environment +#' variable `BRIDGESTAN`. +#' +#' If there is no path set, this function will download +#' a matching version of BridgeStan to a folder called +#' `.bridgestan` in the user's home directory. +#' +#' See also `set_bridgestan_path` +verify_bridgestan_path <- function(path) { + suppressWarnings({ + folder <- normalizePath(path) + }) + if (!dir.exists(folder)) { + stop(paste0("BridgeStan folder '", folder, "' does not exist!\n", "If you need to set a different location, call 'set_bridgestan_path()'")) + } + makefile <- file.path(folder, "Makefile") + if (!file.exists(makefile)) { + stop(paste0("BridgeStan folder '", folder, "' does not contain file 'Makefile',", + " please ensure it is built properly!\n", "If you need to set a different location, call 'set_bridgestan_path()'")) + } +} + +#' Set the path to BridgeStan. +#' +#' This should point to the top-level folder of the repository. +#' @export +set_bridgestan_path <- function(path) { + verify_bridgestan_path(path) + Sys.setenv(BRIDGESTAN = normalizePath(path)) +} + +get_bridgestan_path <- function() { + # try to get from environment + path <- Sys.getenv("BRIDGESTAN", unset = "") + if (path == "") { + path <- CURRENT_BRIDGESTAN + tryCatch({ + verify_bridgestan_path(path) + }, error = function(e) { + print(paste0("Bridgestan not found at location specified by $BRIDGESTAN ", + "environment variable, downloading version ", packageVersion("bridgestan"), + " to ", path)) + get_bridgestan_src() + }) + } + + return(path) +} + + +#' Run BridgeStan's Makefile on a `.stan` file, creating the `.so` +#' used by the StanModel class. +#' This function checks that the path to BridgeStan is valid +#' and will error if not. This can be set with `set_bridgestan_path`. +#' +#' @param stan_file A path to a Stan model file. +#' @param stanc_arg A vector of arguments to pass to stanc3. +#' For example, `c('--O1')` will enable compiler optimization level 1. +#' @param make_args A vector of additional arguments to pass to Make. +#' For example, `c('STAN_THREADS=True')` will enable +#' threading for the compiled model. If the same flags are defined +#' in `make/local`, the versions passed here will take precedent. +#' @return Path to the compiled model. +#' @export +compile_model <- function(stan_file, stanc_args = NULL, make_args = NULL) { + verify_bridgestan_path(get_bridgestan_path()) + suppressWarnings({ + file_path <- normalizePath(stan_file) + }) + if (tools::file_ext(file_path) != "stan") { + stop(paste0("File '", file_path, "' does not end with '.stan'")) + } + if (!file.exists(file_path)) { + stop(paste0("File '", file_path, "' does not exist!")) + } + + output <- paste0(tools::file_path_sans_ext(file_path), "_model.so") + stancflags <- paste("--include-paths=.", paste(stanc_args, collapse = " ")) + + flags <- c(paste("-C", get_bridgestan_path()), make_args, paste0("STANCFLAGS=\"", + stancflags, "\""), output) + + suppressWarnings({ + res <- system2(MAKE, args = flags, stdout = TRUE, stderr = TRUE) + }) + res_attrs <- attributes(res) + if ("status" %in% names(res_attrs) && res_attrs$status != 0) { + stop(paste0("Compilation failed with error code ", res_attrs$status, "\noutput:\n", + paste(res, collapse = "\n"))) + } + + return(output) +} + +windows_path_setup <- function() { + if (.Platform$OS.type == "windows") { + suppressWarnings(out <- system2("where.exe", "tbb.dll", stdout = NULL, stderr = NULL)) + if (out != 0) { + tbb_path <- file.path(get_bridgestan_path(), "stan", "lib", "stan_math", + "lib", "tbb") + Sys.setenv(PATH = paste(tbb_path, Sys.getenv("PATH"), sep = ";")) + } + } +} diff --git a/R/R/download.R b/R/R/download.R new file mode 100644 index 00000000..72cc21ef --- /dev/null +++ b/R/R/download.R @@ -0,0 +1,35 @@ +current_version <- packageVersion("bridgestan") +HOME_BRIDGESTAN <- path.expand(file.path("~", ".bridgestan")) +CURRENT_BRIDGESTAN <- file.path(HOME_BRIDGESTAN, paste0("bridgestan-", current_version)) + +RETRIES <- 5 + +get_bridgestan_src <- function() { + url <- paste0("https://github.com/roualdes/bridgestan/releases/download/", "v", + current_version, "/bridgestan-", current_version, ".tar.gz") + + dir.create(HOME_BRIDGESTAN, showWarnings = FALSE, recursive = TRUE) + temp <- tempfile() + err_text <- paste("Failed to download Bridgestan", current_version, "from github.com.") + for (i in 1:RETRIES) { + tryCatch({ + download.file(url, destfile = temp, mode = "wb", quiet = TRUE, method = "auto") + }, error = function(e) { + cat(err_text, "\n") + if (i == RETRIES) { + stop(err_text, call. = FALSE) + } else { + cat("Retrying (", i + 1, "/", RETRIES, ")...\n", sep = "") + Sys.sleep(1) + } + }) + } + + tryCatch({ + untar(temp, exdir = HOME_BRIDGESTAN) + }, error = function(e) { + stop(paste("Failed to unpack", url, "during installation"), call. = FALSE) + }) + + unlink(temp) +} diff --git a/R/man/StanModel.Rd b/R/man/StanModel.Rd index ffc8852d..11197612 100644 --- a/R/man/StanModel.Rd +++ b/R/man/StanModel.Rd @@ -41,17 +41,21 @@ as well as constraining and unconstraining transforms. \subsection{Method \code{new()}}{ Create a Stan Model instance. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{StanModel$new(lib, data, seed)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{StanModel$new(lib, data, seed, stanc_args = NULL, make_args = NULL)}\if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ -\item{\code{lib}}{A path to a compiled BridgeStan Shared Object file.} +\item{\code{lib}}{A path to a compiled BridgeStan Shared Object file or a .stan file (will be compiled).} \item{\code{data}}{Either a JSON string literal, a path to a data file in JSON format ending in ".json", or the empty string.} \item{\code{seed}}{Seed for the RNG used in constructing the model.} + +\item{\code{stanc_args}}{A list of arguments to pass to stanc3 if the model is not already compiled.} + +\item{\code{make_args}}{A list of additional arguments to pass to Make if the model is not already compiled.} } \if{html}{\out{
}} } @@ -89,6 +93,7 @@ A character vector of the Stan version and important flags. \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-StanModel-model_version}{}}} \subsection{Method \code{model_version()}}{ +Get the version of BridgeStan used in the compiled model. \subsection{Usage}{ \if{html}{\out{
}}\preformatted{StanModel$model_version()}\if{html}{\out{
}} } diff --git a/R/man/StanRNG.Rd b/R/man/StanRNG.Rd index a18b1b87..e5f14dda 100644 --- a/R/man/StanRNG.Rd +++ b/R/man/StanRNG.Rd @@ -15,14 +15,7 @@ RNG object for use with \code{StanModel$param_constrain()} \section{Public fields}{ \if{html}{\out{
}} \describe{ -\item{\code{rng}}{The pointer to the RNG object.} -} -\if{html}{\out{
}} -} -\section{Active bindings}{ -\if{html}{\out{
}} -\describe{ -\item{\code{rng}}{The pointer to the RNG object.} +\item{\code{ptr}}{The pointer to the RNG object.} } \if{html}{\out{
}} } diff --git a/R/man/compile_model.Rd b/R/man/compile_model.Rd new file mode 100644 index 00000000..bc5833c3 --- /dev/null +++ b/R/man/compile_model.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/compile.R +\name{compile_model} +\alias{compile_model} +\title{Run BridgeStan's Makefile on a \code{.stan} file, creating the \code{.so} +used by the StanModel class. +This function checks that the path to BridgeStan is valid +and will error if not. This can be set with \code{set_bridgestan_path}.} +\usage{ +compile_model(stan_file, stanc_args = NULL, make_args = NULL) +} +\arguments{ +\item{stan_file}{A path to a Stan model file.} + +\item{make_args}{A vector of additional arguments to pass to Make. +For example, \code{c('STAN_THREADS=True')} will enable +threading for the compiled model. If the same flags are defined +in \code{make/local}, the versions passed here will take precedent.} + +\item{stanc_arg}{A vector of arguments to pass to stanc3. +For example, \code{c('--O1')} will enable compiler optimization level 1.} +} +\value{ +Path to the compiled model. +} +\description{ +Run BridgeStan's Makefile on a \code{.stan} file, creating the \code{.so} +used by the StanModel class. +This function checks that the path to BridgeStan is valid +and will error if not. This can be set with \code{set_bridgestan_path}. +} diff --git a/R/man/set_bridgestan_path.Rd b/R/man/set_bridgestan_path.Rd new file mode 100644 index 00000000..4130a632 --- /dev/null +++ b/R/man/set_bridgestan_path.Rd @@ -0,0 +1,11 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/compile.R +\name{set_bridgestan_path} +\alias{set_bridgestan_path} +\title{Set the path to BridgeStan.} +\usage{ +set_bridgestan_path(path) +} +\description{ +This should point to the top-level folder of the repository. +} diff --git a/R/man/verify_bridgestan_path.Rd b/R/man/verify_bridgestan_path.Rd new file mode 100644 index 00000000..02b7b406 --- /dev/null +++ b/R/man/verify_bridgestan_path.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/compile.R +\name{verify_bridgestan_path} +\alias{verify_bridgestan_path} +\title{Get the path to BridgeStan.} +\usage{ +verify_bridgestan_path(path) +} +\description{ +By default this is set to the value of the environment +variable \code{BRIDGESTAN}. +} +\details{ +If there is no path set, this function will download +a matching version of BridgeStan to a folder called +\code{.bridgestan} in the user's home directory. + +See also \code{set_bridgestan_path} +} diff --git a/R/tests/testthat.R b/R/tests/testthat.R index 324d70c5..7ecf745d 100644 --- a/R/tests/testthat.R +++ b/R/tests/testthat.R @@ -1,11 +1,3 @@ -# This file is part of the standard setup for testthat. -# It is recommended that you do not modify it. -# -# Where should you do additional test configuration? -# Learn more about the roles of various files in: -# * https://r-pkgs.org/tests.html -# * https://testthat.r-lib.org/reference/test_package.html#special-files - library(testthat) library(bridgestan) diff --git a/R/tests/testthat/setup.R b/R/tests/testthat/setup.R new file mode 100644 index 00000000..05d083ed --- /dev/null +++ b/R/tests/testthat/setup.R @@ -0,0 +1,15 @@ +base = "../../.." + +load_model <- function(name, include_data = TRUE) { + if (include_data) { + data = file.path(base, "test_models", name, paste0(name, ".data.json")) + } else { + data = "" + } + model <- StanModel$new(file.path(base, "test_models", name, paste0(name, "_model.so")), + data, 1234) + return(model) +} + +simple <- load_model("simple") +bernoulli <- load_model("bernoulli") diff --git a/R/tests/testthat/test_collisions.R b/R/tests/testthat/test_collisions.R new file mode 100644 index 00000000..57b9c0bd --- /dev/null +++ b/R/tests/testthat/test_collisions.R @@ -0,0 +1,13 @@ + +test_that("loading another library didn't break prior ones", { + if (.Platform$OS.type == "windows") { + dll = "./test_collisions.dll" + } else { + dll = "./test_collisions.so" + } + if (file.exists(dll)) { + dyn.load(dll) + expect_equal(bernoulli$name(), "bernoulli_model") + expect_equal(simple$name(), "simple_model") + } +}) diff --git a/R/tests/testthat/test_compile.R b/R/tests/testthat/test_compile.R new file mode 100644 index 00000000..d59e0b88 --- /dev/null +++ b/R/tests/testthat/test_compile.R @@ -0,0 +1,37 @@ + + +test_that("compilation works", { + name <- "multi" + file <- file.path(base, "test_models", name, paste0(name, ".stan")) + + lib <- file.path(base, "test_models", name, paste0(name, "_model.so")) + unlink(lib, force = TRUE) + + out <- compile_model(file, stanc_args = c("--O1")) + + expect_true(file.exists(lib)) + expect_equal(normalizePath(lib), normalizePath(out)) + + unlink(lib, force = TRUE) + + out <- compile_model(file, make_args = c("STAN_THREADS=True")) +}) + +test_that("compilation fails on non-stan file", { + expect_error(compile_model(file.path(base, "test_models", "simple", "simple.data.json")), + "does not end with '.stan'") +}) + +test_that("compilation fails on missing file", { + expect_error(compile_model("badpath.stan"), "does not exist!") +}) + +test_that("compilation fails on bad syntax", { + expect_error(compile_model(file.path(base, "test_models", "syntax_error", "syntax_error.stan")), + "Compilation failed") +}) + +test_that("bad paths fail", { + expect_error(set_bridgestan_path("badpath"), "does not exist!") + expect_error(set_bridgestan_path(file.path(base, "test_models")), "does not contain file 'Makefile'") +}) diff --git a/R/tests/testthat/test-bridgestan.R b/R/tests/testthat/test_model.R similarity index 57% rename from R/tests/testthat/test-bridgestan.R rename to R/tests/testthat/test_model.R index bb412038..a987e65c 100644 --- a/R/tests/testthat/test-bridgestan.R +++ b/R/tests/testthat/test_model.R @@ -1,20 +1,9 @@ -base = "../../.." - -load_model <- function(name, include_data=TRUE) { - if (include_data){ - data = file.path(base, paste0("/test_models/", name, "/",name,".data.json")) - } else { - data = "" - } - model <- StanModel$new(file.path(base,paste0("/test_models/", name, "/",name,"_model.so")), data, 1234) - return(model) -} + test_that("missing data throws error", { - expect_error(load_model("simple",include_data=FALSE)) + expect_error(load_model("simple", include_data = FALSE)) }) -simple <- load_model("simple") test_that("simple_model name is correct", { expect_identical(simple$name(), "simple_model") }) @@ -31,7 +20,7 @@ test_that("simple_model has 5 unconstrained parameters", { expect_equal(simple$param_unc_num(), 5) }) -test_that("simple_model grad(x) is -x",{ +test_that("simple_model grad(x) is -x", { x <- runif(5) expect_equal(-x, simple$log_density_gradient(x)$gradient) }) @@ -50,51 +39,37 @@ test_that("models can be passed NAN or INF", { }) -test_that("simple_model Hessian is -I",{ +test_that("simple_model Hessian is -I", { x <- runif(5) expect_equal(-diag(5), simple$log_density_hessian(x)$hessian) }) -test_that("simple_model Hvp returns -v",{ +test_that("simple_model Hvp returns -v", { x <- runif(5) v <- runif(5) expect_equal(-v, simple$log_density_hessian_vector_product(x, v)$Hvp) }) -bernoulli <- load_model("bernoulli") - -test_that("loading another library didn't break prior ones", { - if (.Platform$OS.type == "windows"){ - dll = "./test_collisions.dll" - } else { - dll = "./test_collisions.so" - } - if (file.exists(dll)) { - dyn.load(dll) - expect_equal(bernoulli$name(), "bernoulli_model") - expect_equal(simple$name(), "simple_model") - } -}) - test_that("bernoulli constrain works", { x <- runif(bernoulli$param_unc_num()) - q <- log(x / (1 - x)) + q <- log(x/(1 - x)) expect_equal(x, bernoulli$param_constrain(q)) }) test_that("bernoulli unconstrain works", { x <- runif(bernoulli$param_unc_num()) - q <- log(x / (1 - x)) + q <- log(x/(1 - x)) expect_equal(q, bernoulli$param_unconstrain(x)) - expect_equal(q, bernoulli$param_unconstrain_json(paste("{\"theta\":", as.character(x), "}"))) + expect_equal(q, bernoulli$param_unconstrain_json(paste("{\"theta\":", as.character(x), + "}"))) }) fr_gaussian <- load_model("fr_gaussian") -cov_constrain <- function(v,D){ +cov_constrain <- function(v, D) { L <- matrix(c(0), D, D) - L[upper.tri(L, diag=TRUE)] <- v + L[upper.tri(L, diag = TRUE)] <- v diag(L) <- exp(diag(L)) return(t(L) %*% L) } @@ -107,7 +82,7 @@ test_that("param_constrain works for a nontrivial case", { B_expected <- cov_constrain(a, D) b <- fr_gaussian$param_constrain(a) - B <- array(b, dim=c(D,D)) + B <- array(b, dim = c(D, D)) expect_equal(B, B_expected) }) @@ -125,40 +100,42 @@ test_that("param_unconstrain works for a nontrivial case", { }) test_that("param_constrain handles rng arguments", { - full <- load_model("full", include_data=FALSE) + full <- load_model("full", include_data = FALSE) expect_equal(1, length(full$param_constrain(c(1.2)))) - expect_equal(2, length(full$param_constrain(c(1.2), include_tp=TRUE))) + expect_equal(2, length(full$param_constrain(c(1.2), include_tp = TRUE))) rng <- full$new_rng(123) - expect_equal(3, length(full$param_constrain(c(1.2), include_gq=TRUE, rng=rng))) - expect_equal(4, length(full$param_constrain(c(1.2), include_tp=TRUE, include_gq=TRUE, rng=rng))) + expect_equal(3, length(full$param_constrain(c(1.2), include_gq = TRUE, rng = rng))) + expect_equal(4, length(full$param_constrain(c(1.2), include_tp = TRUE, include_gq = TRUE, + rng = rng))) # check reproducibility - expect_equal(full$param_constrain(c(1.2), include_gq=TRUE, rng=full$new_rng(456)), - full$param_constrain(c(1.2), include_gq=TRUE, rng=full$new_rng(456))) + expect_equal(full$param_constrain(c(1.2), include_gq = TRUE, rng = full$new_rng(456)), + full$param_constrain(c(1.2), include_gq = TRUE, rng = full$new_rng(456))) # require at least one present - expect_error(full$param_constrain(c(1.2), include_gq=TRUE), "rng must be provided") + expect_error(full$param_constrain(c(1.2), include_gq = TRUE), "rng must be provided") }) test_that("constructor propagates errors", { - expect_error(load_model("throw_data",include_data=FALSE), "find this text: datafails") + expect_error(load_model("throw_data", include_data = FALSE), "find this text: datafails") }) test_that("log_density propagates errors", { - m <- load_model("throw_lp",include_data=FALSE) + m <- load_model("throw_lp", include_data = FALSE) expect_error(m$log_density(c(1.2)), "find this text: lpfails") expect_error(m$log_density_gradient(c(1.2)), "find this text: lpfails") expect_error(m$log_density_hessian(c(1.2)), "find this text: lpfails") }) test_that("param_constrain propagates errors", { - m1 <- load_model("throw_tp",include_data=FALSE) - m1$param_constrain(c(1.2)) # no error - expect_error(m1$param_constrain(c(1.2), include_tp=TRUE), "find this text: tpfails") + m1 <- load_model("throw_tp", include_data = FALSE) + m1$param_constrain(c(1.2)) # no error + expect_error(m1$param_constrain(c(1.2), include_tp = TRUE), "find this text: tpfails") - m2 <- load_model("throw_gq",include_data=FALSE) - m2$param_constrain(c(1.2)) # no error - expect_error(m2$param_constrain(c(1.2), include_gq=TRUE, rng=m2$new_rng(1234)), "find this text: gqfails") + m2 <- load_model("throw_gq", include_data = FALSE) + m2$param_constrain(c(1.2)) # no error + expect_error(m2$param_constrain(c(1.2), include_gq = TRUE, rng = m2$new_rng(1234)), + "find this text: gqfails") }) diff --git a/docs/languages/julia.md b/docs/languages/julia.md index f7f85f6d..a3fbbf23 100644 --- a/docs/languages/julia.md +++ b/docs/languages/julia.md @@ -206,7 +206,7 @@ This calculation drops constant terms that do not depend on the parameters if `p This allocates new memory for the output each call. See `log_density_hessian_vector_product!` for a version which allows re-using existing memory. -source
+source
# **`BridgeStan.param_constrain`** — *Function*. @@ -414,7 +414,7 @@ This calculation drops constant terms that do not depend on the parameters if `p The product is stored in the vector `out` and a reference is returned. See `log_density_hessian_vector_product` for a version which allocates fresh memory. -source
+source
# **`BridgeStan.param_constrain!`** — *Function*. @@ -528,10 +528,10 @@ compile_model(stan_file; stanc_args=[], make_args=[]) Run BridgeStan’s Makefile on a `.stan` file, creating the `.so` used by StanModel and return a path to the compiled library. Arguments to `stanc3` can be passed as a vector, for example `["--O1"]` enables level 1 compiler optimizations. Additional arguments to `make` can be passed as a vector, for example `["STAN_THREADS=true"]` enables the model's threading capabilities. If the same flags are defined in `make/local`, the versions passed here will take precedent. -This function assumes that the path to BridgeStan is valid. This can be set with `set_bridgestan_path!()`. +This function checks that the path to BridgeStan is valid and will error if it is not. This can be set with `set_bridgestan_path!()`. -source
+source
# **`BridgeStan.get_bridgestan_path`** — *Function*. @@ -560,8 +560,6 @@ set_bridgestan_path!(path) Set the path BridgeStan. -By default this is set to the value of the environment variable `BRIDGESTAN`. - -source
+source
diff --git a/docs/languages/r.md b/docs/languages/r.md index bfb01c60..d67e36b4 100644 --- a/docs/languages/r.md +++ b/docs/languages/r.md @@ -71,12 +71,16 @@ StanModel$new(lib, data, rng_seed) _Arguments_ - - `lib` A path to a compiled BridgeStan Shared Object file. + - `lib` A path to a compiled BridgeStan Shared Object file or a .stan file (will be compiled). - `data` Either a JSON string literal, a path to a data file in JSON format ending in ".json", or the empty string. - `rng_seed` Seed for the RNG used in constructing the model. + - `stan_args` A list of arguments to pass to stanc3 if the model is not already compiled. + + - `make_args` A list of additional arguments to pass to Make if the model is not already compiled. + _Returns_ A new StanModel. @@ -431,3 +435,41 @@ _Returns_ List containing entries `val` (the log density) and `Hvp` (the hessian-vector product). + +### Compilation utilities + +**Function** `compile_model()`: + +Run BridgeStan's Makefile on a `.stan` file, creating the `.so` used by +the StanModel class. This function checks that the path to BridgeStan +is valid and will error if not. This can be set with `set_bridgestan_path()`. + +_Usage_ +```R +compile_model(stan_file, stanc_args = NULL, make_args = NULL) +``` + +_Arguments_ + + - `stan_file` A path to a Stan model file. + + - `stanc_arg` A vector of arguments to pass to stanc3. For example, + `c("--O1")` will enable compiler optimization level 1. + + - `make_args` A vector of additional arguments to pass to Make. For example, + `c("STAN_THREADS=True")` will enable threading for the + compiled model. If the same flags are defined in + `make/local`, the versions passed here will take precedent. + +_Returns_ + + Path to the compiled `.so` file. + +**Function** `set_bridgestan_path()`: + +Set the path to BridgeStan. This should point to the top-level folder of the repository. + +_Usage_ +```R +set_bridgestan_path(path) +``` diff --git a/julia/src/compile.jl b/julia/src/compile.jl index c103a4c8..8a18bb85 100644 --- a/julia/src/compile.jl +++ b/julia/src/compile.jl @@ -55,7 +55,7 @@ Additional arguments to `make` can be passed as a vector, for example `["STAN_TH enables the model's threading capabilities. If the same flags are defined in `make/local`, the versions passed here will take precedent. -This function assumes that the path to BridgeStan is valid. +This function checks that the path to BridgeStan is valid and will error if it is not. This can be set with `set_bridgestan_path!()`. """ function compile_model( diff --git a/python/bridgestan/compile.py b/python/bridgestan/compile.py index 6890b67f..958d1d94 100644 --- a/python/bridgestan/compile.py +++ b/python/bridgestan/compile.py @@ -29,8 +29,6 @@ def verify_bridgestan_path(path: str) -> None: IS_WINDOWS = platform.system() == "Windows" WINDOWS_PATH_SET = False -PYTHON_FOLDER = Path(__file__).parent.parent - MAKE = os.getenv( "MAKE", "make" if not IS_WINDOWS else "mingw32-make", @@ -95,8 +93,8 @@ def compile_model( Run BridgeStan's Makefile on a ``.stan`` file, creating the ``.so`` used by the StanModel class. - This function assumes that the path to BridgeStan is valid. - This can be set with :func:`set_bridgestan_path`. + This function checks that the path to BridgeStan is valid and will + error if not. This can be set with :func:`set_bridgestan_path`. :param stan_file: A path to a Stan model file. :param stanc_args: A list of arguments to pass to stanc3.