Skip to content

Commit 86440f6

Browse files
committed
add a manually-triggered rstan build workflow
The 'downstream' job stands in for rstan in the normal CI run: it compiles a translation unit using the two TBB APIs StanHeaders needs, in seconds rather than the best part of an hour. This runs the real thing, on demand, for when that's worth waiting for -- before a release, or after touching the Windows TBB setup. workflow_dispatch inputs choose the R version (4.2 by default, the one whose Rtools has no oneTBB), the runner, and whether to additionally compile and fit a tiny Stan model. The last exercises the TBB runtime at run time, not just the headers and link line, so it's opt-in.
1 parent 2b7d99f commit 86440f6

2 files changed

Lines changed: 125 additions & 0 deletions

File tree

.github/scripts/rstan-fit-check.R

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Compile and fit a trivial Stan model. Building rstan proves the headers and
2+
# link line are right; actually running a model additionally exercises the TBB
3+
# runtime that RcppParallel loaded -- including the reduce_sum / task isolation
4+
# path that the old Rtools42 TBB could not support.
5+
6+
library(rstan)
7+
8+
code <- "
9+
data {
10+
int<lower=0> N;
11+
vector[N] y;
12+
}
13+
parameters {
14+
real mu;
15+
}
16+
model {
17+
y ~ normal(mu, 1);
18+
}
19+
"
20+
21+
set.seed(42)
22+
data <- list(N = 20L, y = rnorm(20, mean = 3))
23+
24+
fit <- stan(
25+
model_code = code,
26+
data = data,
27+
chains = 1L,
28+
iter = 200L,
29+
refresh = 0L
30+
)
31+
32+
estimate <- mean(rstan::extract(fit, "mu")[["mu"]])
33+
writeLines(sprintf("posterior mean of mu: %.3f (data mean %.3f)", estimate, mean(data$y)))
34+
35+
# a wildly wrong answer would mean the model ran but the sampler is broken;
36+
# with 20 observations and unit scale this is a very loose bound
37+
if (!is.finite(estimate) || abs(estimate - mean(data$y)) > 1)
38+
stop("posterior mean is implausible; the fit did not work correctly")
39+
40+
writeLines("** rstan fit check passed")

.github/workflows/rstan.yaml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Build rstan from source against this branch's RcppParallel.
2+
#
3+
# The 'downstream' job in R-CMD-check.yaml is a stand-in for this: it compiles a
4+
# small translation unit using the two TBB APIs StanHeaders needs. That catches
5+
# the breakage this workflow exists to rule out, in seconds rather than the best
6+
# part of an hour -- so this is deliberately manual, for when the real thing is
7+
# worth waiting for (before a release, or after touching the Windows TBB setup).
8+
on:
9+
workflow_dispatch:
10+
inputs:
11+
r-version:
12+
description: "R version (4.2 uses Rtools42, which has no oneTBB)"
13+
required: true
14+
default: "4.2"
15+
os:
16+
description: "Runner to build on"
17+
required: true
18+
default: "windows-latest"
19+
type: choice
20+
options:
21+
- windows-latest
22+
- macOS-latest
23+
- ubuntu-latest
24+
fit-model:
25+
description: "Also compile and fit a tiny Stan model (slow)"
26+
required: false
27+
default: false
28+
type: boolean
29+
30+
name: rstan
31+
32+
jobs:
33+
rstan:
34+
runs-on: ${{ inputs.os }}
35+
36+
name: rstan (${{ inputs.os }}, R ${{ inputs.r-version }})
37+
38+
# building rstan from source is slow, and slower still on Windows
39+
timeout-minutes: 120
40+
41+
env:
42+
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
43+
VERBOSE: 1
44+
# rstan's own guidance for building it from source
45+
MAKEFLAGS: -j2
46+
47+
defaults:
48+
run:
49+
shell: bash
50+
51+
steps:
52+
- uses: actions/checkout@v3
53+
54+
- uses: r-lib/actions/setup-r@v2
55+
with:
56+
r-version: ${{ inputs.r-version }}
57+
use-public-rspm: true
58+
59+
- name: install RcppParallel from this branch
60+
run: R CMD INSTALL --preclean .
61+
62+
- name: report how RcppParallel resolved TBB
63+
run: |
64+
Rscript -e 'writeLines(c(
65+
paste("TBB_ENABLED", RcppParallel:::TBB_ENABLED),
66+
paste("TBB_LIB ", RcppParallel:::TBB_LIB),
67+
paste("CxxFlags ", RcppParallel:::tbbCxxFlags()),
68+
paste("LdFlags ", RcppParallel:::tbbLdFlags())
69+
))'
70+
71+
# binaries are fine for everything except rstan and StanHeaders, which are
72+
# the packages that actually consume RcppParallel's flags
73+
- name: install rstan's dependencies
74+
run: Rscript -e 'install.packages(c("Rcpp","RcppEigen","BH","inline","loo","pkgbuild","QuickJSR","withr","V8","gridExtra","ggplot2"))'
75+
76+
- name: install StanHeaders and rstan from source
77+
run: Rscript -e 'install.packages(c("StanHeaders","rstan"), type = "source", INSTALL_opts = "--no-multiarch")'
78+
79+
- name: load rstan
80+
run: |
81+
Rscript -e 'library(rstan); print(rstan::stan_version())'
82+
83+
- name: compile and fit a tiny model
84+
if: ${{ inputs.fit-model }}
85+
run: Rscript .github/scripts/rstan-fit-check.R

0 commit comments

Comments
 (0)