--- title: "Parallel and threaded fitting (including HPC)" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Parallel and threaded fitting (including HPC)} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) ``` Fitting a `hestia` model is the expensive part of most analyses. `hestia` fits through [`flexstanr`](https://accidda.github.io/flexstanr/), which owns the parallelism: how cores are detected, how they are split between chains and threads, and how that behaves under an HPC scheduler are all `flexstanr` behavior and are documented once, there: - [Parallel and threaded fitting](https://accidda.github.io/flexstanr/articles/parallel-hpc.html) -- the core-allocation rules, capping with `max_cores`, and the SLURM recipes (single node, and one chain per array task). This article covers only what is specific to `hestia`. ## Why threading helps a `hestia` fit A fit can use cores in two ways at once: across chains, which are independent and so scale near-linearly, and within a chain. The within-chain half is the model-specific part. `hestia`'s likelihood sums over households, which are conditionally independent given the parameters, so that sum splits across threads with Stan's `reduce_sum`. Both `hmm` and `hmm_cov` are compiled for threading, so a `hestia` fit can always use within-chain threads when there are spare cores to give them. `flexstanr` fills chain-parallelism first and gives any leftover cores to threads, so threading only switches on when you have more cores than chains. ## Turning it on Threading is off unless you ask for it, and you ask for it through the same `stan_options()` you already pass to `run_model()`: ```{r, eval=FALSE} # detect the cores this process is allowed to use and split them automatically fit <- run_model( inf_model = inf_process, obs_model = obs_process, data = sir, init_probs = c(1 - 2 * 1e-10, 1e-10, 1e-10), stan_opts = stan_options(chains = 4, iter = 1000, threading = TRUE) ) # cap the pool, for example to keep some cores free fit <- run_model( ..., stan_opts = stan_options(chains = 4, threading = TRUE, max_cores = 6) ) # single-threaded (the default): manage backend parallelism yourself fit <- run_model(..., stan_opts = stan_options(threading = FALSE)) ``` The same `stan_opts` works on a laptop and inside a scheduled job: `flexstanr` sizes the fit from the allocation the process actually has, not from the machine's total core count. See the [flexstanr article](https://accidda.github.io/flexstanr/articles/parallel-hpc.html) for the allocation rules and job-script templates, substituting `run_model()` for the fit call shown there.