stan-dev / stan-dev/rstan

`expose_stan_functions` won't work with external C++ functions

Open
#1,107 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
R
Stars
1.1k
Forks
266
Avg merge
2h 56m
Merged PRs (30d)
1

Description

Summary:

rstan::expose_stan_functions() yields an error during compilation when the stan function uses an external C++ function in its definition.

Description:
  • The vignette vignette("external", "rstan") describes how to leverage user-defined C++ functions in a stan model.
  • In particular, I would like to define a helper function in the stan functions block that uses my own C++ function (in its definition).
  • Include directives are passed as arguments as described in the above mentioned vignette.
  • The rstan::stan_model() compiles the model successfully and I can sample from it. However,
  • rstan::expose_stan_functions() yields an error during compilation.
Reproducible Steps:

Here follows a reprex. The api() function is just a wrapper around expose_stan_functions(). Issues are commented via ##Issue: :

library(rstan)
library(Rcpp)

rm(list = ls())


test <- '
functions {
  int my_sum(int a, int b) {
    return a + b;
  }
}
model {
  ;
}
'

test_model <- stan_model(model_code = test, model_name = "test", verbose = TRUE)

api <- function(stanmodel, includes = NULL, verbose = TRUE, ...) {
  e <- new.env()
  rstan::expose_stan_functions(stanmodel, includes = includes, env = e, verbose = verbose, ...)
  return(as.list(e))
}

test_api <- api(test_model)
test_api$my_sum(1, 2)


## Now, let's try to outsource the my_sum function to my_sum.hpp

## first check the expected signature
cat(test_model@model_cpp$model_cppcode)
## int my_sum(const int&a, const int&b, std::ostream* pstream__)


my_sum.hpp <- '
int my_sum(const int&a, const int&b, std::ostream* pstream__) {
  return a + b;
}
'

## write to file in wd
sink(file = "my_sum.hpp")
cat(my_sum.hpp)
sink()

mc <- '
functions {
  int my_sum(int a, int b);
}
model {
  ;
}
'

includes <- paste0('\n#include "', 
                   file.path(getwd(), 'my_sum.hpp'), '"\n')

external_model <- stan_model(model_code = mc, model_name = "external",
                             allow_undefined = TRUE,
                             includes = includes,
                             verbose = TRUE)

external_api_1 <- api(external_model, includes = includes)
## Issue: The my_sum function is not exported...

## my_sum is only declared but not defined...

mc <- '
functions {
  int my_sum(int a, int b);
  int my_sum_wrapper(int a, int b) {
    return my_sum(a, b);
  }
}
model {
  ;
}
'
external_model <- stan_model(model_code = mc, model_name = "external",
                             allow_undefined = TRUE,
                             includes = includes,
                             verbose = TRUE)

external_api_2 <- api(external_model, includes = includes, verbose = TRUE)
## Issue: won't compile

## what if we don't call my_sum in the functions block??
mc <- '
functions {
  int my_sum(int a, int b);
  void my_api() {
    print("hello from my_api");
  }
}
model {
  ;
}
'
external_model <- stan_model(model_code = mc, model_name = "external",
                             allow_undefined = TRUE,
                             includes = includes,
                             verbose = TRUE)

external_api_3 <- api(external_model, includes = includes, verbose = TRUE)
# Issue: will compile but (obviously) as before, not export my_sum

external_api$my_api()
Current Output:

The api() call (wrapping expose_stan_functions()) which should generate external_api_2 yields the following output before throwing the compilation error:

