Run Permutation Searchlight Inference
Source:R/permutation_searchlight.R
run_permutation_searchlight.RdComputes permutation-based p-values for a searchlight MVPA result by running the analysis on permuted labels, building a covariate-adjusted null distribution, and mapping p-values back to all brain voxels.
Usage
run_permutation_searchlight(
model_spec,
observed = NULL,
radius = 8,
method = c("standard", "randomized"),
perm_ctrl = permutation_control(),
metric = NULL,
...
)Arguments
- model_spec
An
mvpa_model,rsa_model, or compatible model specification whose design has apermute_labelsmethod. Forrsa_modelthe permutation relabels items, so the null carries the same item-level dependence as the observed statistic.- observed
Optional pre-computed searchlight result (output of
run_searchlight()). IfNULL, it is computed internally. Can also be a named numeric vector of observed metric values indexed by center ID.- radius
Numeric. Searchlight radius in mm (default 8).
- method
Character. Searchlight method passed to
run_searchlight()whenobservedisNULLor whenperm_strategy = "searchlight".- perm_ctrl
A
permutation_controlobject.- metric
Character vector naming the performance metric(s) to test. If
NULL(default), the first metric is used, except forrsa_modelspecifications, where every model predictor is tested: a permutation pass returns all predictors at once, so scoring them uses the same permutation passes. The null hypothesis is controlled byperm_ctrl$rsa_null, not by how many metrics are selected. A metric that is named explicitly but absent from the results is an error rather than a silent fallback to the first metric.- ...
Additional arguments forwarded to
run_searchlight()(observed pass and"searchlight"permutations). For"iterate"permutations, only arguments that are formal parameters ofmvpa_iterateare forwarded; other keys are ignored for that path to avoid argument-mismatch failures (e.g.,engine = "legacy"is meaningful forrun_searchlightbut not formvpa_iterate).
Value
When one metric is tested, a permutation_result S3 object.
When several are tested (the default for rsa_model), a
permutation_result_set: a named list of permutation_result
objects, one per metric, all scored against the same permutations. Each
permutation_result contains:
- p_map
Spatial map of raw p-values.
- p_adj_map
Spatial map of FDR-adjusted p-values (if requested).
- p_values
Numeric vector of raw p-values (all centers).
- p_adjusted
Numeric vector of adjusted p-values.
- observed
The observed searchlight result.
- diagnostics
A
"null_diagnostics"object (ifdiagnose = TRUE).- perm_ctrl
The
permutation_controlused.- metric
Metric name used for inference.
- perm_strategy
The strategy that was actually used.
- rsa_null, null_hypothesis, null_predictors
For RSA, the null scope, its description, and the predictors covered by that null. These fields are
NULLfor other model classes.
RSA null hypothesis
Raw item permutations break associations with every design predictor,
including nuisance predictors. With regtype = "lm" or
"rfit" and multiple design predictors, they do not preserve the
effects of the other predictors when testing one coefficient. Such runs
are refused by default, even when metric selects a single output.
Use permutation_control(rsa_null = "joint") only to test the joint
no-association null. Each output metric then supplies a statistic for that
same joint null; significance does not establish that the corresponding
predictor has a unique effect. Conditional coefficient inference is not
implemented. This restriction also covers semi-partial and constrained
regression statistics.
Correlation models test marginal associations, without adjusting for the
other RDMs, and single-predictor regressions remain available with the
default rsa_null = "individual". RSA results record
rsa_null, null_hypothesis, and null_predictors.
FDR adjustment is performed separately within each metric's spatial map;
it does not correct across metrics.
Permutation strategy
The perm_strategy field in perm_ctrl determines how each
permutation pass is executed. The two strategies share the same
downstream pipeline (null construction, p-value scoring, FDR
correction) — they differ only in how null metric values are
produced. Neither strategy contains any engine-specific branching.
"iterate"(default)Calls
mvpa_iterateon a subsampled set of centers. This is the generic per-ROI iterator that works with every model type and every dataset class. Thesubsampleparameter inperm_ctrlcontrols how many centers are evaluated per permutation.Null pool size:
n_perm * n_subsampled_centers.Best for: slow classifiers, large brains, limited compute. The subsampling gives a 5–20\(\times\) speedup over a full-brain pass.
"searchlight"Calls
run_searchlighton the full brain for each permutation. Because the call goes through the standardrun_searchlightS3 dispatch, it automatically benefits from any fast engine the model qualifies for (e.g.\ SWIFT, dual-LDA) and from any user-definedrun_searchlight.<class>method. No engine-specific code exists here — it is purely the standardrun_searchlightcall.Since the full brain is computed anyway, all centers contribute to the null distribution (the
subsampleparameter is ignored and a note is logged). This yields a richer null and therefore better-calibrated p-values.Null pool size:
n_perm * all_centers.Best for: models with a fast searchlight engine, or when you want the richest possible null distribution.
Examples
# \donttest{
ds <- gen_sample_dataset(c(5, 5, 5), 20, blocks = 2, nlevels = 2)
cval <- blocked_cross_validation(ds$design$block_var)
mdl <- load_model("sda_notune")
mspec <- mvpa_model(mdl, ds$dataset, ds$design, "classification",
crossval = cval)
# Strategy 1: subsampled iterator (default, universal)
pc1 <- permutation_control(n_perm = 10, subsample = 0.2, seed = 1L)
res1 <- run_permutation_searchlight(mspec, radius = 3, perm_ctrl = pc1)
#> INFO [2026-09-09 12:46:01] Running observed searchlight (radius = 3 mm) ...
#> Warning: run_searchlight preflight reported 0 failure(s) and 1 warning(s).
#> block_count: Only 2 blocks found. Leave-one-block-out CV will have only 2 folds, providing limited evaluation with high variance estimates.
#> INFO [2026-09-09 12:46:01] searchlight engine: general-purpose iterator (no eligible fast path)
#> INFO [2026-09-09 12:46:01] Running standard searchlight with radius = 3
#> INFO [2026-09-09 12:46:01] shard backend: preparing shared memory for dataset (mvpa_image_dataset, mvpa_dataset, list)
#> INFO [2026-09-09 12:46:01] shard backend [volumetric]: shared 20 x 125 matrix (125 masked voxels)
#> INFO [2026-09-09 12:46:01] creating standard searchlight
#> INFO [2026-09-09 12:46:01] running standard searchlight iterator
#> INFO [2026-09-09 12:46:01] Using automatic searchlight batch size 125 for 125 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:05]
#> MVPA Iteration Complete
#> - Total ROIs: 125
#> - Processed: 125
#> - Skipped: 0
#> INFO [2026-09-09 12:46:05] searchlight (standard): 125 ROIs processed (success=125, errors=0)
#> INFO [2026-09-09 12:46:05] Building searchlight iterator ...
#> INFO [2026-09-09 12:46:05] Subsampling searchlight centers ...
#> INFO [2026-09-09 12:46:05] Using 25 / 125 centers for permutation runs.
#> INFO [2026-09-09 12:46:05] Permutation 1 / 10 (strategy: iterate) ...
#> INFO [2026-09-09 12:46:05] Using automatic searchlight batch size 25 for 25 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:06]
#> MVPA Iteration Complete
#> - Total ROIs: 25
#> - Processed: 25
#> - Skipped: 0
#> INFO [2026-09-09 12:46:07] Permutation 2 / 10 (strategy: iterate) ...
#> INFO [2026-09-09 12:46:07] Using automatic searchlight batch size 25 for 25 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:08]
#> MVPA Iteration Complete
#> - Total ROIs: 25
#> - Processed: 25
#> - Skipped: 0
#> INFO [2026-09-09 12:46:08] Permutation 3 / 10 (strategy: iterate) ...
#> INFO [2026-09-09 12:46:08] Using automatic searchlight batch size 25 for 25 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:10]
#> MVPA Iteration Complete
#> - Total ROIs: 25
#> - Processed: 25
#> - Skipped: 0
#> INFO [2026-09-09 12:46:10] Permutation 4 / 10 (strategy: iterate) ...
#> INFO [2026-09-09 12:46:10] Using automatic searchlight batch size 25 for 25 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:12]
#> MVPA Iteration Complete
#> - Total ROIs: 25
#> - Processed: 25
#> - Skipped: 0
#> INFO [2026-09-09 12:46:12] Permutation 5 / 10 (strategy: iterate) ...
#> INFO [2026-09-09 12:46:12] Using automatic searchlight batch size 25 for 25 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:13]
#> MVPA Iteration Complete
#> - Total ROIs: 25
#> - Processed: 25
#> - Skipped: 0
#> INFO [2026-09-09 12:46:14] Permutation 6 / 10 (strategy: iterate) ...
#> INFO [2026-09-09 12:46:14] Using automatic searchlight batch size 25 for 25 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:15]
#> MVPA Iteration Complete
#> - Total ROIs: 25
#> - Processed: 25
#> - Skipped: 0
#> INFO [2026-09-09 12:46:15] Permutation 7 / 10 (strategy: iterate) ...
#> INFO [2026-09-09 12:46:15] Using automatic searchlight batch size 25 for 25 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:17]
#> MVPA Iteration Complete
#> - Total ROIs: 25
#> - Processed: 25
#> - Skipped: 0
#> INFO [2026-09-09 12:46:17] Permutation 8 / 10 (strategy: iterate) ...
#> INFO [2026-09-09 12:46:17] Using automatic searchlight batch size 25 for 25 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:19]
#> MVPA Iteration Complete
#> - Total ROIs: 25
#> - Processed: 25
#> - Skipped: 0
#> INFO [2026-09-09 12:46:19] Permutation 9 / 10 (strategy: iterate) ...
#> INFO [2026-09-09 12:46:19] Using automatic searchlight batch size 25 for 25 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:20]
#> MVPA Iteration Complete
#> - Total ROIs: 25
#> - Processed: 25
#> - Skipped: 0
#> INFO [2026-09-09 12:46:21] Permutation 10 / 10 (strategy: iterate) ...
#> INFO [2026-09-09 12:46:21] Using automatic searchlight batch size 25 for 25 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:22]
#> MVPA Iteration Complete
#> - Total ROIs: 25
#> - Processed: 25
#> - Skipped: 0
#> INFO [2026-09-09 12:46:22] Running null diagnostics for 'Accuracy' ...
#> Null Distribution Diagnostics (n_perm = 10 )
#> --------------------------------------------------
#> nfeatures [OK]
#> rho=-0.063 p=0.3224
#> No significant correlation with nfeatures.
#> --------------------------------------------------
#> INFO [2026-09-09 12:46:22] Building adjusted null distribution for 'Accuracy' (adjusted, 5 bins) ...
#> INFO [2026-09-09 12:46:22] Computing p-values for 125 centers ...
#> INFO [2026-09-09 12:46:22] Building p-value spatial maps ...
#> INFO [2026-09-09 12:46:22] Done 'Accuracy'. 0 centers significant at FDR < 0.05 (fdr).
# Strategy 2: full-brain via run_searchlight (engine-aware)
pc2 <- permutation_control(n_perm = 5, perm_strategy = "searchlight",
seed = 1L)
res2 <- run_permutation_searchlight(mspec, radius = 3, perm_ctrl = pc2)
#> INFO [2026-09-09 12:46:22] Running observed searchlight (radius = 3 mm) ...
#> Warning: run_searchlight preflight reported 0 failure(s) and 1 warning(s).
#> block_count: Only 2 blocks found. Leave-one-block-out CV will have only 2 folds, providing limited evaluation with high variance estimates.
#> INFO [2026-09-09 12:46:22] searchlight engine: general-purpose iterator (no eligible fast path)
#> INFO [2026-09-09 12:46:22] Running standard searchlight with radius = 3
#> INFO [2026-09-09 12:46:22] shard backend: preparing shared memory for dataset (mvpa_image_dataset, mvpa_dataset, list)
#> INFO [2026-09-09 12:46:22] shard backend [volumetric]: shared 20 x 125 matrix (125 masked voxels)
#> INFO [2026-09-09 12:46:22] creating standard searchlight
#> INFO [2026-09-09 12:46:22] running standard searchlight iterator
#> INFO [2026-09-09 12:46:22] Using automatic searchlight batch size 125 for 125 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:26]
#> MVPA Iteration Complete
#> - Total ROIs: 125
#> - Processed: 125
#> - Skipped: 0
#> INFO [2026-09-09 12:46:26] searchlight (standard): 125 ROIs processed (success=125, errors=0)
#> INFO [2026-09-09 12:46:26] Building searchlight iterator ...
#> INFO [2026-09-09 12:46:26] Strategy = 'searchlight': full brain computed per permutation. 'subsample' parameter ignored; all 125 centers contribute to null.
#> INFO [2026-09-09 12:46:26] Permutation 1 / 5 (strategy: searchlight) ...
#> Warning: run_searchlight preflight reported 0 failure(s) and 1 warning(s).
#> block_count: Only 2 blocks found. Leave-one-block-out CV will have only 2 folds, providing limited evaluation with high variance estimates.
#> INFO [2026-09-09 12:46:26] searchlight engine: general-purpose iterator (no eligible fast path)
#> INFO [2026-09-09 12:46:26] Running standard searchlight with radius = 3
#> INFO [2026-09-09 12:46:26] shard backend: preparing shared memory for dataset (mvpa_image_dataset, mvpa_dataset, list)
#> INFO [2026-09-09 12:46:26] shard backend [volumetric]: shared 20 x 125 matrix (125 masked voxels)
#> INFO [2026-09-09 12:46:26] creating standard searchlight
#> INFO [2026-09-09 12:46:26] running standard searchlight iterator
#> INFO [2026-09-09 12:46:26] Using automatic searchlight batch size 125 for 125 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:30]
#> MVPA Iteration Complete
#> - Total ROIs: 125
#> - Processed: 125
#> - Skipped: 0
#> INFO [2026-09-09 12:46:30] searchlight (standard): 125 ROIs processed (success=125, errors=0)
#> INFO [2026-09-09 12:46:30] Permutation 2 / 5 (strategy: searchlight) ...
#> Warning: run_searchlight preflight reported 0 failure(s) and 1 warning(s).
#> block_count: Only 2 blocks found. Leave-one-block-out CV will have only 2 folds, providing limited evaluation with high variance estimates.
#> INFO [2026-09-09 12:46:30] searchlight engine: general-purpose iterator (no eligible fast path)
#> INFO [2026-09-09 12:46:30] Running standard searchlight with radius = 3
#> INFO [2026-09-09 12:46:30] shard backend: preparing shared memory for dataset (mvpa_image_dataset, mvpa_dataset, list)
#> INFO [2026-09-09 12:46:30] shard backend [volumetric]: shared 20 x 125 matrix (125 masked voxels)
#> INFO [2026-09-09 12:46:30] creating standard searchlight
#> INFO [2026-09-09 12:46:30] running standard searchlight iterator
#> INFO [2026-09-09 12:46:30] Using automatic searchlight batch size 125 for 125 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:34]
#> MVPA Iteration Complete
#> - Total ROIs: 125
#> - Processed: 125
#> - Skipped: 0
#> INFO [2026-09-09 12:46:35] searchlight (standard): 125 ROIs processed (success=125, errors=0)
#> INFO [2026-09-09 12:46:35] Permutation 3 / 5 (strategy: searchlight) ...
#> Warning: run_searchlight preflight reported 0 failure(s) and 1 warning(s).
#> block_count: Only 2 blocks found. Leave-one-block-out CV will have only 2 folds, providing limited evaluation with high variance estimates.
#> INFO [2026-09-09 12:46:35] searchlight engine: general-purpose iterator (no eligible fast path)
#> INFO [2026-09-09 12:46:35] Running standard searchlight with radius = 3
#> INFO [2026-09-09 12:46:35] shard backend: preparing shared memory for dataset (mvpa_image_dataset, mvpa_dataset, list)
#> INFO [2026-09-09 12:46:35] shard backend [volumetric]: shared 20 x 125 matrix (125 masked voxels)
#> INFO [2026-09-09 12:46:35] creating standard searchlight
#> INFO [2026-09-09 12:46:35] running standard searchlight iterator
#> INFO [2026-09-09 12:46:35] Using automatic searchlight batch size 125 for 125 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:38]
#> MVPA Iteration Complete
#> - Total ROIs: 125
#> - Processed: 125
#> - Skipped: 0
#> INFO [2026-09-09 12:46:39] searchlight (standard): 125 ROIs processed (success=125, errors=0)
#> INFO [2026-09-09 12:46:39] Permutation 4 / 5 (strategy: searchlight) ...
#> Warning: run_searchlight preflight reported 0 failure(s) and 1 warning(s).
#> block_count: Only 2 blocks found. Leave-one-block-out CV will have only 2 folds, providing limited evaluation with high variance estimates.
#> INFO [2026-09-09 12:46:39] searchlight engine: general-purpose iterator (no eligible fast path)
#> INFO [2026-09-09 12:46:39] Running standard searchlight with radius = 3
#> INFO [2026-09-09 12:46:39] shard backend: preparing shared memory for dataset (mvpa_image_dataset, mvpa_dataset, list)
#> INFO [2026-09-09 12:46:39] shard backend [volumetric]: shared 20 x 125 matrix (125 masked voxels)
#> INFO [2026-09-09 12:46:39] creating standard searchlight
#> INFO [2026-09-09 12:46:39] running standard searchlight iterator
#> INFO [2026-09-09 12:46:39] Using automatic searchlight batch size 125 for 125 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:42]
#> MVPA Iteration Complete
#> - Total ROIs: 125
#> - Processed: 125
#> - Skipped: 0
#> INFO [2026-09-09 12:46:43] searchlight (standard): 125 ROIs processed (success=125, errors=0)
#> INFO [2026-09-09 12:46:43] Permutation 5 / 5 (strategy: searchlight) ...
#> Warning: run_searchlight preflight reported 0 failure(s) and 1 warning(s).
#> block_count: Only 2 blocks found. Leave-one-block-out CV will have only 2 folds, providing limited evaluation with high variance estimates.
#> INFO [2026-09-09 12:46:43] searchlight engine: general-purpose iterator (no eligible fast path)
#> INFO [2026-09-09 12:46:43] Running standard searchlight with radius = 3
#> INFO [2026-09-09 12:46:43] shard backend: preparing shared memory for dataset (mvpa_image_dataset, mvpa_dataset, list)
#> INFO [2026-09-09 12:46:43] shard backend [volumetric]: shared 20 x 125 matrix (125 masked voxels)
#> INFO [2026-09-09 12:46:43] creating standard searchlight
#> INFO [2026-09-09 12:46:43] running standard searchlight iterator
#> INFO [2026-09-09 12:46:43] Using automatic searchlight batch size 125 for 125 centers (memory budget 512.0 MiB).
#> INFO [2026-09-09 12:46:47]
#> MVPA Iteration Complete
#> - Total ROIs: 125
#> - Processed: 125
#> - Skipped: 0
#> INFO [2026-09-09 12:46:47] searchlight (standard): 125 ROIs processed (success=125, errors=0)
#> INFO [2026-09-09 12:46:47] Running null diagnostics for 'Accuracy' ...
#> Null Distribution Diagnostics (n_perm = 5 )
#> --------------------------------------------------
#> nfeatures [OK]
#> rho=-0.084 p=0.0350
#> No significant correlation with nfeatures.
#> --------------------------------------------------
#> INFO [2026-09-09 12:46:47] Building adjusted null distribution for 'Accuracy' (adjusted, 5 bins) ...
#> INFO [2026-09-09 12:46:47] Computing p-values for 125 centers ...
#> INFO [2026-09-09 12:46:47] Building p-value spatial maps ...
#> INFO [2026-09-09 12:46:47] Done 'Accuracy'. 0 centers significant at FDR < 0.05 (fdr).
# }