Warning: These instructions are primarily intended for myself. The aim was to provide a repeatable, simple approach to create and use an R package that has a compiled stan program embedded in it. I have not checked, but they almost certainty not compliant with CRAN requirements nor even necessarily with best practice. It was just something to get me up and running. The reason I created them is out of frustration at the recent revisions to the build infrastructure, which I find to be unnecessarily complicated and failure-prone.

Setup

This section contains the setup and the various utility functions used throughout.

Libraries used:

library(data.table)
library(rstan)

Scenario

You want to create an rpackage that embeds a stan model that will be compiled at installation time.

Typically, you might refer to

Good luck with that. If you are going to follow the above packages then I would aim, as best as possible, to avoid using roxygen2 (that is most likely a contentious perspective).

R packages that contain compiled stan programs can take a while to build (due to the compilation process). The approach I use is to create a minimal package that contains the stan models then make that a dependency for other packages where I need to use that particular set of models.

For example, say I am developing a package to bundle some rct simulation code and this relies one or more stan models. I would use the following naming convention:

  • simplr an r package containing the main body of code making up the rct simulator
  • simplr.stanc a minimal r package that embeds a compiled stan model and provides an API to that model

I will start with simplr.stanc.

r pkg plus stan

Note - these instructions were developed on Ubuntu 20.04 and all my compilation infrastructure for compiling (and so on) is up and running. The following file structure makes up an r package containing a stan model

$ tree simplr.stanc/
simplr.stanc/
├── configure
├── configure.win
├── DESCRIPTION
├── inst
│   ├── include
│   │   └── stan_meta_header.hpp
│   └── stan
│       └── simple.stan
├── man
│   ├── simplr.stanc-package.Rd
│   └── stanc_random.Rd
├── NAMESPACE
└── R
    ├── model_api.R
    └── stanmodels.R

5 directories, 10 files

create the above directory structure. The following sections contain templates for each file and additional commentary (that will be incrementally revised, where necessary).

configure

You need two configure scripts (chmod to 755):

configure

#! /bin/sh

"${R_HOME}/bin/Rscript" -e "rstantools::rstan_config()"

configure.win

#! /bin/sh
"${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" -e "rstantools::rstan_config()"

DESCRIPTION

Package: simplr.stanc
Title: Stan models for coadministr trial simulator
Version: 1.0
Authors@R: 
    person(given = "Mark",
           family = "Mark",
           role = c("aut", "cre"),
           email = "mark@example.com")
Description: Interface for obtaining pre-compiled stan models as used in
    the coadministr trial simulator package.
License: MIT
Encoding: UTF-8
LazyData: true
Biarch: true
Depends: 
    R (>= 3.4.0)
Imports: 
    methods,
    Rcpp (>= 0.12.0),
    RcppParallel (>= 5.0.1),
    rstan (>= 2.18.1),
    rstantools (>= 2.1.1.9000)
LinkingTo: 
    BH (>= 1.66.0),
    Rcpp (>= 0.12.0),
    RcppEigen (>= 0.3.3.3.0),
    RcppParallel (>= 5.0.1),
    rstan (>= 2.18.1),
    StanHeaders (>= 2.18.0)
SystemRequirements: GNU make

Directory (inst)

stan_meta_header.hpp

// Insert all #include<foo.hpp> statements here

simple.stan

data {
}
parameters {

}
model {
}
generated quantities{
  real x = uniform_rng(0, 1);
}

Directory (man)

See Writing R documentation.

simplr.stanc-package.Rd

The name of this help file needs to be in the format -package.Rd.

\name{simplr.stanc-package}
\alias{simplr.stanc}
\title{
\packageTitle{simplr.stanc}
}
\description{
Precompiled Stan models for use in the simplr package.
}
\references{
Stan Development Team (2020). RStan: the R interface to Stan. R package version 2.21.2. https://mc-stan.org
}

stanc_simple.Rd

In practice you need documentation for each of the API functions for accessing the stan models.

\name{stanc_simple}
\alias{stanc_simple}
\title{
Retrieve stan compiled model
}
\description{
Retrieves a stan compiled model object.
}
\usage{
stanc_simple()
}

\value{
rstan object
}
\author{
Mark
}

NAMESPACE

See Package namespaces

exportPattern("ˆ[[:alpha:]]+")
export(stanc_simple)
import(Rcpp)
import(methods)
importFrom(rstan,sampling)
useDynLib(simplr.stanc, .registration = TRUE)

Directory (R)

Provides an API to the stan models. Here I just return the stan model object, but you might want to be more sophisticated.

model_api.R

stanc_random <- function(){
  stanmodels$simple
}

stanmodels.R

This is the content of an autogenerated script from rstantools when you use the official approach. For how to create it from scratch, see Step by step guide.

# Generated by rstantools.  Do not edit by hand.

# names of stan models, multiple models would be: c("model1", "model2", ...)
stanmodels <- c("simple")

# load each stan module
Rcpp::loadModule("stan_fit4simple_mod", what = TRUE)