using C++ compiler: ‘Apple clang version 12.0.0 (clang-1200.0.32.29)’
using C++17
using SDK: ‘’
clang++ -std=gnu++17 -I"/usr/local/Cellar/r/4.3.1/lib/R/include" -DNDEBUG   -I"/usr/local/lib/R/4.3/site-library/Rcpp/include/"  -I"/usr/local/lib/R/4.3/site-library/RcppEigen/include/"  -I"/usr/local/lib/R/4.3/site-library/RcppEigen/include/unsupported"  -I"/usr/local/lib/R/4.3/site-library/BH/include" -I"/usr/local/lib/R/4.3/site-library/StanHeaders/include/src/"  -I"/usr/local/lib/R/4.3/site-library/StanHeaders/include/"  -I"/usr/local/lib/R/4.3/site-library/RcppParallel/include/"  -I"/usr/local/lib/R/4.3/site-library/rstan/include" -DEIGEN_NO_DEBUG  -DBOOST_DISABLE_ASSERTS  -DBOOST_PENDING_INTEGER_LOG2_HPP  -DSTAN_THREADS  -DUSE_STANC3 -DSTRICT_R_HEADERS  -DBOOST_PHOENIX_NO_VARIADIC_EXPRESSION  -D_HAS_AUTO_PTR_ETC=0  -include '/usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp'  -D_REENTRANT -DRCPP_PARALLEL_USE_TBB=1   -I"/usr/local/lib/R/4.3/site-library/Rcpp/include" -I"/usr/local/lib/R/4.3/site-library/StanHeaders/include" -I"/usr/local/lib/R/4.3/site-library/rstan/include" -I"/usr/local/lib/R/4.3/site-library/RcppEigen/include" -I"/usr/local/lib/R/4.3/site-library/BH/include" -I"/private/var/folders/w5/j_vympl57nq2plg57t3yvy1r0000gn/T/RtmpDPYWid/sourceCpp-x86_64-apple-darwin19.6.0-1.0.11" -I/usr/local/opt/gettext/include -I/usr/local/opt/readline/include -I/usr/local/opt/xz/include -I/usr/local/include    -fPIC  -g -O2  -c filea1c8ba937c.cpp -o filea1c8ba937c.o
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:22:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Dense:1:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Core:540:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:22:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Dense:2:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/LU:47:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:22:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Dense:3:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Cholesky:12:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Jacobi:29:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:22:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Dense:3:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Cholesky:43:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:22:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Dense:4:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/QR:15:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Householder:27:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:22:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Dense:4:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/QR:48:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:22:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Dense:5:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/SVD:48:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:22:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Dense:6:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Geometry:58:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:22:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Dense:7:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Eigenvalues:58:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:23:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Sparse:26:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/SparseCore:66:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:23:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Sparse:27:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/OrderingMethods:71:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:23:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Sparse:29:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/SparseCholesky:43:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:23:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Sparse:32:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/SparseQR:34:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from <built-in>:1:
In file included from /usr/local/lib/R/4.3/site-library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:23:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/Sparse:33:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/IterativeLinearSolvers:46:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from filea1c8ba937c.cpp:10:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/RcppEigen.h:25:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/RcppEigenForward.h:32:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/unsupported/Eigen/KroneckerProduct:34:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/unsupported/Eigen/../../Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from filea1c8ba937c.cpp:10:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/RcppEigen.h:25:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/RcppEigenForward.h:36:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/unsupported/Eigen/Polynomials:135:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/unsupported/Eigen/../../Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
In file included from filea1c8ba937c.cpp:10:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/RcppEigen.h:25:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/RcppEigenForward.h:37:
In file included from /usr/local/lib/R/4.3/site-library/RcppEigen/include/unsupported/Eigen/SparseExtra:51:
/usr/local/lib/R/4.3/site-library/RcppEigen/include/unsupported/Eigen/../../Eigen/src/Core/util/ReenableStupidWarnings.h:14:30: warning: pragma diagnostic pop could not pop, no matching push [-Wunknown-pragmas]
    #pragma clang diagnostic pop
                             ^
17 warnings generated.
clang++ -std=gnu++17 -dynamiclib -Wl,-headerpad_max_install_names -undefined dynamic_lookup -single_module -multiply_defined suppress -L/usr/local/Cellar/r/4.3.1/lib/R/lib -L/usr/local/opt/gettext/lib -L/usr/local/opt/readline/lib -L/usr/local/opt/xz/lib -L/usr/local/lib -o sourceCpp_37.so filea1c8ba937c.o /usr/local/lib/R/4.3/site-library/rstan/lib//libStanServices.a -L/usr/local/lib/R/4.3/site-library/StanHeaders/lib/ -lStanHeaders -L/usr/local/lib/R/4.3/site-library/RcppParallel/lib/ -ltbb -L/usr/local/Cellar/r/4.3.1/lib/R/lib -lR -lintl -Wl,-framework -Wl,CoreFoundation
Error in rstan::expose_stan_functions(stanmodel, includes = includes, :
Compilation failed!
Expected Output:
  • I would expect the expose_stan_functions() to export both the my_sum and my_sum_wrapper functions...
  • From its function documentation ?expose_stan_functions (in particular the description of the includes argument), I would expect that the function was intended to be used together with external C++ source...

Here is the excerpt of the documentation on includes:

"If not NULL (the default), then a character vector of length one (possibly containing one or more "\n") of the form '#include "/full/path/to/my_header.hpp"', which will be inserted into the C++ code in the model's namespace and can be used to provide definitions"

RStan Version:

2.32.3

R Version:

R version 4.3.1 (2023-06-16)

Operating System:

OS X 10.15.7

Your help would be much appreciated! Thank you!

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at rstan::expose_stan_functions() and compare its generated compilation path with rstan::stan_model(), using the external vignette and the supplied reprex. Reproduce external_api_2 with the declared and included my_sum function, then trace why the wrapper compilation fails. Done means external C++ functions used by Stan helper functions compile and are exported through expose_stan_functions().

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, r
Domain
api, build-system, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.