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.