Skip to content

Add a progressr-based progress bar for sampling. - #1138

Merged
jgabry merged 45 commits into
stan-dev:v1.0from
josswright:progressr
Oct 12, 2026
Merged

jgabry merged 45 commits into
stan-dev:v1.0from
josswright:progressr

Conversation

@josswright

@josswright josswright commented Feb 13, 2026 •

Copy link
Copy Markdown

Submission Checklist

  • Run unit tests
  • Declare copyright holder and agree to license (see below)

Summary

As discussed in #18 , this pull request adds a progress bar to CmdStanR's sample() function via the progressr framework.

This can be enabled via two new options to model$sample(...):

  • show_progress_bar - boolean - default FALSE
    Enables the progress bar.
  • suppress_iteration_messages - boolean - default FALSE if show_progress_bar is FALSE, and TRUE if show_progress_bar is TRUE.
    Causes any output lines reporting an iteration (such as Chain 1 Iteration 1 / 2000 (Warmup)) to be suppressed.

For the progress bar to be displayed, a progress bar handler must be created and registered by the end user. (See: https://progressr.futureverse.org/) For ease of use, a register_default_progress_handler() function has been added. Calling register_default_progress_handler() before a model$sample(...) call will spawn a default progress bar via the cli library.

Copyright and Licensing

Please list the copyright holder for the work you are submitting
(this will be you or your assignee, such as a university or company):

Joss Wright, University of Oxford

By submitting this pull request, the copyright holder is agreeing to
license the submitted work under the following licenses:

josswright and others added 10 commits December 21, 2025 22:55
This uses the
[`progressr`](https://progressr.futureverse.org/index.html) framework to
enable a progress bar for sampling operations. By default, this replaces
standard iteration messages, but not other informative messages produced
during sampling.

From a user point of view, this adds two arguments to the `$sample()`
method:
- `show_progress_bar`: Default = FALSE

  If TRUE, registers a progress bar via `progressr` and signals an
  update for every output line that matches "Iteration:". The user is
  responsible for registering those progress updates with an appropriate
  handler.

- `suppress_iteration_messages`: Defaults to the value of
  show_progress_bar, but can also be set directly.

  If TRUE, disables display of output lines matching "Iteration:", while
  still causing the progress bar to update. This keeps all of Stan's
  other informative output, but just removes the superfluous iteration
  messages when using a progress bar.

I've tried to keep all additions to the code as non-intrusive as
possible.

One minor decision I made was to pass the value of `refresh` from the
`$sample()` method through to the `CmdStanProcs` class so that the
progress bar can report and update the number of steps in the bar to the
same 'scale' as the number of iterations. (Calling `$sample()` with
`iter_sampling=8000` and `refresh=100` will result in 80 'ticks' of the
progress bar, each increasing the progress by 100.)

By default, `progressr` doesn't register a handler to display the
progress bar, as this is the responsibility of the user. Multiple
packages can be used to display the progress bar. A default progress bar
can be registered using the [`cli`](https://cli.r-lib.org/) library in
this way:

```r
library(progressr)
library(cli)

handlers( global=TRUE )
handlers("cli")

options( cli.spinner = "moon",
         cli.progress_show_after = 0,
         cli.progress_clear = FALSE )

handlers( handler_cli(
            format = "{cli::pb_spin} Progress: |{cli::pb_bar}| {cli::pb_current}/{cli::pb_total} | {cli::pb_percent} | ETA: {cli::pb_eta}",
            clear = FALSE ))
```

A variety of alternative progress bar handlers are available, including
audible and notification-based handlers, and ones that interact with
RStudio's jobs pane:
<https://progressr.futureverse.org/articles/progressr-11-handlers.html>
In addition to updating the progress bar on stdout lines that match
"Iteration:", the CmdStanProcs object(s) now pass the current stdout
line to the progress bar as a message for _every_ line, even if the
number of completed iterations hasn't increased.
Added a `register_default_progress_handler()` function to `utils.R` that
creates a default progress bar for sampling operations and registers it
as global handler for progressr. Requires `cli` and `progressr`.
Was accidentally passing `refresh` to each informational progress bar
update, rather than the calculated `progress_amount`. This caused each
message line to update the progress bar, even if it didn't match an
iteration message.
Moved code to close the progress bar to after the `check_finished()`
function.
The nature of CmdStan's output makes pulling appropriate refresh and
update values for the progress bar slightly tricky. In the original
verison of the code, this led to the bar occasionally updating past the
point where it was full, or terminating early due to an incorrectly
calculated number of steps.

This commit reworks the calculations of updates and refresh values for
the progress bar, as well as adding some extra conditions to avoid the
bar potentially crashing in odd scenarios.
New arguments for `iter_warmup` and `iter_sampling` were not being
assigned a default value if not specified in sampling statement.
@VisruthSK VisruthSK linked an issue Feb 15, 2026 that may be closed by this pull request
@VisruthSK

Copy link
Copy Markdown
Member

Looks like some tests are failing because the lower bound for iter_sampling should be 0 instead of 1.

@josswright

Copy link
Copy Markdown
Author

Looks like some tests are failing because the lower bound for iter_sampling should be 0 instead of 1.

Great. Thanks for this. (First pull request for me on a project like this, so I was a little unfamiliar with the process and running the unit tests.)

I've fixed the initial errors, and a couple of other warnings. Now running the tests and documentation generation commands locally before I push an update.

josswright and others added 2 commits February 15, 2026 15:46
- `iter_{warmup,sampling}` arguments to MCMCProcs are allowed to be 0.
- Documentation for `register_default_progress_handler` added to
  repository.
- Ensured that calls to `progressr` functions are appropriately
  prefixed.
@josswright
josswright marked this pull request as draft February 15, 2026 18:37
@codecov-commenter

codecov-commenter commented Feb 15, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.52459% with 7 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (v1.0@9f5536f). Learn more about missing BASE report.

Files with missing lines Patch % Lines
R/run.R 92.72% 4 Missing ⚠️
R/model.R 50.00% 3 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             v1.0    #1138   +/-   ##
=======================================
  Coverage        ?   94.04%           
=======================================
  Files           ?       19           
  Lines           ?     7538           
  Branches        ?        0           
=======================================
  Hits            ?     7089           
  Misses          ?      449           
  Partials        ?        0           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

josswright and others added 4 commits February 15, 2026 19:13
Added documentation for `show_progress_bar` and
`suppress_iteration_messages` in `sample()` function.

Changed `require()` calls for `progressr` and `cli` to suggested
`requireNamespace()` alternatives.
@josswright
josswright marked this pull request as ready for review February 16, 2026 17:44
@josswright

Copy link
Copy Markdown
Author

Thanks for the help with the unit tests.

In the spirit of peer review, some notes and thoughts:

  • I'm not entirely happy with CmdStan's default iter_warmup and iter_sampling values being hard-coded into MCMCProcs default call rather than derived somehow. I imagine that the default values are fairly fixed, but I would have liked not to rely on that. Every route I could see to getting them was clunky and brought in unnecessary code.

  • As mentioned above, I toyed with the idea of setting refresh to 1 by default when showing the progress bar with iteration messages suppressed. In the end that seemed to introduce yet another case of a parameter depending on another parameter, as well as a potential source of fragility. I'll note, though, that refresh can safely be set to 1 for a nice, smooth progress bar without any noticeable overhead or slowdown that I saw in testing.

  • The code to derive the number of steps for the progress bar is more complicated than I originally imagined because of how CmdStan reports iterations.

    At one stage, when the number of steps was incorrectly calculated as too low, the progress bar was crashing, which also killed the sampling run. I've now ensured that the progress bar automatically closes when it reaches its finished number of steps, which should avoid crashes and just print a warning in the case of some output oddity causing more update steps to be signalled.

    I've tested as extensively as I can, but I live in fear of this killing someone's multi-day sampling run.

@jgabry

jgabry commented Feb 18, 2026

Copy link
Copy Markdown
Member

Awesome, thank you for the PR! I think I have the flu right now, but I will take a look when I'm feeling better and caught up on work.

@josswright

Copy link
Copy Markdown
Author

Awesome, thank you for the PR! I think I have the flu right now, but I will take a look when I'm feeling better and caught up on work.

No rush on my side! Hope you feel better soon.

@jgabry

jgabry commented Feb 26, 2026 •

Copy link
Copy Markdown
Member

I haven't forgotten about this, just dealing with a backlog of work. In the meantime, I posted on the Stan forum to hopefully get some more people to try it out and let us know how it goes:

https://discourse.mc-stan.org/t/please-help-test-a-new-progress-bar-for-sampling-in-cmdstanr/40946

I've tested as extensively as I can, but I live in fear of this killing someone's multi-day sampling run.

Should we maybe add something to the documentation about this? At least while the progress bar is still new we'll be gathering info about whether any users suffer this fate, so we could warn users that it's an experimental feature and to be cautious using it. Then eventually we can remove the experimental caveat from the doc. What do you think @josswright @VisruthSK?

@jgabry jgabry mentioned this pull request Oct 7, 2026
2 tasks
@jgabry

jgabry commented Oct 7, 2026

Copy link
Copy Markdown
Member

@VisruthSK awesome, looks like this handles everything in my review comments on #1313.

Looking back on some of my old review comments on this PR from before, I think there are a couple of small things we should add to the doc because users will want to know:

  • why it's one bar for all chains (RStudio's terminal only supports single-line bars, so progressr doesn't do per-chain ones)
  • an example of picking a non-default handler, such as progressr::handlers("cli")
  • that refresh = 1 gives a smooth bar for slow models (can go next to the sentence about the overhead for fast ones). In other words, for fast models refresh=1 adds too much overhead, but for slow models it doesn't add much overhead (relative to sampling speed) and it improves the smoothness of the progress bar.

And then one tiny thing I noticed in the "sample can suppress CmdStan iteration messages" test. capture.output() prints the returned fit in the test output. Can you do the assignment inside, i.e. capture.output(fit <- mod$sample(...)), which stops the print?

@josswright

Copy link
Copy Markdown
Author

Just to say that I'm so sorry to have dropped the ball on this after the initial activity. Some rather complicated life things happened, which are still ongoing, and it's not left me much time.

@jgabry

jgabry commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

No worries at all @josswright! I really appreciate all the work you did to get this started and I think we can finish it from here. I'm going to add you as a contributor in the DESCRIPTION file on this branch so you'll be listed whenever this merges. Might be able to get it into the upcoming v1.0 release.

@jgabry
jgabry changed the base branch from master to v1.0 October 9, 2026 18:54
@VisruthSK

Copy link
Copy Markdown
Member

Just wanted to second what Jonah said, thanks for tackling this and solving all the tough parts! Hope things are going okay for you.

@jgabry

jgabry commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

@avehtari coming back to your five comments from February now that this might get into the 1.0 release candidate:

  1. The bar is still only for $sample() and $sample_mpi() in 1.0 and is labeled experimental, so we can extend it to $laplace() and $pathfinder() once we've seen if users like it.
  2. Global option: options(cmdstanr_progress_bar = TRUE) works now
  3. Still works with brms
  4. register_default_progress_handler() is gone. Instead of calling that function, the user calls progressr::handlers(global = TRUE) directly and the docs now say to do it with if (interactive()) in a notebook's setup chunk to avoid a failure while the document renders
  5. Fixed. While the progress bar is still up the per-chain "finished in ..." lines are held back and then printed together once the bar is done, right above the summary.

jgabry added 4 commits October 9, 2026 15:06
finish_progress() cleared the queue of held chain ids after printing
them, so when a held "finished unexpectedly" warning became an error
(options(warn = 2), or a tryCatch around $sample()) the deferred call
printed the same lines again.
RStudio's console pane handles cursor-up sequences since
rstudio/rstudio#17786 (May 2026), and cli flips its ansi_tty capability
on for those versions (r-lib/cli#823), so multi-line bars will work in
current RStudio but not older releases. One bar stays the design.
progressr's intro vignette asks packages to always signal progress and
leave the decision to the user's handlers. CmdStanR keeps an argument
on purpose: progressr scopes reporting to the session or to one call,
not to a package, and the bar also replaces CmdStan's own iteration
lines, which something has to decide to hide. Decided 2026-10-09.
@jgabry jgabry mentioned this pull request Oct 10, 2026
2 tasks done
@jgabry
jgabry merged commit 237351b into stan-dev:v1.0 Oct 12, 2026
@jgabry

jgabry commented Oct 12, 2026

Copy link
Copy Markdown
Member

Merged! Thanks again @josswright. And thanks @VisruthSK for taking over. And thanks @avehtari for your review comments (they are all addressed now).

@jgabry jgabry mentioned this pull request Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Progress bar

5 participants