Skip to content

Add classes to all loo warnings - #417

Open
florence-bockting wants to merge 2 commits into
masterfrom
add-loo-warning-classes
Open

florence-bockting wants to merge 2 commits into
masterfrom
add-loo-warning-classes

Conversation

@florence-bockting

Copy link
Copy Markdown
Contributor

Description

This PR adds a class to each loo warning, following up on our corresponding work in the posteriorpackage: stan-dev/posterior#470.

  • Every warning has the class loo_warning and a class for its cause.
  • Diagnostic warnings have the class loo_warning_diagnostic.
  • The warning text is unchanged and, similar to posterior, a list of warning classes and messages is provided in R/loo-package.R
  • Adds rlang to Imports.
  • Deletes the unused throw_psis_r_eff_warning() and throw_loo_r_eff_warning() (this was dead code)

This PR is related to Issue #353 (but does not fully solve it).
The PR was assisted by an LLM.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.71233% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.95%. Comparing base (11415f5) to head (febdcbc).

Files with missing lines Patch % Lines
R/loo_compare.psis_loo_ss_list.R 25.00% 9 Missing ⚠️
R/loo_subsample.R 0.00% 4 Missing ⚠️
R/psis_approximate_posterior.R 0.00% 2 Missing ⚠️
R/psislw.R 80.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #417      +/-   ##
==========================================
+ Coverage   92.65%   92.95%   +0.30%     
==========================================
  Files          31       31              
  Lines        3240     3238       -2     
==========================================
+ Hits         3002     3010       +8     
+ Misses        238      228      -10     

☔ 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.

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

This is how benchmark results would change (along with a 95% confidence interval in relative change) if febdcbc is merged into master:

  • ✔️loo_function: 962ms -> 964ms [-1.2%, +1.67%]
  • ✔️loo_matrix: 1.22s -> 1.22s [-0.98%, +1%]
    Further explanation regarding interpretation and methodology can be found in the documentation.

@florence-bockting
florence-bockting marked this pull request as ready for review November 3, 2026 06:15

@jgabry jgabry left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I made a few suggestions, but pretty small.

Comment thread R/psis.R
if (any(tail_len_bad)) {
if (length(tail_lengths) == 1) {
warning(
.warn_diagnostic(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original warning() calls here used immediate. = TRUE, which we added on purpose in a376936. do_importance_sampling() calls this before the PSIS loop, so we made it so that users see the warning right away instead of waiting for a potentially very long computation.

But rlang::warn() doesn't seem to have an immediate. argument, so now the warning will only show up after the whole call finishes (and same for the else branch below).

I think we should keep the immediate behavior by temporarily setting options(warn = 1). The .warn function would change to something like this:

.warn <- function(..., class = NULL, immediate = FALSE) {
  if (immediate && getOption("warn") < 1) {
    rlang::local_options(warn = 1)
  }
  rlang::warn(paste(c(...), collapse = ""), class = c(class, "loo_warning"))
}

and then we can pass immediate = TRUE here via .warn_diagnostic since it will get passed along via ....

What do you think?

Comment thread R/psis.R
bad <- which(tail_len_bad)
Nbad <- length(bad)
warning(
.warn_diagnostic(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above about immediate

Comment thread R/loo-package.R
#'
#' @section Warning classes:
#'
#' Every warning signaled by \pkg{loo} has the class `loo_warning` and one

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't 100% true yet because there are still three .Deprecated() calls (in psislw(), in compare(), and for the log_q argument of psis_approximate_posterior()). Those warnings have the class deprecatedWarning but not loo_warning. Should we convert them to .warn(..., class = "loo_warning_deprecated") too? Or maybe we don't need to change those since we can remove them in loo v3.0 because psislw and compare are already deprecated?

Comment thread NEWS.md
* Fix `crps()` and `scrps()` ignoring the `permutations` argument for numeric
input by @thanasibakis in #398
* Speed up the Bayesian bootstrap in `pseudobma_weights()` by @VisruthSK in #368
* All warnings now have the class `loo_warning` and a class for each cause;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's probably worth also mentioning that the base warning class changes to rlang_warning. I'm guessing it doesn't affect anyone, but just in case.

Comment thread R/loo-package.R
Comment on lines +68 to +69
#' * `loo_warning_subsample_se`: A subsampling standard error is naive or
#' negative.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class seems to cover two different causes: "Naive diff SE is used" from loo_compare() with subsampling, and "Negative estimate of SE" from loo_subsample(). In the posterior PR each cause got its own class. Should we split this into two classes, e.g. loo_warning_subsample_naive_se and loo_warning_subsample_negative_se? Just a thought, I don't really think it's a big deal either way.

)
})

test_that("warning helpers signal their classes", {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests cover the helpers and the throw_*() functions, but not the places that call .warn() or .warn_diagnostic() directly, e.g. the loo_compare() checks, subsampling,do_psis_i(), and others. Should we add a few tests through user-facing functions? I don't think they all need tests, but a few tests would be a sanity check that this functionality is working properly. For example, maybe loo_compare() with mismatched yhash and one or two others?

Comment thread R/loo_subsample.R
} else {
warning("Negative estimate of SE, more subsampling obs. needed.", call. = FALSE)
.warn_diagnostic("Negative estimate of SE, more subsampling obs. needed.", class = "loo_warning_subsample_se")
x$estimates["elpd_loo", "SE"] <- NaN

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this PR, but I noticed it while reviewing: this line 1095 is in the p_loo branch, so it should be x$estimates["p_loo", "SE"] <- NaN (p_loo not elpd_loo). Could you just fix it here since you're already editing these lines? I could open a separate issue and PR, but it would be super easy to just fix it here.

This branch has not been deployed

No deployments
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.

3 participants