diff --git a/R/example.R b/R/example.R index bb9b371d..f322185f 100644 --- a/R/example.R +++ b/R/example.R @@ -124,7 +124,7 @@ print_example_program <- #' stan_program <- " #' data { #' int N; -#' int y[N]; +#' array[N] int y; #' } #' parameters { #' real theta; diff --git a/R/fit.R b/R/fit.R index efd05fdd..96e438b3 100644 --- a/R/fit.R +++ b/R/fit.R @@ -1158,7 +1158,7 @@ CmdStanFit$set("public", name = "return_codes", value = return_codes) #' mcmc_program <- write_stan_file( #' 'data { #' int N; -#' int y[N]; +#' array[N] int y; #' } #' parameters { #' real theta; @@ -1169,7 +1169,7 @@ CmdStanFit$set("public", name = "return_codes", value = return_codes) #' } #' } #' generated quantities { -#' int y_rep[N]; +#' array[N] int y_rep; #' profile("gq") { #' y_rep = bernoulli_rng(rep_vector(theta, N)); #' } diff --git a/R/model.R b/R/model.R index 35d333ba..14871b91 100644 --- a/R/model.R +++ b/R/model.R @@ -806,7 +806,7 @@ CmdStanModel$set("public", name = "variables", value = variables) #' file <- write_stan_file(" #' data { #' int N; -#' int y[N]; +#' array[N] int y; #' } #' parameters { #' // should have but omitting to demonstrate pedantic mode @@ -932,7 +932,7 @@ CmdStanModel$set("public", name = "check_syntax", value = check_syntax) #' file <- write_stan_file(" #' data { #' int N; -#' int y[N]; +#' array[N] int y; #' } #' parameters { #' real lambda; @@ -1659,7 +1659,7 @@ CmdStanModel$set("public", name = "variational", value = variational) #' mcmc_program <- write_stan_file( #' "data { #' int N; -#' int y[N]; +#' array[N] int y; #' } #' parameters { #' real theta; @@ -1678,13 +1678,13 @@ CmdStanModel$set("public", name = "variational", value = variational) #' gq_program <- write_stan_file( #' "data { #' int N; -#' int y[N]; +#' array[N] int y; #' } #' parameters { #' real theta; #' } #' generated quantities { -#' int y_rep[N] = bernoulli_rng(rep_vector(theta, N)); +#' array[N] int y_rep = bernoulli_rng(rep_vector(theta, N)); #' }" #' ) #' diff --git a/R/utils.R b/R/utils.R index 2f7d2b60..639daf0b 100644 --- a/R/utils.R +++ b/R/utils.R @@ -816,7 +816,20 @@ get_standalone_hpp <- function(stan_file, stancflags) { get_function_name <- function(fun_start, fun_end, model_lines) { fun_string <- paste(model_lines[(fun_start+1):fun_end], collapse = " ") - fun_name <- gsub("auto ", "", fun_string, fixed = TRUE) + types <- c( + "auto", + "int", + "double", + "Eigen::Matrix<(.*)>", + "std::vector<(.*)>" + ) + pattern <- paste0( + # Only match if the type occurs at start of string + "^(\\s*)?(", + paste0(types, collapse="|"), + # Only match if type followed by a function name and opening bracket + ")\\s*(?=\\w*\\()") + fun_name <- gsub(pattern, "", fun_string, perl = TRUE) sub("\\(.*", "", fun_name, perl = TRUE) } @@ -864,7 +877,9 @@ get_plain_rtn <- function(fun_start, fun_end, model_lines) { # that instantiates an RNG prep_fun_cpp <- function(fun_start, fun_end, model_lines) { fun_body <- paste(model_lines[fun_start:fun_end], collapse = " ") - fun_body <- gsub("auto", get_plain_rtn(fun_start, fun_end, model_lines), fun_body) + if (cmdstan_version() < "2.33") { + fun_body <- gsub("auto", get_plain_rtn(fun_start, fun_end, model_lines), fun_body) + } 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) diff --git a/_pkgdown.yml b/_pkgdown.yml index ca5375be..e9133e95 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -72,7 +72,6 @@ articles: - cmdstanr-internals - posterior - r-markdown - - deprecations - profiling - articles-online-only/opencl diff --git a/man/CmdStanGQ.Rd b/man/CmdStanGQ.Rd index 6aa7a3ce..86082056 100644 --- a/man/CmdStanGQ.Rd +++ b/man/CmdStanGQ.Rd @@ -52,7 +52,7 @@ all of which have their own (linked) documentation pages. mcmc_program <- write_stan_file( "data { int N; - int y[N]; + array[N] int y; } parameters { real theta; @@ -71,13 +71,13 @@ fit_mcmc <- mod_mcmc$sample(data = data, seed = 123, refresh = 0) gq_program <- write_stan_file( "data { int N; - int y[N]; + array[N] int y; } parameters { real theta; } generated quantities { - int y_rep[N] = bernoulli_rng(rep_vector(theta, N)); + array[N] int y_rep = bernoulli_rng(rep_vector(theta, N)); }" ) diff --git a/man/fit-method-profiles.Rd b/man/fit-method-profiles.Rd index 3aff8f11..96311179 100644 --- a/man/fit-method-profiles.Rd +++ b/man/fit-method-profiles.Rd @@ -26,7 +26,7 @@ requires adding profiling statements to the Stan program. mcmc_program <- write_stan_file( 'data { int N; - int y[N]; + array[N] int y; } parameters { real theta; @@ -37,7 +37,7 @@ mcmc_program <- write_stan_file( } } generated quantities { - int y_rep[N]; + array[N] int y_rep; profile("gq") { y_rep = bernoulli_rng(rep_vector(theta, N)); } diff --git a/man/model-method-check_syntax.Rd b/man/model-method-check_syntax.Rd index 9193a684..8f9623bf 100644 --- a/man/model-method-check_syntax.Rd +++ b/man/model-method-check_syntax.Rd @@ -45,7 +45,7 @@ parsing succeeds. If invalid syntax in found an error is thrown. file <- write_stan_file(" data { int N; - int y[N]; + array[N] int y; } parameters { // should have but omitting to demonstrate pedantic mode diff --git a/man/model-method-format.Rd b/man/model-method-format.Rd index 1d7ed11b..57944b58 100644 --- a/man/model-method-format.Rd +++ b/man/model-method-format.Rd @@ -49,7 +49,7 @@ model directly back to the file or prints it for inspection. file <- write_stan_file(" data { int N; - int y[N]; + array[N] int y; } parameters { real lambda; diff --git a/man/model-method-generate-quantities.Rd b/man/model-method-generate-quantities.Rd index 5a80bb73..ebe5d1f6 100644 --- a/man/model-method-generate-quantities.Rd +++ b/man/model-method-generate-quantities.Rd @@ -119,7 +119,7 @@ based on previously fitted parameters. mcmc_program <- write_stan_file( "data { int N; - int y[N]; + array[N] int y; } parameters { real theta; @@ -138,13 +138,13 @@ fit_mcmc <- mod_mcmc$sample(data = data, seed = 123, refresh = 0) gq_program <- write_stan_file( "data { int N; - int y[N]; + array[N] int y; } parameters { real theta; } generated quantities { - int y_rep[N] = bernoulli_rng(rep_vector(theta, N)); + array[N] int y_rep = bernoulli_rng(rep_vector(theta, N)); }" ) diff --git a/man/write_stan_file.Rd b/man/write_stan_file.Rd index cdb5cbc7..d172cf70 100644 --- a/man/write_stan_file.Rd +++ b/man/write_stan_file.Rd @@ -51,7 +51,7 @@ should ensure thread-safety in the rare cases when it is needed. stan_program <- " data { int N; - int y[N]; + array[N] int y; } parameters { real theta; diff --git a/tests/testthat/test-data.R b/tests/testthat/test-data.R index bae7b49e..01fa7291 100644 --- a/tests/testthat/test-data.R +++ b/tests/testthat/test-data.R @@ -339,7 +339,7 @@ test_that("process_data() corrrectly casts integers and floating point numbers", stan_file <- write_stan_file(" data { - int k[3,3]; + array[3,3] int k; } ") mod <- cmdstan_model(stan_file, compile = FALSE) diff --git a/tests/testthat/test-example.R b/tests/testthat/test-example.R index 842a3075..8d14d5d0 100644 --- a/tests/testthat/test-example.R +++ b/tests/testthat/test-example.R @@ -23,7 +23,7 @@ test_that("cmdstanr_example works", { stan_program <- " data { int N; - int y[N]; + array[N] int y; } parameters { real theta; diff --git a/tests/testthat/test-fit-mle.R b/tests/testthat/test-fit-mle.R index 40fddc2b..96ef2b95 100644 --- a/tests/testthat/test-fit-mle.R +++ b/tests/testthat/test-fit-mle.R @@ -53,7 +53,7 @@ test_that("time is reported after optimization", { test_that("no error when checking estimates after failure", { fit <- cmdstanr_example("schools", method = "optimize", seed = 123) # optim ålways fails for this - expect_silent(fit$summary()) # no error + expect_error(fit$summary(), "Fitting failed. Unable to retrieve the draws.") }) test_that("draws() works for different formats", { diff --git a/tests/testthat/test-fit-shared.R b/tests/testthat/test-fit-shared.R index a7ed1078..193a9664 100644 --- a/tests/testthat/test-fit-shared.R +++ b/tests/testthat/test-fit-shared.R @@ -308,7 +308,7 @@ test_that("sig_figs works with all methods", { m <- "data { int N; int K; - int y[N]; + array[N] int y; matrix[N, K] X; } parameters { diff --git a/tests/testthat/test-model-compile.R b/tests/testthat/test-model-compile.R index 3134bbc0..90d9579c 100644 --- a/tests/testthat/test-model-compile.R +++ b/tests/testthat/test-model-compile.R @@ -278,7 +278,7 @@ test_that("compile() works with pedantic=TRUE", { } ") expect_message( - mod_pedantic_warn <- cmdstan_model(stan_file, pedantic = TRUE), + mod_pedantic_warn <- cmdstan_model(stan_file, pedantic = TRUE, force_recompile = TRUE), "The parameter x was declared but was not used", fixed = TRUE ) @@ -387,13 +387,10 @@ test_that("check_syntax() works with pedantic=TRUE", { fixed = TRUE ) - expect_output( - expect_message( - mod_pedantic_warn$check_syntax(pedantic = TRUE), - "The parameter x was declared but was not used", - fixed = TRUE - ), - regexp = NA + expect_message( + mod_pedantic_warn$check_syntax(pedantic = TRUE), + "The parameter x was declared but was not used", + fixed = TRUE ) }) @@ -424,14 +421,14 @@ test_that("check_syntax() works with pedantic=TRUE", { " stan_file <- write_stan_file(model_code) mod_dep_warning <- cmdstan_model(stan_file, compile = FALSE) - expect_message( + expect_error( mod_dep_warning$compile(), - "deprecated in the Stan language", + "An error occured during compilation! See the message above for more information.", fixed = TRUE ) - expect_message( + expect_error( mod_dep_warning$check_syntax(), - "deprecated in the Stan language", + "Syntax error found! See the message above for more information.", fixed = TRUE ) }) @@ -690,13 +687,9 @@ test_that("format() works", { stan_file_tmp <- write_stan_file(code) mod_1 <- cmdstan_model(stan_file_tmp, compile = FALSE) - expect_output( - expect_message( - mod_1$format(), - "is deprecated", - fixed = TRUE - ), - "target += normal_log(y, 0, 1);", + expect_error( + mod_1$format(), + "Syntax error found! See the message above for more information.", fixed = TRUE ) @@ -710,13 +703,9 @@ test_that("format() works", { "target += normal_lpdf(y | 0, 1);", fixed = TRUE ) - expect_output( - expect_message( - mod_1$format(canonicalize = list("includes")), - "is deprecated", - fixed = TRUE - ), - "target += normal_log(y, 0, 1);", + expect_error( + mod_1$format(), + "Syntax error found! See the message above for more information.", fixed = TRUE ) diff --git a/tests/testthat/test-model-expose-functions.R b/tests/testthat/test-model-expose-functions.R index e1e99d1b..75db20f5 100644 --- a/tests/testthat/test-model-expose-functions.R +++ b/tests/testthat/test-model-expose-functions.R @@ -192,7 +192,8 @@ test_that("Exposing functions with precompiled model gives meaningful error", { parameters { real x; } model { x ~ std_normal(); } ") - mod1 <- cmdstan_model(stan_file, compile_standalone = TRUE) + mod1 <- cmdstan_model(stan_file, compile_standalone = TRUE, + force_recompile = TRUE) expect_equal(7.5, mod1$functions$a_plus_b(5, 2.5)) mod2 <- cmdstan_model(stan_file) diff --git a/tests/testthat/test-model-sample_mpi.R b/tests/testthat/test-model-sample_mpi.R index 6b348f2e..1ea0f9a6 100644 --- a/tests/testthat/test-model-sample_mpi.R +++ b/tests/testthat/test-model-sample_mpi.R @@ -4,15 +4,15 @@ test_that("sample_mpi() works", { skip_if(!mpi_toolchain_present()) mpi_file <- write_stan_file(" functions { - vector test(vector beta, vector theta, real[] x, int[] y) { + vector test(vector beta, vector theta, array[] real x, array[] int y) { return theta; } } transformed data { vector[4] a; - vector[5] b[4] = {[1,1,1,1,1]', [2,2,2,2,2]', [3,3,3,3,3]', [4,4,4,4,4]'}; - real x[4,4]; - int y[4,4]; + array[4] vector[5] b = {[1,1,1,1,1]', [2,2,2,2,2]', [3,3,3,3,3]', [4,4,4,4,4]'}; + array[4,4] real x; + array[4,4] int y; } parameters { real beta; diff --git a/vignettes/deprecations.Rmd b/vignettes/deprecations.Rmd deleted file mode 100644 index 920aebed..00000000 --- a/vignettes/deprecations.Rmd +++ /dev/null @@ -1,131 +0,0 @@ ---- -title: "Handling deprecated Stan features with the canonicalizer in CmdStanR" -author: "Rok Češnovar and Jonah Gabry" -output: - rmarkdown::html_vignette: - toc: true - toc_depth: 4 -params: - EVAL: !r identical(Sys.getenv("NOT_CRAN"), "true") -vignette: > - %\VignetteIndexEntry{Handling deprecated Stan features with the canonicalizer in CmdStanR} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r child="children/_settings-knitr.Rmd"} -``` - -## Introduction - -This vignette demonstrates how to handle cases where your Stan program contains -deprecated features resulting in deprecation warnings. In most cases, the -Stan-to-C++ compiler can be used to automatically update your code to a -non-deprecated feature that replaces the deprecated one. This vignette showcases -how that automatic conversion can be done using CmdStanR. - -The automatic conversion of deprecated features to non-deprecated features is -done using the so-called "canonicalizer", which is part of the Stan-to-C++ -compiler. We recommend using CmdStan 2.29.2 or later when using the -canonicalizer and this vignette. The minimum CmdStanR version to run the -code in the vignette is 0.5.0. - -```{r library, message=FALSE} -library(cmdstanr) -check_cmdstan_toolchain(fix = TRUE, quiet = TRUE) -``` - -## Deprecation warnings - -The following logistic regression model uses several deprecated language -features, resulting in several warnings during compilation. - -```{r logistic} -stan_file <- write_stan_file(" -data { - int k; - int n; - matrix[n, k] X; - int y[n]; -} -parameters { - vector[k] beta; - real alpha; -} -model { - # priors - target += std_normal_log(beta); - alpha ~ std_normal(); - - y ~ bernoulli_logit(X * beta + alpha); -} -") -mod <- cmdstan_model(stan_file) -``` - - -The first warning is about using the deprecated array syntax - -``` -int y[n]; -``` - -which should be replaced with the new syntax using the `array` keyword: - -``` -array[n] int y; -``` - -The second warning is about using the deprecated commenting symbol `#`, -which should be replaced by `//`. - -The last warning is about the use of the deprecated `_log` suffix for -probability density and mass functions. In this case the `_log` suffix should be -replaced with `_lpdf`. For probability mass functions the suffix `_lpmf` is -used. - -We can go and fix these issues manually or use the canonicalizer as outlined -in the next section. - -## Using the canonicalizer - -The canonicalizer is available through the `canonicalize` argument of the -`$format()` method of the `CmdStanModel` class. The arguments accepts -`TRUE` and `FALSE` values, in which case all or none of the features of the -canonicalizer are used. It can also accept a list of character vectors that -determine which features of the canonicalizer to use. - -The canonincalizer in CmdStan 2.29.2 supports four features: `parentheses`, -`braces`, `includes` and `deprecations`. The `parentheses` and `braces` features -clean up the use of parentheses and braces, while `includes` will replace -`#include` statements with the code from the included files. See the -[canonicalizer section of the Stan User's Guide](https://mc-stan.org/docs/2_29/stan-users-guide/stanc-pretty-printing.html#canonicalizing) -for more details. - -In this vignette we will be using the `deprecations` feature that replaces -deprecated Stan model features with non-deprecated ones if possible. - -```{r canonicalize, message=FALSE} -mod$format(canonicalize = list("deprecations")) -``` - -By default, the format function will print the resulting model code. We -can see that all three issues were resolved. `y` is now defined using the -new array keyword, the comment uses `//` and the `std_normal_log()` is replaced -with `std_normal_lpdf()`. - -You can also use the `$format()` method to write the updated version of the -model directly to the Stan model file. That can be enabled by setting -`overwrite_file = TRUE`. The previous version of the file will automatically -be backed up to a file with the `.stan.bak` suffix. If that is not desired or -you are using a version system and making a backup is redundant, -you can disable it by setting `backup = FALSE`. - -```{r overwrite_file} -mod$format( - canonicalize = list("deprecations"), - overwrite_file = TRUE, - backup = FALSE -) -mod$print() -```