diff --git a/.github/workflows/R-CMD-check-wsl.yaml b/.github/workflows/R-CMD-check-wsl.yaml index 803ae6e5..d7fa1bc8 100644 --- a/.github/workflows/R-CMD-check-wsl.yaml +++ b/.github/workflows/R-CMD-check-wsl.yaml @@ -37,10 +37,10 @@ jobs: - uses: actions/checkout@v4 - - uses: r-lib/actions/setup-r@v2.8.7 - - uses: r-lib/actions/setup-pandoc@v2.8.7 + - uses: r-lib/actions/setup-r@v2.9.0 + - uses: r-lib/actions/setup-pandoc@v2.9.0 - - uses: r-lib/actions/setup-r-dependencies@v2.8.7 + - uses: r-lib/actions/setup-r-dependencies@v2.9.0 with: extra-packages: any::rcmdcheck, local::. @@ -71,7 +71,7 @@ jobs: sessioninfo::session_info(pkgs, include_base = TRUE) shell: Rscript {0} - - uses: r-lib/actions/check-r-package@v2.8.7 + - uses: r-lib/actions/check-r-package@v2.9.0 env: _R_CHECK_CRAN_INCOMING_: false diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index d99131b3..c43b91e6 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -25,7 +25,7 @@ jobs: config: - {os: macOS-latest, r: 'devel', rtools: ''} - {os: macOS-latest, r: 'release', rtools: ''} - #- {os: windows-latest, r: 'devel', rtools: '44'} + - {os: windows-latest, r: 'devel', rtools: '44'} - {os: windows-latest, r: 'release', rtools: '44'} - {os: windows-latest, r: 'oldrel', rtools: '43'} - {os: ubuntu-latest, r: 'devel', rtools: ''} @@ -49,14 +49,14 @@ jobs: - uses: actions/checkout@v4 - - uses: r-lib/actions/setup-r@v2.8.7 + - uses: r-lib/actions/setup-r@v2.9.0 with: r-version: ${{ matrix.config.r }} rtools-version: ${{ matrix.config.rtools }} - - uses: r-lib/actions/setup-pandoc@v2.8.7 + - uses: r-lib/actions/setup-pandoc@v2.9.0 - - uses: r-lib/actions/setup-r-dependencies@v2.8.7 + - uses: r-lib/actions/setup-r-dependencies@v2.9.0 with: extra-packages: any::rcmdcheck, local::. @@ -73,7 +73,7 @@ jobs: sessioninfo::session_info(pkgs, include_base = TRUE) shell: Rscript {0} - - uses: r-lib/actions/check-r-package@v2.8.7 + - uses: r-lib/actions/check-r-package@v2.9.0 env: _R_CHECK_CRAN_INCOMING_: false diff --git a/.github/workflows/Test-coverage.yaml b/.github/workflows/Test-coverage.yaml index 7681de61..385af644 100644 --- a/.github/workflows/Test-coverage.yaml +++ b/.github/workflows/Test-coverage.yaml @@ -42,10 +42,10 @@ jobs: if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/master'" - uses: actions/checkout@v4 - - uses: r-lib/actions/setup-r@v2.8.7 - - uses: r-lib/actions/setup-pandoc@v2.8.7 + - uses: r-lib/actions/setup-r@v2.9.0 + - uses: r-lib/actions/setup-pandoc@v2.9.0 - - uses: r-lib/actions/setup-r-dependencies@v2.8.7 + - uses: r-lib/actions/setup-r-dependencies@v2.9.0 with: extra-packages: any::rcmdcheck, local::., any::covr, any::gridExtra diff --git a/.github/workflows/cmdstan-tarball-check.yaml b/.github/workflows/cmdstan-tarball-check.yaml index c79fae4f..d621e43e 100644 --- a/.github/workflows/cmdstan-tarball-check.yaml +++ b/.github/workflows/cmdstan-tarball-check.yaml @@ -40,12 +40,12 @@ jobs: sudo apt-get install -y libcurl4-openssl-dev || true sudo apt-get install -y openmpi-bin openmpi-common libopenmpi-dev || true - - uses: r-lib/actions/setup-r@v2.8.7 + - uses: r-lib/actions/setup-r@v2.9.0 with: r-version: ${{ matrix.config.r }} rtools-version: ${{ matrix.config.rtools }} - - uses: r-lib/actions/setup-pandoc@v2.8.7 + - uses: r-lib/actions/setup-pandoc@v2.9.0 - name: Query dependencies run: | diff --git a/R/csv.R b/R/csv.R index 16fe4807..5adbdd66 100644 --- a/R/csv.R +++ b/R/csv.R @@ -840,6 +840,14 @@ read_csv_metadata <- function(csv_file) { csv_file_info$fitted_params <- wsl_safe_path(csv_file_info$fitted_params, revert = TRUE) } + csv_file_info <- lapply(csv_file_info, function(item) { + if (is.character(item) && length(item) == 1) { + if (item %in% c("true", "false")) { + item <- as.integer(as.logical(item)) + } + } + item + }) csv_file_info } diff --git a/R/utils.R b/R/utils.R index 8b0bc01a..e07e1d57 100644 --- a/R/utils.R +++ b/R/utils.R @@ -727,7 +727,7 @@ get_cmdstan_flags <- function(flag_name) { paste(flags, collapse = " ") } -rcpp_source_stan <- function(code, env, verbose = FALSE) { +rcpp_source_stan <- function(code, env, verbose = FALSE, ...) { cxxflags <- get_cmdstan_flags("CXXFLAGS") cmdstanr_includes <- system.file("include", package = "cmdstanr", mustWork = TRUE) cmdstanr_includes <- paste0(" -I\"", cmdstanr_includes,"\"") @@ -746,7 +746,7 @@ rcpp_source_stan <- function(code, env, verbose = FALSE) { PKG_CXXFLAGS = paste0(cxxflags, cmdstanr_includes, collapse = " "), PKG_LIBS = libs ), - Rcpp::sourceCpp(code = code, env = env, verbose = verbose) + Rcpp::sourceCpp(code = code, env = env, verbose = verbose, ...) ) ) invisible(NULL) @@ -887,8 +887,12 @@ prep_fun_cpp <- function(fun_start, fun_end, model_lines) { } fun_body <- gsub("// [[stan::function]]", "// [[Rcpp::export]]\n", fun_body, fixed = TRUE) fun_body <- gsub("std::ostream\\*\\s*pstream__\\s*=\\s*nullptr", "", fun_body) - fun_body <- gsub("boost::ecuyer1988&\\s*base_rng__", "SEXP base_rng_ptr", fun_body) - fun_body <- gsub("base_rng__,", "*(Rcpp::XPtr(base_rng_ptr).get()),", fun_body, fixed = TRUE) + if (cmdstan_version() < "2.35.0") { + fun_body <- gsub("boost::ecuyer1988&\\s*base_rng__", "SEXP base_rng_ptr", fun_body) + } else { + fun_body <- gsub("stan::rng_t&\\s*base_rng__", "SEXP base_rng_ptr", fun_body) + } + fun_body <- gsub("base_rng__,", "*(Rcpp::XPtr(base_rng_ptr).get()),", fun_body, fixed = TRUE) fun_body <- gsub("pstream__", "&Rcpp::Rcout", fun_body, fixed = TRUE) fun_body <- paste(fun_body, collapse = "\n") gsub(pattern = ",\\s*)", replacement = ")", fun_body) @@ -921,6 +925,7 @@ compile_functions <- function(env, verbose = FALSE, global = FALSE) { env$hpp_code[1:(funs[1] - 1)], "#include ", "#include ", + "#include ", stan_funs), collapse = "\n") if (global) { @@ -935,7 +940,7 @@ compile_functions <- function(env, verbose = FALSE, global = FALSE) { if (length(rng_funs) > 0) { rng_cpp <- system.file("include", "base_rng.cpp", package = "cmdstanr", mustWork = TRUE) rcpp_source_stan(paste0(readLines(rng_cpp), collapse="\n"), env, verbose) - env$rng_ptr <- env$base_rng(seed=0) + env$rng_ptr <- env$base_rng(seed=1) } # For all RNG functions, pass the initialised Boost RNG by default diff --git a/inst/include/base_rng.cpp b/inst/include/base_rng.cpp index 7b38ba16..42d9e772 100644 --- a/inst/include/base_rng.cpp +++ b/inst/include/base_rng.cpp @@ -1,8 +1,8 @@ #include -#include +#include // [[Rcpp::export]] -SEXP base_rng(boost::uint32_t seed = 0) { - Rcpp::XPtr rng_ptr(new boost::ecuyer1988(seed)); +SEXP base_rng(boost::uint32_t seed = 1) { + Rcpp::XPtr rng_ptr(new stan::rng_t(seed)); return rng_ptr; } diff --git a/inst/include/model_methods.cpp b/inst/include/model_methods.cpp index 70eeabf5..e4931462 100644 --- a/inst/include/model_methods.cpp +++ b/inst/include/model_methods.cpp @@ -3,13 +3,13 @@ #include #include #include -#include #ifdef CMDSTAN_JSON #include #else #include #include #endif +#include std::shared_ptr var_context(std::string file_path) { if (file_path == "") { @@ -36,7 +36,7 @@ Rcpp::List model_ptr(std::string data_path, boost::uint32_t seed) { Rcpp::XPtr ptr( &new_model(*var_context(data_path), seed, &Rcpp::Rcout) ); - Rcpp::XPtr base_rng(new boost::ecuyer1988(seed)); + Rcpp::XPtr base_rng(new stan::rng_t(seed)); return Rcpp::List::create( Rcpp::Named("model_ptr") = ptr, Rcpp::Named("base_rng") = base_rng @@ -144,7 +144,7 @@ std::vector constrain_variables(SEXP ext_model_ptr, SEXP base_rng, bool return_trans_pars, bool return_gen_quants) { Rcpp::XPtr ptr(ext_model_ptr); - Rcpp::XPtr rng(base_rng); + Rcpp::XPtr rng(base_rng); std::vector params_i; std::vector vars; diff --git a/inst/include/rcpp_tuple_interop.hpp b/inst/include/rcpp_tuple_interop.hpp index 52a614ab..2d59f949 100644 --- a/inst/include/rcpp_tuple_interop.hpp +++ b/inst/include/rcpp_tuple_interop.hpp @@ -1,3 +1,6 @@ +#ifndef CMDSTANR_RCPP_TUPLE_INTEROP_HPP +#define CMDSTANR_RCPP_TUPLE_INTEROP_HPP + #include #include @@ -38,3 +41,5 @@ namespace Rcpp { }, x); } } + +#endif diff --git a/inst/include/stan_rng.hpp b/inst/include/stan_rng.hpp new file mode 100644 index 00000000..834464f7 --- /dev/null +++ b/inst/include/stan_rng.hpp @@ -0,0 +1,17 @@ +#ifndef CMDSTANR_STAN_RNG_HPP +#define CMDSTANR_STAN_RNG_HPP + +#include +#include + +// A consistent rng_t is defined from 2.35 onwards +// so add a fallback for older versions +#if STAN_MAJOR == 2 && STAN_MINOR >= 35 +#include +#else +namespace stan { + using rng_t = boost::ecuyer1988; +} +#endif + +#endif diff --git a/tests/testthat/test-model-compile.R b/tests/testthat/test-model-compile.R index 23595245..2be8390f 100644 --- a/tests/testthat/test-model-compile.R +++ b/tests/testthat/test-model-compile.R @@ -3,7 +3,7 @@ context("model-compile") set_cmdstan_path() stan_program <- cmdstan_example_file() mod <- cmdstan_model(stan_file = stan_program, compile = FALSE) - +cmdstan_make_local(cpp_options = list("PRECOMPILED_HEADERS"="false")) test_that("object initialized correctly", { expect_equal(mod$stan_file(), stan_program) @@ -130,6 +130,9 @@ test_that("name in STANCFLAGS is set correctly", { test_that("switching threads on and off works without rebuild", { main_path_o <- file.path(cmdstan_path(), "src", "cmdstan", "main.o") main_path_threads_o <- file.path(cmdstan_path(), "src", "cmdstan", "main_threads.o") + backup <- cmdstan_make_local() + no_threads <- grep("STAN_THREADS", backup, invert = TRUE, value = TRUE) + cmdstan_make_local(cpp_options = list(no_threads), append = FALSE) if (file.exists(main_path_threads_o)) { file.remove(main_path_threads_o) } @@ -155,6 +158,7 @@ test_that("switching threads on and off works without rebuild", { expect_equal(before_mtime, after_mtime) expect_warning(mod$compile(threads = TRUE, dry_run = TRUE), "deprecated") + cmdstan_make_local(cpp_options = backup, append = FALSE) }) test_that("multiple cpp_options work", { @@ -483,19 +487,19 @@ test_that("include_paths_stanc3_args() works", { test_that("cpp_options work with settings in make/local", { backup <- cmdstan_make_local() + no_threads <- grep("STAN_THREADS", backup, invert = TRUE, value = TRUE) + cmdstan_make_local(cpp_options = list(no_threads), append = FALSE) if (length(mod$exe_file()) > 0 && file.exists(mod$exe_file())) { file.remove(mod$exe_file()) } - cmdstan_make_local(cpp_options = list(), append = TRUE) rebuild_cmdstan() mod <- cmdstan_model(stan_file = stan_program) expect_null(mod$cpp_options()$STAN_THREADS) file.remove(mod$exe_file()) - cmdstan_make_local(cpp_options = backup, append = FALSE) cmdstan_make_local(cpp_options = list(stan_threads = TRUE), append = TRUE) file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") diff --git a/tests/testthat/test-model-expose-functions.R b/tests/testthat/test-model-expose-functions.R index fb82fe26..1ee9b78e 100644 --- a/tests/testthat/test-model-expose-functions.R +++ b/tests/testthat/test-model-expose-functions.R @@ -351,12 +351,13 @@ test_that("rng functions can be exposed", { expect_equal( fit$functions$wrap_normal_rng(5,10), - -4.5298764235381225873 + # Stan RNG changed in 2.35 + ifelse(cmdstan_version() < "2.35.0",-4.529876423, 0.02974925) ) expect_equal( fit$functions$wrap_normal_rng(5,10), - 8.1295902610102039887 + ifelse(cmdstan_version() < "2.35.0", 8.12959026, 10.3881349) ) }) diff --git a/tests/testthat/test-profiling.R b/tests/testthat/test-profiling.R index 15832df6..638f1fdc 100644 --- a/tests/testthat/test-profiling.R +++ b/tests/testthat/test-profiling.R @@ -12,7 +12,9 @@ test_that("profiling works if profiling data is present", { profiles <- fit$profiles() expect_equal(length(profiles), 4) expect_equal(dim(profiles[[1]]), c(3,9)) - expect_equal(profiles[[1]][,"name"], c("glm", "priors", "udf")) + for (name in profiles[[1]][,"name"]) { + expect_true(name %in% c("udf", "priors", "glm")) + } file.remove(fit$profile_files()) expect_error( @@ -24,7 +26,9 @@ test_that("profiling works if profiling data is present", { profiles_no_csv <- fit$profiles() expect_equal(length(profiles_no_csv), 4) expect_equal(dim(profiles_no_csv[[1]]), c(3,9)) - expect_equal(profiles_no_csv[[1]][,"name"], c("glm", "priors", "udf")) + for (name in profiles_no_csv[[1]][,"name"]) { + expect_true(name %in% c("udf", "priors", "glm")) + } }) test_that("profiling errors if no profiling files are present", {