diff --git a/.github/workflows/rhub.yaml b/.github/workflows/rhub.yaml index 74ec7b0..dc1ec2c 100644 --- a/.github/workflows/rhub.yaml +++ b/.github/workflows/rhub.yaml @@ -7,20 +7,24 @@ # It is unlikely that you need to modify this file manually. name: R-hub -run-name: "${{ github.event.inputs.id }}: ${{ github.event.inputs.name || format('Manually run by {0}', github.triggering_actor) }}" +run-name: >- + ${{ github.event.inputs.id || format('Manual run by @{0}', github.triggering_actor) }}: + ${{ github.event.inputs.name || github.event.inputs.config }} on: workflow_dispatch: inputs: config: - description: 'A comma separated list of R-hub platforms to use.' + description: >- + Comma-separated list of R-hub platforms to use. + Full list: https://r-hub.github.io/containers/ type: string default: 'linux,windows,macos' name: - description: 'Run name. You can leave this empty now.' + description: 'Run name. You can leave this empty.' type: string id: - description: 'Unique ID. You can leave this empty now.' + description: 'Unique ID. You can leave this empty.' type: string jobs: diff --git a/DESCRIPTION b/DESCRIPTION index 7543fa2..1b1552d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: xplainfi Title: Feature Importance Methods for Global Explanations -Version: 1.1.0.9000 +Version: 1.2.0 Authors@R: person("Lukas", "Burk", , "cran@lukasburk.de", role = c("aut", "cre", "cph"), comment = c(ORCID = "0000-0001-7528-3795")) diff --git a/NEWS.md b/NEWS.md index 4585bab..0375a99 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# xplainfi 1.1.0.9000 (development version) +# xplainfi 1.2.0 ## Behavior changes @@ -24,6 +24,7 @@ ## Bug fixes +- `$importance()` with a test-based `ci_method` (`"lei"`, `"cpi"`) now warns and returns `NA` inference fields for a feature whose hypothesis test errors, instead of aborting the whole call; this guards against an R-devel (2026-05) change to `stats::wilcox.test` that errors on many-ties or many-zeros inputs. - `relation = "ratio"` importances now return `NA` (with a warning) instead of `Inf`/`NaN` for features whose baseline score is `0`. - `PerturbationImportance` no longer calls `$obs_loss()` without `measure`, which errored when `measures` was not the task default. - `ConditionalARFSampler$sample()` now errors when `parallel = TRUE` but no parallel backend is registered, e.g. after deserializing a sampler in a new session. @@ -41,7 +42,7 @@ - Use of a pre-trained `mlr3` learner is now supported in `PerturbationImportance` (`PFI`, `CFI`, `RFI`) and `SAGE` methods. - Requires the provided `Resampling` to be instantiated and consist of a single iteration, e.g. there must be only 1 test set. - - The `rsmp_all_test(task)` utility can be used to construct a single-iteration `Resampling` object from a given `Task` where all observations are alligned to the test set and the train set is empty. We will likely refine the API around this in the future. + - The `rsmp_all_test(task)` utility can be used to construct a single-iteration `Resampling` object from a given `Task` where all observations are aligned to the test set and the train set is empty. We will likely refine the API around this in the future. - Internally, a `ResampleResult` will be constructed from the given `learner`, `task`, and `resampling` arguments, which is then consistent with the previous default of performing `resample()` to get trained learners for each resampling iteration. ## Inference @@ -95,7 +96,7 @@ The major version bump is largely to mark the occasion that the package is now c - Clean up various documentation issues and other metadata. - Adjusted the `min_permutations` default in `SAGE` methods to 10 rather than 3, since the previous value was found to lead to spurious early stopping. - Fix `sim_dgp_ewald` lading to erroneous variances when compared to their settings. -- Reduce runtime of tests (mostly by using less ARF and mor Gaussian sampling) +- Reduce runtime of tests (mostly by using less ARF and more Gaussian sampling) - Remove `KnockoffSequentialSampler` as the `seqknockoff` package is not available on CRAN or R-universe. `KnockoffSampler` with the corresponding `knockoff_fun = seqknockoff::knockoffs_seq` still works. # xplainfi 0.2.1 diff --git a/R/SAGE-marginal.R b/R/SAGE-marginal.R index 2ed89e5..7f34e33 100644 --- a/R/SAGE-marginal.R +++ b/R/SAGE-marginal.R @@ -10,10 +10,10 @@ #' task = tgen("friedman1")$generate(n = 100) #' sage = MarginalSAGE$new( #' task = task, -#' learner = lrn("regr.ranger", num.trees = 50), +#' learner = lrn("regr.ranger", num.trees = 20), #' measure = msr("regr.mse"), -#' n_permutations = 3L, -#' n_samples = 20 +#' n_permutations = 2L, +#' n_samples = 10 #' ) #' sage$compute() #' @export diff --git a/man/MarginalSAGE.Rd b/man/MarginalSAGE.Rd index 42e8201..1641f42 100644 --- a/man/MarginalSAGE.Rd +++ b/man/MarginalSAGE.Rd @@ -13,10 +13,10 @@ library(mlr3) task = tgen("friedman1")$generate(n = 100) sage = MarginalSAGE$new( task = task, - learner = lrn("regr.ranger", num.trees = 50), + learner = lrn("regr.ranger", num.trees = 20), measure = msr("regr.mse"), - n_permutations = 3L, - n_samples = 20 + n_permutations = 2L, + n_samples = 10 ) sage$compute() \dontshow{\}) # examplesIf} diff --git a/tests/testthat.R b/tests/testthat.R index f8f9da3..0c96970 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -6,6 +6,10 @@ # * https://r-pkgs.org/testing-design.html#sec-tests-files-overview # * https://testthat.r-lib.org/articles/special-files.html +# Cap threads before any BLAS/OpenMP init: CRAN's Debian machine links a multithreaded (OpenMP) BLAS whose +# spin-pool inflates the CPU/elapsed ratio. Must be set here, before library() and any matrix op, to take effect. +Sys.setenv("OMP_THREAD_LIMIT" = 2) + library(testthat) library(xplainfi) diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index 872f26e..62926c1 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -3,6 +3,13 @@ # Load mlr3 to avoid mlr3:: namespace clutter in tests library(mlr3) +# CRAN policy: at most 2 cores in tests. data.table (OpenMP) and ranger (own thread pool via arf) need capping; +# ranger uses std::thread, so OMP_THREAD_LIMIT in testthat.R does not cover it. Restored in teardown. +old_dt_threads = data.table::getDTthreads() +old_ranger_threads = getOption("ranger.num.threads") +data.table::setDTthreads(2) +options(ranger.num.threads = 2) + # Get current log threshold logger = lgr::get_logger("mlr3") old_threshold = logger$threshold @@ -18,6 +25,8 @@ withr::defer( { lgr::get_logger("mlr3")$set_threshold(old_threshold) xplain_opt(verbose = old_opts$verbose, progress = old_opts$progress) + data.table::setDTthreads(old_dt_threads) + options(ranger.num.threads = old_ranger_threads) }, teardown_env() ) diff --git a/vignettes/articles/perturbation-importance.Rmd b/vignettes/articles/perturbation-importance.Rmd index 30a8f21..4cd9bcd 100644 --- a/vignettes/articles/perturbation-importance.Rmd +++ b/vignettes/articles/perturbation-importance.Rmd @@ -314,7 +314,7 @@ digraph confounding_dag { The red arrows show the confounding paths: the hidden confounder creates spurious correlations between x1, proxy, and y. The **blue arrows** show true direct causal effects. Note that `independent` is truly independent (no confounding) while `proxy` provides a noisy measurement of the confounder. -In the observable confounder scenaro (used later), the confounder H would be included as a feature in the dataset, allowing direct conditioning rather than relying on the noisy proxy. +In the observable confounder scenario (used later), the confounder H would be included as a feature in the dataset, allowing direct conditioning rather than relying on the noisy proxy. ```{r viz-confounding} #| echo: false