# instantiate each stanmodel object
stanmodels <- sapply(stanmodels, function(model_name) {
  # create C++ code for stan model
  stan_file <- if(dir.exists("stan")) "stan" else file.path("inst", "stan")
  stan_file <- file.path(stan_file, paste0(model_name, ".stan"))
  stanfit <- rstan::stanc_builder(stan_file,
                                  allow_undefined = TRUE,
                                  obfuscate_model_name = FALSE)
  stanfit$model_cpp <- list(model_cppname = stanfit$model_name,
                            model_cppcode = stanfit$cppcode)
  # create stanmodel object
  methods::new(Class = "stanmodel",
               model_name = stanfit$model_name,
               model_code = stanfit$model_code,
               model_cpp = stanfit$model_cpp,
               mk_cppmodule = function(x) get(paste0("model_", model_name)))
})

Install

For the simplr.stanc package this will also include a compile step. First change directory to the parent of simplr.stanc then

$ R CMD INSTALL --preclean simplr.stanc

if all goes well, you should see

installing to /home/.../simplr.stanc/libs
** R
** inst
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** checking absolute paths in shared objects and dynamic libraries
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (simplr.stanc)

Launch an R terminal

library(simplr.stanc)
stanc_simple()

should produce

S4 class stanmodel 'simple' coded as follows:
data {
}
parameters {

}
model {
}
generated quantities{
  real x = uniform_rng(0, 1);
} 

r pkg

This creates a package that makes use of the models in the simplr.stanc package. The following file structure makes up the r package

$ tree simplr/
simplr/
├── DESCRIPTION
├── man
│   ├── simplr-package.Rd
│   ├── simplr_work1.Rd
│   └── simplr_work2.Rd
├── NAMESPACE
└── R
    └── simplr_.R

create the above directory structure. The following sections contain templates for each file and additional commentary, where necessary.

DESCRIPTION

I have included a couple of additional dependencies. For data.table see Importing data.table.

Package: simplr
Type: Package
Title: Simulator
Version: 1.0
Date: 2021-04-07
Authors@R: 
    person(given = "Mark",
           family = "Mark",
           role = c("aut", "cre"),
           email = "mark@example.com")
Description: Tests out using the stan models in simplr.stanc package
License: MIT
Encoding: UTF-8
LazyData: true
Biarch: true
Depends: 
    R (>= 3.4.0)
Imports: 
    simplr.stanc,
    rstan,
    data.table

Directory (man)

simplr-package.Rd

The name of this help file needs to be in the format -package.Rd.

\name{simplr-package}
\alias{simplr}
\title{
\packageTitle{simplr}
}
\description{
Precompiled Stan models for use in the coadministr package.
}
\references{
None
}

simplr_work1.Rd

In practice you need documentation for each of the API functions for accessing the stan models.

\name{simplr_work1}
\alias{simplr_work1}
\title{
Calling simplr.stanc package API
}
\description{
Nothing much really.
}
\usage{
simplr_work1()
}

\value{
rstan object
}
\author{
Mark
}

Directory (R)

simple.R

simplr_work1 <- function(n = 10){
  
  out <- rstan::sampling(coadministr::random_number_model(),
                         iter = n, chain = 1, refresh = 0,
                         algorithm = "Fixed_param")
  
  post <- rstan::extract(out, pars = "x")
  data.table(id = 1:n, x = post$x)
  
}

simplr_work2 <- function(){
  out <- rstan::sampling(coadministr::random_number_model(),
                         iter = 5, chain = 1, refresh = 0,
                         algorithm = "Fixed_param")
  out
}

NAMESPACE

exportPattern("ˆ[[:alpha:]]+")
import(simplr.stanc)
import(rstan)
import(data.table)
export(simplr_work1)
export(simplr_work2)

Install

First change directory to the parent of simplr then

$ R CMD INSTALL --preclean simplr

if all goes well, you should see

* installing to library ‘/home/.../4.0’
* installing *source* package ‘simplr’ ...
** using staged installation
** R
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (simplr)

Launch an R terminal and run

> library(simplr)
> simplr_work1()
    id         x
 1:  1 0.7103067
 2:  2 0.7992876
 3:  3 0.5360210
 4:  4 0.2189318
 5:  5 0.9516744
 6:  6 0.7103067
 7:  7 0.7992876
 8:  8 0.5360210
 9:  9 0.2189318
10: 10 0.9516744
> simplr_work2()
Inference for Stan model: minimal.
1 chains, each with iter=5; warmup=2; thin=1; 
post-warmup draws per chain=3, total post-warmup draws=3.

     mean se_mean   sd 2.5%  25% 50%  75% 97.5% n_eff Rhat
x    0.53    0.24 0.29 0.24 0.41 0.6 0.69  0.77     1  Inf
lp__ 0.00     NaN 0.00 0.00 0.00 0.0 0.00  0.00   NaN  NaN

* installing to library ‘/home/mjon6454/R/x86_64-pc-linux-gnu-library/4.0’
* installing *source* package ‘simplr’ ...
** using staged installation
** R
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (simplr)

Samples were drawn using (diag_e) at Thu Apr  8 10:51:51 2021.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at 
convergence, Rhat=1).