| Title: | Least Squares Separate (LSS) Analysis for fMRI Data |
|---|---|
| Description: | Implements efficient least squares separate (LSS) analysis for functional magnetic resonance imaging (fMRI) data. LSS is used to estimate trial-by-trial activation patterns in event-related fMRI designs. The package provides both R and C++ implementations for computational efficiency. |
| Authors: | Brad Buchsbaum [aut, cre] |
| Maintainer: | Brad Buchsbaum <[email protected]> |
| License: | GPL-3 |
| Version: | 0.1.0 |
| Built: | 2026-06-04 06:50:20 UTC |
| Source: | https://github.com/bbuchsbaum/fmrilss |
Compare performance between the standard mixed_solve implementation and the
optimized mixed_solve_optimized version.
benchmark_mixed_solve(X, Z, K = NULL, Y, n_reps = 5)benchmark_mixed_solve(X, Z, K = NULL, Y, n_reps = 5)
X |
Fixed effects design matrix |
Z |
Random effects design matrix |
K |
Kinship matrix (optional, defaults to identity) |
Y |
Response matrix (n x V) |
n_reps |
Number of repetitions for benchmarking |
Data frame with timing results
## Not run: X <- matrix(rnorm(100 * 2), 100, 2) Z <- matrix(rnorm(100 * 3), 100, 3) Y <- matrix(rnorm(100 * 5), 100, 5) benchmark_mixed_solve(X, Z, Y = Y, n_reps = 2) ## End(Not run)## Not run: X <- matrix(rnorm(100 * 2), 100, 2) Z <- matrix(rnorm(100 * 3), 100, 3) Y <- matrix(rnorm(100 * 5), 100, 5) benchmark_mixed_solve(X, Z, Y = Y, n_reps = 2) ## End(Not run)
Evaluates how well each method recovered the true HRF
calculate_recovery_metrics(results, true_hrf)calculate_recovery_metrics(results, true_hrf)
results |
Output from compare_hrf_recovery |
true_hrf |
Ground truth HRF |
Data frame with recovery metrics
## Not run: onsets <- generate_rapid_design(n_events = 4, total_time = 60, seed = 1) sim <- generate_lwu_data(onsets, total_time = 60, n_voxels = 2, seed = 1) grid <- create_lwu_grid(n_tau = 2, n_sigma = 2, n_rho = 2) res <- compare_hrf_recovery(sim, hrf_grid = grid) calculate_recovery_metrics(res, sim$true_hrf) ## End(Not run)## Not run: onsets <- generate_rapid_design(n_events = 4, total_time = 60, seed = 1) sim <- generate_lwu_data(onsets, total_time = 60, n_voxels = 2, seed = 1) grid <- create_lwu_grid(n_tau = 2, n_sigma = 2, n_rho = 2) res <- compare_hrf_recovery(sim, hrf_grid = grid) calculate_recovery_metrics(res, sim$true_hrf) ## End(Not run)
Compares OASIS, SPMG1, SPMG3, and FIR for HRF recovery
compare_hrf_recovery(data, hrf_grid = NULL)compare_hrf_recovery(data, hrf_grid = NULL)
data |
Synthetic data from generate_lwu_data |
hrf_grid |
Optional pre-computed HRF grid for OASIS |
List with results from all methods
## Not run: onsets <- generate_rapid_design(n_events = 4, total_time = 60, seed = 1) sim <- generate_lwu_data(onsets, total_time = 60, n_voxels = 2, seed = 1) grid <- create_lwu_grid(n_tau = 2, n_sigma = 2, n_rho = 2) res <- compare_hrf_recovery(sim, hrf_grid = grid) names(res) ## End(Not run)## Not run: onsets <- generate_rapid_design(n_events = 4, total_time = 60, seed = 1) sim <- generate_lwu_data(onsets, total_time = 60, n_voxels = 2, seed = 1) grid <- create_lwu_grid(n_tau = 2, n_sigma = 2, n_rho = 2) res <- compare_hrf_recovery(sim, hrf_grid = grid) names(res) ## End(Not run)
Generates a grid of LWU HRF models with varying parameters
create_lwu_grid( tau_range = c(4, 8), sigma_range = c(1.5, 3.5), rho_range = c(0.1, 0.6), n_tau = 5, n_sigma = 3, n_rho = 3 )create_lwu_grid( tau_range = c(4, 8), sigma_range = c(1.5, 3.5), rho_range = c(0.1, 0.6), n_tau = 5, n_sigma = 3, n_rho = 3 )
tau_range |
Range of tau values to test |
sigma_range |
Range of sigma values to test |
rho_range |
Range of rho values to test |
n_tau |
Number of tau values in grid |
n_sigma |
Number of sigma values in grid |
n_rho |
Number of rho values in grid |
List of HRF models and their parameters
grid <- create_lwu_grid(n_tau = 2, n_sigma = 2, n_rho = 2) nrow(grid$parameters)grid <- create_lwu_grid(n_tau = 2, n_sigma = 2, n_rho = 2) nrow(grid$parameters)
Fits a GLM to estimate HRF basis coefficients for every voxel.
estimate_voxel_hrf(Y, events, basis, nuisance_regs = NULL)estimate_voxel_hrf(Y, events, basis, nuisance_regs = NULL)
Y |
Numeric matrix of BOLD data (time x voxels). |
events |
Data frame with |
basis |
HRF object from the |
nuisance_regs |
Optional numeric matrix of nuisance regressors. |
A VoxelHRF object containing at least:
coefficients |
Matrix of HRF basis coefficients. |
basis |
The HRF basis object used. |
conditions |
Character vector of modeled conditions. |
## Not run: set.seed(1) Y <- matrix(rnorm(100), 50, 2) events <- data.frame(onset = c(5, 25), duration = 1, condition = "A") basis <- fmrihrf::hrf_gamma() sframe <- fmrihrf::sampling_frame(blocklens = nrow(Y), TR = 1) times <- fmrihrf::samples(sframe, global = TRUE) rset <- fmrihrf::regressor_set(onsets = events$onset, fac = factor(1:nrow(events)), hrf = basis, duration = events$duration, span = 30) X <- fmrihrf::evaluate(rset, grid = times, precision = 0.1, method = "conv") coef <- matrix(rnorm(ncol(X) * ncol(Y)), ncol(X), ncol(Y)) Y <- X %*% coef + Y * 0.1 est <- estimate_voxel_hrf(Y, events, basis) str(est) ## End(Not run)## Not run: set.seed(1) Y <- matrix(rnorm(100), 50, 2) events <- data.frame(onset = c(5, 25), duration = 1, condition = "A") basis <- fmrihrf::hrf_gamma() sframe <- fmrihrf::sampling_frame(blocklens = nrow(Y), TR = 1) times <- fmrihrf::samples(sframe, global = TRUE) rset <- fmrihrf::regressor_set(onsets = events$onset, fac = factor(1:nrow(events)), hrf = basis, duration = events$duration, span = 30) X <- fmrihrf::evaluate(rset, grid = times, precision = 0.1, method = "conv") coef <- matrix(rnorm(ncol(X) * ncol(Y)), ncol(X), ncol(Y)) Y <- X %*% coef + Y * 0.1 est <- estimate_voxel_hrf(Y, events, basis) str(est) ## End(Not run)
Fits OASIS models with different HRF parameters and selects best
fit_oasis_grid(Y, onsets, sframe, hrf_grid, ridge_x = 0.01, ridge_b = 0.01)fit_oasis_grid(Y, onsets, sframe, hrf_grid, ridge_x = 0.01, ridge_b = 0.01)
Y |
Data matrix (time x voxels) |
onsets |
Event onset times |
sframe |
Sampling frame |
hrf_grid |
List of HRF models to test |
ridge_x |
Ridge parameter for design matrix |
ridge_b |
Ridge parameter for aggregator |
List with best HRF index, parameters, and beta estimates
## Not run: onsets <- generate_rapid_design(n_events = 4, total_time = 60, seed = 1) sim <- generate_lwu_data(onsets, total_time = 60, n_voxels = 2, seed = 1) grid <- create_lwu_grid(n_tau = 2, n_sigma = 2, n_rho = 2) fit <- fit_oasis_grid(sim$Y, sim$onsets, sim$sframe, grid) fit$best_params ## End(Not run)## Not run: onsets <- generate_rapid_design(n_events = 4, total_time = 60, seed = 1) sim <- generate_lwu_data(onsets, total_time = 60, n_voxels = 2, seed = 1) grid <- create_lwu_grid(n_tau = 2, n_sigma = 2, n_rho = 2) fit <- fit_oasis_grid(sim$Y, sim$onsets, sim$sframe, grid) fit$best_params ## End(Not run)
These helpers create validated option lists for lss() and friends.
No value itself. This topic groups the documented constructors
stglmnet_options(), oasis_options(), and prewhiten_options().
stglmnet_options(mode = "fixed", lambda = 0.1) oasis_options(ridge_x = 0.1, ridge_b = 0.1) prewhiten_options(method = "ar", p = 1)stglmnet_options(mode = "fixed", lambda = 0.1) oasis_options(ridge_x = 0.1, ridge_b = 0.1) prewhiten_options(method = "ar", p = 1)
Creates synthetic fMRI time series using specified LWU HRF parameters
generate_lwu_data( onsets, tau = 6, sigma = 2.5, rho = 0.35, TR = 1, total_time = 300, n_voxels = 10, amplitudes = NULL, noise_sd = 0.2, seed = NULL )generate_lwu_data( onsets, tau = 6, sigma = 2.5, rho = 0.35, TR = 1, total_time = 300, n_voxels = 10, amplitudes = NULL, noise_sd = 0.2, seed = NULL )
onsets |
Vector of event onset times in seconds |
tau |
LWU tau parameter (time-to-peak) |
sigma |
LWU sigma parameter (width) |
rho |
LWU rho parameter (undershoot amplitude) |
TR |
Repetition time in seconds |
total_time |
Total scan time in seconds |
n_voxels |
Number of voxels to simulate |
amplitudes |
Event amplitudes (scalar or vector) |
noise_sd |
Standard deviation of noise |
seed |
Random seed |
List with Y (data matrix), true_hrf, true_betas, and design info
## Not run: onsets <- generate_rapid_design(n_events = 4, total_time = 60, seed = 1) sim <- generate_lwu_data(onsets, total_time = 60, n_voxels = 2, seed = 1) dim(sim$Y) ## End(Not run)## Not run: onsets <- generate_rapid_design(n_events = 4, total_time = 60, seed = 1) sim <- generate_lwu_data(onsets, total_time = 60, n_voxels = 2, seed = 1) dim(sim$Y) ## End(Not run)
Functions to test OASIS's ability to recover HRF parameters from rapid event-related designs with overlapping HRFs. Generate Rapid Event-Related Design
generate_rapid_design( n_events = 25, total_time = 300, min_isi = 2, max_isi = 4, seed = NULL )generate_rapid_design( n_events = 25, total_time = 300, min_isi = 2, max_isi = 4, seed = NULL )
n_events |
Number of events to generate |
total_time |
Total time in seconds |
min_isi |
Minimum inter-stimulus interval in seconds |
max_isi |
Maximum inter-stimulus interval in seconds |
seed |
Random seed for reproducibility |
Creates a rapid event-related design with specified ISI range
Numeric vector of event onset times
generate_rapid_design(n_events = 5, total_time = 40, seed = 1)generate_rapid_design(n_events = 5, total_time = 40, seed = 1)
Build and validate trial-wise design objects used by the ITEM helper layer.
The returned object has class item_bundle and carries trial-level metadata
needed by item_cv() and item_slice_fold().
item_build_design( X_t, T_target = NULL, run_id = NULL, C_transform = NULL, trial_id = NULL, trial_hash = NULL, meta = list(), diagnostics = list(), validate = TRUE )item_build_design( X_t, T_target = NULL, run_id = NULL, C_transform = NULL, trial_id = NULL, trial_hash = NULL, meta = list(), diagnostics = list(), validate = TRUE )
X_t |
Numeric trial-wise design matrix with shape |
T_target |
Optional supervised targets with |
run_id |
Optional run/session identifier of length |
C_transform |
Optional transformation matrix used to map |
trial_id |
Optional trial identifier vector of length |
trial_hash |
Optional hash used by alignment guards.
If supplied, |
meta |
Optional metadata list. |
diagnostics |
Optional diagnostics list to attach to the bundle. |
validate |
Logical; when |
An object of class item_bundle with fields:
Gamma, X_t, C_transform, T_target, U, U_by_run, run_id,
trial_id, trial_hash, trial_info, meta, and diagnostics.
bundle <- item_build_design( X_t = diag(4), T_target = factor(c("A", "B", "A", "B")), run_id = c(1, 1, 2, 2) ) bundle$trial_infobundle <- item_build_design( X_t = diag(4), T_target = factor(c("A", "B", "A", "B")), run_id = c(1, 1, 2, 2) ) bundle$trial_info
Compute the trial covariance term as the inverse of a ridge-stabilized normal-equation system, using stable solve paths with fallbacks.
item_compute_u( X_t, V = NULL, v_type = c("cov", "precision"), ridge = 0, method = c("chol", "svd", "pinv"), run_id = NULL, output = c("matrix", "by_run"), tol = sqrt(.Machine$double.eps) )item_compute_u( X_t, V = NULL, v_type = c("cov", "precision"), ridge = 0, method = c("chol", "svd", "pinv"), run_id = NULL, output = c("matrix", "by_run"), tol = sqrt(.Machine$double.eps) )
X_t |
Numeric trial-wise design matrix ( |
V |
Optional temporal covariance/precision object. Accepted forms:
|
v_type |
Whether |
ridge |
Non-negative ridge term added to the trial-wise system matrix before inversion. |
method |
Preferred solver path ( |
run_id |
Optional trial-level run ids (length |
output |
Return full |
tol |
Numerical tolerance for rank/solver fallbacks. |
Numeric matrix U by default, or a named list U_by_run when
output = "by_run". Returned object includes item_diagnostics attribute
with rank/condition/solver details.
X_t <- diag(4) item_compute_u(X_t) item_compute_u(X_t, run_id = c(1, 1, 2, 2), output = "by_run")X_t <- diag(4) item_compute_u(X_t) item_compute_u(X_t, run_id = c(1, 1, 2, 2), output = "by_run")
Run deterministic leave-one-run-out (LOSO) crossvalidation for classification or regression using ITEM decoder weights.
item_cv( Gamma, T_target = NULL, U = NULL, run_id = NULL, mode = c("classification", "regression"), metric = NULL, ridge = 0, method = c("chol", "svd", "pinv"), class_levels = NULL, trial_id = NULL, trial_hash = NULL, check_hash = FALSE )item_cv( Gamma, T_target = NULL, U = NULL, run_id = NULL, mode = c("classification", "regression"), metric = NULL, ridge = 0, method = c("chol", "svd", "pinv"), class_levels = NULL, trial_id = NULL, trial_hash = NULL, check_hash = FALSE )
Gamma |
Trial-wise beta matrix ( |
T_target |
Supervised targets ( |
U |
Trial covariance as full matrix ( |
run_id |
Run/session id vector ( |
mode |
Decoding mode: |
metric |
Optional metric name.
Classification: |
ridge |
Ridge passed to |
method |
Solver preference for |
class_levels |
Optional fixed class order for classification. |
trial_id |
Optional trial id vector (used if |
trial_hash |
Optional trial hash (used if |
check_hash |
Logical; validate stored trial hash before CV. |
Object of class item_cv_result with per-fold metrics, aggregate
metric summary, and trial-level predictions.
Gamma <- matrix( c(1, 0, 0.9, 0.1, 0.1, 0.9, 0, 1), ncol = 2, byrow = TRUE ) item_cv( Gamma = Gamma, T_target = factor(c("A", "A", "B", "B")), U = diag(4), run_id = c(1, 1, 2, 2) )Gamma <- matrix( c(1, 0, 0.9, 0.1, 0.1, 0.9, 0, 1), ncol = 2, byrow = TRUE ) item_cv( Gamma = Gamma, T_target = factor(c("A", "A", "B", "B")), U = diag(4), run_id = c(1, 1, 2, 2) )
Fit ITEM weights with a ridge-stabilized generalized least-squares solve.
item_fit( Gamma_train, T_train, U_train, ridge = 0, method = c("chol", "svd", "pinv"), tol = sqrt(.Machine$double.eps) )item_fit( Gamma_train, T_train, U_train, ridge = 0, method = c("chol", "svd", "pinv"), tol = sqrt(.Machine$double.eps) )
Gamma_train |
Numeric matrix ( |
T_train |
Numeric target matrix ( |
U_train |
Trial covariance ( |
ridge |
Non-negative ridge term added to the left-hand system. |
method |
Preferred solver path ( |
tol |
Numerical tolerance for rank/solver fallbacks. |
Numeric weight matrix W_hat (n_features x p) with
item_diagnostics attribute.
Gamma_train <- matrix( c(1, 0, 0.9, 0.1, 0.1, 0.9), ncol = 2, byrow = TRUE ) T_train <- rbind(c(1, 0), c(1, 0), c(0, 1)) W_hat <- item_fit(Gamma_train, T_train, diag(3)) item_predict(Gamma_train, W_hat)Gamma_train <- matrix( c(1, 0, 0.9, 0.1, 0.1, 0.9), ncol = 2, byrow = TRUE ) T_train <- rbind(c(1, 0), c(1, 0), c(0, 1)) W_hat <- item_fit(Gamma_train, T_train, diag(3)) item_predict(Gamma_train, W_hat)
Convenience wrapper that runs lsa() to estimate trial-wise amplitudes,
computes U, and returns an item_bundle ready for crossvalidation.
item_from_lsa( Y, X_t, T_target, run_id, Z = NULL, Nuisance = NULL, V = NULL, v_type = c("cov", "precision"), ridge = 0, lsa_method = c("r", "cpp"), solver = c("chol", "svd", "pinv"), u_output = c("matrix", "by_run"), C_transform = NULL, trial_id = NULL, trial_hash = NULL, meta = list(), validate = TRUE )item_from_lsa( Y, X_t, T_target, run_id, Z = NULL, Nuisance = NULL, V = NULL, v_type = c("cov", "precision"), ridge = 0, lsa_method = c("r", "cpp"), solver = c("chol", "svd", "pinv"), u_output = c("matrix", "by_run"), C_transform = NULL, trial_id = NULL, trial_hash = NULL, meta = list(), validate = TRUE )
Y |
Numeric data matrix ( |
X_t |
Numeric trial-wise design matrix ( |
T_target |
Supervised targets with |
run_id |
Trial-level run/session identifier ( |
Z |
Optional nuisance matrix passed to |
Nuisance |
Alias for |
V |
Optional covariance/precision for |
v_type |
Whether |
ridge |
Ridge passed to |
lsa_method |
LS-A backend ( |
solver |
Solver preference for |
u_output |
Return full |
C_transform |
Optional transform matrix used to map |
trial_id |
Optional trial id vector. |
trial_hash |
Optional trial hash. |
meta |
Optional metadata list. |
validate |
Logical; enforce strict checks before returning. |
item_bundle with fields Gamma, X_t, T_target, U/U_by_run,
run_id, meta, and diagnostics.
set.seed(1) X_t <- diag(4) Y <- X_t %*% matrix( c(1, 0, 0.8, 0.2, 0.2, 0.8, 0, 1), ncol = 2, byrow = TRUE ) + matrix(rnorm(8, sd = 0.01), 4, 2) bundle <- item_from_lsa( Y = Y, X_t = X_t, T_target = factor(c("A", "B", "A", "B")), run_id = c(1, 1, 2, 2), lsa_method = "r" ) names(bundle)set.seed(1) X_t <- diag(4) Y <- X_t %*% matrix( c(1, 0, 0.8, 0.2, 0.2, 0.8, 0, 1), ncol = 2, byrow = TRUE ) + matrix(rnorm(8, sd = 0.01), 4, 2) bundle <- item_from_lsa( Y = Y, X_t = X_t, T_target = factor(c("A", "B", "A", "B")), run_id = c(1, 1, 2, 2), lsa_method = "r" ) names(bundle)
Compute T_hat = Gamma_test %*% W_hat.
item_predict(Gamma_test, W_hat)item_predict(Gamma_test, W_hat)
Gamma_test |
Numeric matrix ( |
W_hat |
Numeric matrix ( |
Numeric matrix of predictions (n_test x p).
Gamma_test <- matrix(c(1, 0, 0, 1), ncol = 2, byrow = TRUE) W_hat <- diag(2) item_predict(Gamma_test, W_hat)Gamma_test <- matrix(c(1, 0, 0, 1), ncol = 2, byrow = TRUE) W_hat <- diag(2) item_predict(Gamma_test, W_hat)
Create deterministic leave-one-run-out slices for Gamma, T_target, and
trial covariance (U or U_by_run).
item_slice_fold(bundle, test_run, check_hash = FALSE)item_slice_fold(bundle, test_run, check_hash = FALSE)
bundle |
Object of class |
test_run |
Run/session id to hold out for testing. |
check_hash |
Logical; if |
A list with train/test slices:
Gamma_train, Gamma_test, T_train, T_test,
U_train, U_test, train_idx, test_idx, train_runs, test_run.
bundle <- item_build_design( X_t = diag(4), T_target = factor(c("A", "B", "A", "B")), run_id = c(1, 1, 2, 2) ) bundle$Gamma <- matrix(c(1, 0, 0.8, 0.2, 0.2, 0.8, 0, 1), 4, 2, byrow = TRUE) bundle$U <- diag(4) fold <- item_slice_fold(bundle, test_run = 2) fold$test_idxbundle <- item_build_design( X_t = diag(4), T_target = factor(c("A", "B", "A", "B")), run_id = c(1, 1, 2, 2) ) bundle$Gamma <- matrix(c(1, 0, 0.8, 0.2, 0.2, 0.8, 0, 1), 4, 2, byrow = TRUE) bundle$U <- diag(4) fold <- item_slice_fold(bundle, test_run = 2) fold$test_idx
Performs a standard multiple regression analysis where all trial regressors are fitted simultaneously. This provides a reference comparison to the Least Squares Separate (LSS) approach.
lsa(Y, X, Z = NULL, Nuisance = NULL, method = c("r", "cpp"))lsa(Y, X, Z = NULL, Nuisance = NULL, method = c("r", "cpp"))
Y |
A numeric matrix where rows are timepoints and columns are voxels/features. This is the dependent variable data. |
X |
A numeric matrix where rows are timepoints and columns are trial-specific regressors. Each column represents a single trial or event. |
Z |
A numeric matrix of nuisance regressors (e.g., motion parameters, drift terms). Defaults to NULL. |
Nuisance |
An alias for Z, provided for consistency with LSS interface. If both Z and Nuisance are provided, Z takes precedence. |
method |
Character string specifying the computational method:
|
LSA fits the model: Y = Xbeta + Zgamma + error, where all trial regressors in X are estimated simultaneously. This is in contrast to LSS, which fits each trial separately while treating other trials as nuisance regressors.
A numeric matrix of size T × V containing the beta estimates for each trial regressor (rows) and each voxel (columns).
n_timepoints <- 100 n_trials <- 10 n_voxels <- 50 X <- matrix(0, n_timepoints, n_trials) for(i in 1:n_trials) { start <- (i-1) * 8 + 1 if(start + 5 <= n_timepoints) { X[start:(start+5), i] <- 1 } } Y <- matrix(rnorm(n_timepoints * n_voxels), n_timepoints, n_voxels) true_betas <- matrix(rnorm(n_trials * n_voxels, 0, 0.5), n_trials, n_voxels) for(i in 1:n_trials) { Y <- Y + X[, i] %*% matrix(true_betas[i, ], 1, n_voxels) } beta_estimates <- lsa(Y, X)n_timepoints <- 100 n_trials <- 10 n_voxels <- 50 X <- matrix(0, n_timepoints, n_trials) for(i in 1:n_trials) { start <- (i-1) * 8 + 1 if(start + 5 <= n_timepoints) { X[start:(start+5), i] <- 1 } } Y <- matrix(rnorm(n_timepoints * n_voxels), n_timepoints, n_voxels) true_betas <- matrix(rnorm(n_trials * n_voxels, 0, 0.5), n_trials, n_voxels) for(i in 1:n_trials) { Y <- Y + X[, i] %*% matrix(true_betas[i, ], 1, n_voxels) } beta_estimates <- lsa(Y, X)
Computes trial-wise beta estimates using the Least Squares Separate approach of Mumford et al. (2012). This method fits a separate GLM for each trial, with the trial of interest and all other trials as separate regressors.
lss( Y, X, Z = NULL, Nuisance = NULL, method = c("r_optimized", "cpp_optimized", "r_vectorized", "cpp", "naive", "oasis", "stglmnet"), block_size = 96, oasis = list(), stglmnet = list(), prewhiten = NULL )lss( Y, X, Z = NULL, Nuisance = NULL, method = c("r_optimized", "cpp_optimized", "r_vectorized", "cpp", "naive", "oasis", "stglmnet"), block_size = 96, oasis = list(), stglmnet = list(), prewhiten = NULL )
Y |
A numeric matrix of size n × V where n is the number of timepoints and V is the number of voxels/variables |
X |
A numeric matrix of size n × T where T is the number of trials. Each column represents the design for one trial |
Z |
A numeric matrix of size n × F representing experimental regressors to include in all trial-wise models. These are regressors we want to model and get beta estimates for, but not trial-wise (e.g., intercept, condition effects, block effects). If NULL, an intercept-only design is used. Defaults to NULL |
Nuisance |
A numeric matrix of size n × N representing nuisance regressors to be projected out before LSS analysis (e.g., motion parameters, physiological noise). If NULL, no nuisance projection is performed. Defaults to NULL |
method |
Character string specifying which implementation to use. Options are:
|
block_size |
An integer specifying the voxel block size for parallel
processing, only applicable when |
oasis |
A list of options for the OASIS method (ridge, SE, design
construction, etc.).
See Details and |
stglmnet |
A list of options for the |
prewhiten |
A list of prewhitening options using the fmriAR
package, or |
The LSS approach fits a separate GLM for each trial, where each model includes:
The trial of interest (from column i of X)
All other trials combined (sum of all other columns of X)
Experimental regressors (Z matrix) - these are modeled to get beta estimates but not trial-wise
If Nuisance regressors are provided, they are first projected out from both Y and X using standard linear regression residualization.
When using method="oasis", the following options are available in the oasis list
(see also oasis_options for a validated constructor):
design_spec: A list for building trial-wise designs from event onsets using fmrihrf.
Must contain: sframe (sampling frame), cond (list with onsets,
hrf, and optionally span), and optionally others (list of other conditions
to be modeled as nuisances). When provided, X can be NULL and will be constructed automatically.
K: Explicit basis dimension for multi-basis HRF models (e.g., 3 for SPMG3).
If not provided, it's auto-detected from X dimensions or defaults to 1 for single-basis HRFs.
ridge_mode: Either "fractional" (default) or "absolute". In absolute mode,
ridge_x and ridge_b are used directly as regularization parameters. In fractional mode,
they represent fractions of the mean design energy for adaptive regularization.
ridge_x: Ridge parameter for trial-specific regressors (default 0.05).
Controls
regularization strength for individual trial estimates.
ridge_b: Ridge parameter for the aggregator regressor (default 0.05).
Controls
regularization strength for the sum of all other trials.
return_se: Logical, whether to return standard errors (default FALSE). When TRUE,
returns a list with beta (trial estimates) and se (standard errors) components.
return_diag: Logical, whether to return design diagnostics (default FALSE).
When TRUE, includes diagnostic information about the design matrix structure.
block_cols: Integer, voxel block size for memory-efficient processing (default 4096).
Larger values use more memory but may be faster for systems with sufficient RAM.
ntrials: Explicit number of trials (used when K > 1 to determine output dimensions).
If not provided, calculated as ncol(X) / K.
hrf_grid: Vector of HRF indices for grid-based HRF selection (advanced use).
Allows testing multiple HRF shapes simultaneously.
Prewhitening (temporal autocorrelation correction):
Use the top-level prewhiten parameter for all temporal whitening.
This replaces the old oasis$whiten = "ar1" syntax, which is now
deprecated and ignored. Do not put AR options inside the
oasis list; they belong in prewhiten.
When using method = "stglmnet", the backend accepts an additional
nested stglmnet= list for lambda selection, overlap-adaptive
penalties, and optional pooled trial parameterizations. The common pattern is
stglmnet = stglmnet_options(mode = "cv") to select lambda by
cross-validation, or stglmnet = stglmnet_options(mode = "fixed",
lambda = 0.01) for a fixed elastic-net fit. The backend reuses fmrilss
prewhitening and nuisance-projection utilities rather than maintaining a
separate whitening path.
The prewhiten list accepts the following fields
(see also prewhiten_options for a validated constructor):
method: Character, "ar" (default when the list is
non-NULL), "arma", or "none".
"ar" fits a pure autoregressive model; "arma" adds a
moving-average component (requires q > 0).
p: AR order.
An integer, or "auto" (default) to select via AIC/BIC up to
p_max. Use p = 1 for a simple AR(1) model (the most
common choice for fMRI); higher orders are rarely needed but may
help with short TRs or multi-band sequences.
q: Integer MA order for ARMA models (default 0). Only
relevant when method = "arma".
p_max: Integer, maximum AR order when p = "auto"
(default 6).
pooling: How AR coefficients are estimated across voxels.
One of:
"global"(default) A single set of AR coefficients is estimated from the median autocorrelation across all voxels. Fast and usually adequate.
"voxel"Fit a separate AR model per voxel. Most
accurate but slow; consider "parcel" instead.
"run"Fit one AR model per run (requires runs).
Useful when noise structure differs between runs.
"parcel"Fit one AR model per parcel (requires
parcels). Good compromise between "global" and
"voxel".
runs: Integer vector of length nrow(Y) giving
run/block labels. Required for pooling = "run" and recommended
whenever data span multiple runs so that whitening respects run
boundaries.
parcels: Integer vector of length ncol(Y) giving
parcel labels. Required for pooling = "parcel".
exact_first: Character, "ar1" (default) or
"none". When "ar1", the first observation of each
segment is scaled by for the exact
likelihood; "none" drops the first observation instead.
compute_residuals: Logical (default TRUE). When TRUE,
OLS residuals from the full design are computed before fitting the
noise model. Set to FALSE only if Y is already residualized.
Typical prewhiten recipes:
# Simple AR(1) — good default for most fMRI data
prewhiten = list(method = "ar", p = 1)
# Auto-select AR order (AIC), global pooling
prewhiten = list(method = "ar", p = "auto")
# Per-run AR(1) for multi-run data
prewhiten = list(method = "ar", p = 1, pooling = "run",
runs = blockids)
# Parcel-based AR with atlas labels
prewhiten = list(method = "ar", p = 1, pooling = "parcel",
parcels = atlas_labels)
# Or use the validated constructor:
prewhiten = prewhiten_options(method = "ar", p = 1, pooling = "run",
runs = blockids)
Prewhitening is applied before the LSS analysis to account for temporal autocorrelation in the fMRI time series. Both Y and all design matrices (X, Z, Nuisance) are filtered through the same whitening operator so that OLS on the whitened system is equivalent to GLS on the original data.
The OASIS method provides a mathematically equivalent but computationally optimized version of standard LSS. It reformulates the per-trial GLM fitting as a single matrix operation, eliminating redundant computations. This is particularly beneficial for designs with many trials or when processing large datasets. When K > 1 (multi-basis HRFs), the output will have K*ntrials rows, with basis functions for each trial arranged sequentially.
A numeric matrix of size T × V containing the trial-wise beta estimates. Note: Currently only returns estimates for the trial regressors (X). Beta estimates for the experimental regressors (Z) are computed but not returned.
Mumford, J. A., Turner, B. O., Ashby, F. G., & Poldrack, R. A. (2012). Deconvolving BOLD activation in event-related designs for multivoxel pattern classification analyses. NeuroImage, 59(3), 2636-2643.
n_timepoints <- 100 n_trials <- 10 n_voxels <- 50 X <- matrix(0, n_timepoints, n_trials) for(i in 1:n_trials) { start <- (i-1) * 8 + 1 if(start + 5 <= n_timepoints) { X[start:(start+5), i] <- 1 } } Y <- matrix(rnorm(n_timepoints * n_voxels), n_timepoints, n_voxels) true_betas <- matrix(rnorm(n_trials * n_voxels, 0, 0.5), n_trials, n_voxels) for(i in 1:n_trials) { Y <- Y + X[, i] %*% matrix(true_betas[i, ], 1, n_voxels) } beta_estimates <- lss(Y, X) Z <- cbind(1, scale(1:n_timepoints)) beta_estimates_with_regressors <- lss(Y, X, Z = Z) Nuisance <- matrix(rnorm(n_timepoints * 6), n_timepoints, 6) beta_estimates_clean <- lss(Y, X, Z = Z, Nuisance = Nuisance) ## Not run: beta_oasis <- lss(Y, X, method = "oasis", oasis = list(ridge_x = 0.1, ridge_b = 0.1, ridge_mode = "fractional")) result_with_se <- lss(Y, X, method = "oasis", oasis = list(return_se = TRUE)) beta_estimates <- result_with_se$beta standard_errors <- result_with_se$se sframe <- sampling_frame(blocklens = 200, TR = 1.0) beta_auto <- lss(Y, X = NULL, method = "oasis", oasis = list( design_spec = list( sframe = sframe, cond = list( onsets = c(10, 30, 50, 70, 90, 110, 130, 150), hrf = HRF_SPMG1, span = 25 ), others = list( list(onsets = c(20, 40, 60, 80, 100, 120, 140)) ) ) )) beta_multibasis <- lss(Y, X = NULL, method = "oasis", oasis = list( design_spec = list( sframe = sframe, cond = list( onsets = c(10, 30, 50, 70, 90), hrf = HRF_SPMG3, span = 30 ) ), K = 3 )) ## End(Not run)n_timepoints <- 100 n_trials <- 10 n_voxels <- 50 X <- matrix(0, n_timepoints, n_trials) for(i in 1:n_trials) { start <- (i-1) * 8 + 1 if(start + 5 <= n_timepoints) { X[start:(start+5), i] <- 1 } } Y <- matrix(rnorm(n_timepoints * n_voxels), n_timepoints, n_voxels) true_betas <- matrix(rnorm(n_trials * n_voxels, 0, 0.5), n_trials, n_voxels) for(i in 1:n_trials) { Y <- Y + X[, i] %*% matrix(true_betas[i, ], 1, n_voxels) } beta_estimates <- lss(Y, X) Z <- cbind(1, scale(1:n_timepoints)) beta_estimates_with_regressors <- lss(Y, X, Z = Z) Nuisance <- matrix(rnorm(n_timepoints * 6), n_timepoints, 6) beta_estimates_clean <- lss(Y, X, Z = Z, Nuisance = Nuisance) ## Not run: beta_oasis <- lss(Y, X, method = "oasis", oasis = list(ridge_x = 0.1, ridge_b = 0.1, ridge_mode = "fractional")) result_with_se <- lss(Y, X, method = "oasis", oasis = list(return_se = TRUE)) beta_estimates <- result_with_se$beta standard_errors <- result_with_se$se sframe <- sampling_frame(blocklens = 200, TR = 1.0) beta_auto <- lss(Y, X = NULL, method = "oasis", oasis = list( design_spec = list( sframe = sframe, cond = list( onsets = c(10, 30, 50, 70, 90, 110, 130, 150), hrf = HRF_SPMG1, span = 25 ), others = list( list(onsets = c(20, 40, 60, 80, 100, 120, 140)) ) ) )) beta_multibasis <- lss(Y, X = NULL, method = "oasis", oasis = list( design_spec = list( sframe = sframe, cond = list( onsets = c(10, 30, 50, 70, 90), hrf = HRF_SPMG3, span = 30 ) ), K = 3 )) ## End(Not run)
Fast C++ implementation of least squares separate (LSS) beta estimation using vectorized matrix operations. Computes all trial betas in a single pass without loops.
lss_beta_cpp(C_projected, Y_projected)lss_beta_cpp(C_projected, Y_projected)
C_projected |
Projected trial regressors (n x T) from project_confounds_cpp |
Y_projected |
Projected data (n x V) from project_confounds_cpp |
This vectorized implementation computes all LSS betas simultaneously using matrix algebra. It's significantly faster than per-trial loops and automatically benefits from BLAS multithreading. The algorithm handles numerical edge cases by setting problematic denominators to NaN.
For best performance on large datasets, ensure your R installation uses optimized BLAS (like OpenBLAS or Intel MKL).
Beta matrix (T x V) with LSS estimates for each trial and voxel
## Not run: result <- project_confounds_cpp(X_confounds, Y_data, C_trials) betas <- lss_beta_cpp(result$Q_dmat_ran, result$residual_data) ## End(Not run)## Not run: result <- project_confounds_cpp(X_confounds, Y_data, C_trials) betas <- lss_beta_cpp(result$Q_dmat_ran, result$residual_data) ## End(Not run)
A wrapper for the optimized C++ LSS implementation
lss_cpp_optimized(Y, bdes)lss_cpp_optimized(Y, bdes)
Y |
the voxel by time data matrix |
bdes |
the block design list created by |
a matrix of beta estimates
set.seed(1) Y <- matrix(rnorm(16), 8, 2) X_trials <- matrix(0, 8, 2) X_trials[2:3, 1] <- 1 X_trials[5:6, 2] <- 1 bdes <- list( dmat_base = matrix(1, 8, 1), dmat_fixed = NULL, dmat_ran = X_trials ) lss_cpp_optimized(Y, bdes)set.seed(1) Y <- matrix(rnorm(16), 8, 2) X_trials <- matrix(0, 8, 2) X_trials[2:3, 1] <- 1 X_trials[5:6, 2] <- 1 bdes <- list( dmat_base = matrix(1, 8, 1), dmat_fixed = NULL, dmat_ran = X_trials ) lss_cpp_optimized(Y, bdes)
Perform Least Squares Separate (LSS) analysis using event_model and baseline_model objects from the fmridesign package. This provides a streamlined interface for complex designs with multi-condition, parametric modulators, and structured nuisance handling.
lss_design( Y, event_model, baseline_model = NULL, method = "oasis", oasis = list(), prewhiten = NULL, blockids = NULL, validate = TRUE, ... )lss_design( Y, event_model, baseline_model = NULL, method = "oasis", oasis = list(), prewhiten = NULL, blockids = NULL, validate = TRUE, ... )
Y |
Numeric matrix of fMRI data (timepoints × voxels). |
event_model |
An event_model object from |
baseline_model |
Optional baseline_model object from
|
method |
LSS method to use. Currently only "oasis" is supported for event_model integration. |
oasis |
List of OASIS-specific options: ridge regularization
( |
prewhiten |
Optional prewhitening specification as a list (or
|
blockids |
Optional block/run identifiers for event_model. If NULL, extracted from event_model$blockids. |
validate |
Logical. If TRUE (default), performs validation checks on design compatibility, collinearity, and temporal alignment. |
... |
Additional arguments passed to the underlying LSS method. |
Design Specification:
The event_model should typically use trialwise() for LSS:
emod <- event_model(onset ~ trialwise(basis = "spmg1"),
data = events,
block = ~run,
sampling_frame = sframe)
For factorial designs (e.g., estimating condition-level betas separately):
emod <- event_model(onset ~ hrf(condition),
data = events,
block = ~run,
sampling_frame = sframe)
Baseline Model:
If provided, baseline_model components are mapped as follows:
drift and block terms → Z parameter (fixed effects)
nuisance term → Nuisance parameter (confounds)
Multi-Run Handling:
Both event_model and baseline_model must use the same sampling_frame.
Run structure is automatically respected. Event onsets should be run-relative
(resetting to 0 each run) as per fmridesign convention - conversion to global
time is handled automatically.
Prewhitening:
Use the prewhiten parameter (not the oasis list) for temporal
autocorrelation correction. For multi-run data, pass
prewhiten = list(method = "ar", p = 1, pooling = "run", runs = blockids)
so that whitening respects run boundaries.
See lss and prewhiten_options for full details.
Validation:
When validate = TRUE, the function checks:
Temporal alignment: nrow(Y) matches total scans in sampling_frame
Collinearity: Design matrix condition number < 30 (suppressed when
ridge is already configured via oasis$ridge_x or oasis$ridge_b)
Compatibility: event_model and baseline_model use same sampling_frame
Matrix of trial-wise beta estimates (trials × voxels), or (trials × basis_functions) × voxels for multi-basis HRFs.
lss for the traditional matrix-based interface,
fmridesign::event_model for event model creation,
fmridesign::baseline_model for baseline model creation
## Not run: library(fmridesign) library(fmrihrf) sframe <- sampling_frame(blocklens = c(150, 150), TR = 2) trials <- data.frame( onset = c(10, 30, 50, 70, 90, 110, 10, 30, 50, 70, 90, 110), run = rep(1:2, each = 6) ) emod <- event_model( onset ~ trialwise(basis = "spmg1"), data = trials, block = ~run, sampling_frame = sframe ) motion <- list( matrix(rnorm(150 * 6), 150, 6), matrix(rnorm(150 * 6), 150, 6) ) bmodel <- baseline_model( basis = "bs", degree = 5, sframe = sframe, nuisance_list = motion ) Y <- matrix(rnorm(300 * 1000), 300, 1000) beta <- lss_design(Y, emod, bmodel, method = "oasis") dim(beta) ## End(Not run)## Not run: library(fmridesign) library(fmrihrf) sframe <- sampling_frame(blocklens = c(150, 150), TR = 2) trials <- data.frame( onset = c(10, 30, 50, 70, 90, 110, 10, 30, 50, 70, 90, 110), run = rep(1:2, each = 6) ) emod <- event_model( onset ~ trialwise(basis = "spmg1"), data = trials, block = ~run, sampling_frame = sframe ) motion <- list( matrix(rnorm(150 * 6), 150, 6), matrix(rnorm(150 * 6), 150, 6) ) bmodel <- baseline_model( basis = "bs", degree = 5, sframe = sframe, nuisance_list = motion ) Y <- matrix(rnorm(300 * 1000), 300, 1000) beta <- lss_design(Y, emod, bmodel, method = "oasis") dim(beta) ## End(Not run)
lss() usageThese functions provide a modern-signature entry point for methods that
historically required a block-design (bdes) object.
No value itself. This topic groups lss_naive_fit() and
lss_optimized_fit().
set.seed(1) Y <- matrix(rnorm(16), 8, 2) X <- matrix(0, 8, 2) X[2:3, 1] <- 1 X[5:6, 2] <- 1 lss_naive_fit(Y, X)set.seed(1) Y <- matrix(rnorm(16), 8, 2) X <- matrix(0, 8, 2) X[2:3, 1] <- 1 X[5:6, 2] <- 1 lss_naive_fit(Y, X)
Performs LSS analysis using the naive approach where each trial model is fit
separately. This is the conceptually simplest implementation but less efficient
than the optimized lss function.
lss_naive(Y = NULL, bdes, dset = NULL)lss_naive(Y = NULL, bdes, dset = NULL)
Y |
A numeric matrix where rows are timepoints and columns are voxels/features.
If NULL, the function will attempt to extract data from |
bdes |
A list containing design matrices with components:
|
dset |
Optional dataset object. If provided and Y is NULL, data will be
extracted using |
This function implements the LSS approach by fitting a separate GLM for each trial. Following the method described by Mumford et al. (2012), the model for each trial includes:
The regressor for the trial of interest.
A single regressor representing all other trials (the sum of their individual regressors).
All base regressors (e.g., intercept, drift terms).
All fixed effects regressors (if any).
While less efficient than the optimized lss function, this
implementation is conceptually clear and serves as a reference for validation.
A numeric matrix with dimensions (n_events x n_voxels) containing the LSS beta estimates for each trial and voxel.
lss for the optimized implementation
set.seed(1) n_timepoints <- 20 n_trials <- 4 n_voxels <- 3 X <- matrix(0, n_timepoints, n_trials) for (i in seq_len(n_trials)) { onset <- 1 + (i - 1) * 4 X[onset:(onset + 2), i] <- 1 } Z <- cbind(Intercept = 1, Linear = seq_len(n_timepoints)) Y <- matrix(rnorm(n_timepoints * n_voxels), n_timepoints, n_voxels) for (i in seq_len(n_trials)) { Y <- Y + X[, i] %*% matrix(rnorm(n_voxels, sd = 0.2), 1, n_voxels) } bdes <- list( dmat_base = Z, dmat_ran = X, dmat_fixed = NULL, fixed_ind = NULL ) beta_estimates_naive <- lss_naive(Y = Y, bdes = bdes) beta_estimates_fast <- lss(Y = Y, X = X, Z = Z) max(abs(beta_estimates_naive - beta_estimates_fast))set.seed(1) n_timepoints <- 20 n_trials <- 4 n_voxels <- 3 X <- matrix(0, n_timepoints, n_trials) for (i in seq_len(n_trials)) { onset <- 1 + (i - 1) * 4 X[onset:(onset + 2), i] <- 1 } Z <- cbind(Intercept = 1, Linear = seq_len(n_timepoints)) Y <- matrix(rnorm(n_timepoints * n_voxels), n_timepoints, n_voxels) for (i in seq_len(n_trials)) { Y <- Y + X[, i] %*% matrix(rnorm(n_voxels, sd = 0.2), 1, n_voxels) } bdes <- list( dmat_base = Z, dmat_ran = X, dmat_fixed = NULL, fixed_ind = NULL ) beta_estimates_naive <- lss_naive(Y = Y, bdes = bdes) beta_estimates_fast <- lss(Y = Y, X = X, Z = Z) max(abs(beta_estimates_naive - beta_estimates_fast))
Equivalent to calling lss(..., method = "naive").
lss_naive_fit(Y, X, Z = NULL, Nuisance = NULL, prewhiten = NULL)lss_naive_fit(Y, X, Z = NULL, Nuisance = NULL, prewhiten = NULL)
Y |
Numeric matrix (timepoints x voxels). |
X |
Trial design matrix (timepoints x trials). |
Z |
Optional experimental regressors. |
Nuisance |
Optional nuisance regressors to project out. |
prewhiten |
Optional prewhitening options list (see |
A numeric matrix (trials x voxels) of beta estimates.
set.seed(1) Y <- matrix(rnorm(16), 8, 2) X <- matrix(0, 8, 2) X[2:3, 1] <- 1 X[5:6, 2] <- 1 lss_naive_fit(Y, X)set.seed(1) Y <- matrix(rnorm(16), 8, 2) X <- matrix(0, 8, 2) X[2:3, 1] <- 1 X[5:6, 2] <- 1 lss_naive_fit(Y, X)
An optimized version of the LSS analysis that avoids creating large intermediate matrices, providing a significant speedup and lower memory usage for the pure R implementation.
lss_optimized(Y = NULL, bdes, dset = NULL, use_cpp = TRUE)lss_optimized(Y = NULL, bdes, dset = NULL, use_cpp = TRUE)
Y |
A numeric matrix where rows are timepoints and columns are voxels/features. |
bdes |
A list containing the design matrices. |
dset |
Optional dataset object. |
use_cpp |
Logical. If TRUE (default), uses the C++ implementation. If FALSE, uses the new optimized R implementation. |
A numeric matrix of LSS beta estimates.
set.seed(1) Y <- matrix(rnorm(16), 8, 2) X_trials <- matrix(0, 8, 2) X_trials[2:3, 1] <- 1 X_trials[5:6, 2] <- 1 bdes <- list( dmat_base = matrix(1, 8, 1), dmat_ran = X_trials, dmat_fixed = NULL, fixed_ind = NULL ) lss_optimized(Y, bdes, use_cpp = FALSE)set.seed(1) Y <- matrix(rnorm(16), 8, 2) X_trials <- matrix(0, 8, 2) X_trials[2:3, 1] <- 1 X_trials[5:6, 2] <- 1 bdes <- list( dmat_base = matrix(1, 8, 1), dmat_ran = X_trials, dmat_fixed = NULL, fixed_ind = NULL ) lss_optimized(Y, bdes, use_cpp = FALSE)
This is a convenience wrapper around lss() that selects one of the optimized
implementations.
lss_optimized_fit( Y, X, Z = NULL, Nuisance = NULL, engine = c("cpp", "r"), block_size = 96, prewhiten = NULL )lss_optimized_fit( Y, X, Z = NULL, Nuisance = NULL, engine = c("cpp", "r"), block_size = 96, prewhiten = NULL )
Y |
Numeric matrix (timepoints x voxels). |
X |
Trial design matrix (timepoints x trials). |
Z |
Optional experimental regressors. |
Nuisance |
Optional nuisance regressors to project out. |
engine |
|
block_size |
Block size used by the C++ optimized path. |
prewhiten |
Optional prewhitening options list (see |
A numeric matrix (trials x voxels) of beta estimates.
set.seed(1) Y <- matrix(rnorm(16), 8, 2) X <- matrix(0, 8, 2) X[2:3, 1] <- 1 X[5:6, 2] <- 1 lss_optimized_fit(Y, X, engine = "r")set.seed(1) Y <- matrix(rnorm(16), 8, 2) X <- matrix(0, 8, 2) X[2:3, 1] <- 1 X[5:6, 2] <- 1 lss_optimized_fit(Y, X, engine = "r")
Orchestrates the SBHM pipeline: (1) prepass aggregate fit in the learned shared basis, (2) cosine matching to a library of HRFs represented in the same basis, (3) Least Squares Separate (OASIS) with the SBHM basis to obtain trial-wise r-dimensional coefficients, and (4) projection of those coefficients onto the matched coordinates to produce scalar amplitudes.
lss_sbhm( Y, sbhm, design_spec, Nuisance = NULL, prewhiten = NULL, prepass = list(), match = list(shrink = list(tau = 0, ref = NULL, snr = NULL), topK = 3, soft_blend = TRUE, blend_margin = 0.08, whiten = FALSE, sv_floor_rel = 0.05, whiten_power = 0.5, min_margin = NULL, min_beta_norm = NULL, fallback_ref = NULL, orient_ref = TRUE, alpha_source = "prepass", rank1_min = 0), oasis = list(), amplitude = list(method = "lss1", ridge = list(mode = "fractional", lambda = 0.02), ridge_frac = list(x = 0.02, b = 0.02), cond_gate = NULL, adaptive = list(enable = FALSE, base = 0.02, k0 = 1000, max = 0.08), return_se = FALSE), return = c("amplitude", "coefficients", "both") )lss_sbhm( Y, sbhm, design_spec, Nuisance = NULL, prewhiten = NULL, prepass = list(), match = list(shrink = list(tau = 0, ref = NULL, snr = NULL), topK = 3, soft_blend = TRUE, blend_margin = 0.08, whiten = FALSE, sv_floor_rel = 0.05, whiten_power = 0.5, min_margin = NULL, min_beta_norm = NULL, fallback_ref = NULL, orient_ref = TRUE, alpha_source = "prepass", rank1_min = 0), oasis = list(), amplitude = list(method = "lss1", ridge = list(mode = "fractional", lambda = 0.02), ridge_frac = list(x = 0.02, b = 0.02), cond_gate = NULL, adaptive = list(enable = FALSE, base = 0.02, k0 = 1000, max = 0.08), return_se = FALSE), return = c("amplitude", "coefficients", "both") )
Y |
Numeric matrix T×V of fMRI time series. |
sbhm |
SBHM object from |
design_spec |
List for design construction (same as |
Nuisance |
Optional T×P nuisance regressors. |
prewhiten |
Optional prewhitening options (see |
prepass |
Optional list forwarded to |
match |
Optional list forwarded to
|
oasis |
Optional list forwarded to |
amplitude |
List controlling the scalar amplitude stage. Fields:
|
return |
One of |
Most users should treat the prepass, match, oasis, and amplitude inputs
as optional override lists: you can provide only the fields you want to
change, and rely on defaults for everything else.
If you already use fmridesign, prefer lss_sbhm_design() to avoid manually
assembling an OASIS design_spec.
A list with components:
amplitude ntrials×V matrix (when requested)
coeffs_r r×ntrials×V array of trial-wise coefficients (when requested)
matched_idx length-V integer indices into the library
margin length-V confidence margins (top1 - top2 cosine)
alpha_coords r×V matched coordinates per voxel
diag list with r, ntrials, and times
lss_sbhm_design(), sbhm_prepass(), sbhm_match()
## Not run: library(fmrihrf) set.seed(3) Tlen <- 180; V <- 4 sframe <- sampling_frame(blocklens = Tlen, TR = 1) H <- cbind(exp(-seq(0, 30, length.out = Tlen)/5), exp(-seq(0, 30, length.out = Tlen)/7)) sbhm <- sbhm_build(library_H = H, r = 4, sframe = sframe, normalize = TRUE) onsets <- seq(8, 140, by = 12) design_spec <- list(sframe = sframe, cond = list(onsets = onsets, duration = 0, span = 30)) hrf_B <- sbhm_hrf(sbhm$B, sbhm$tgrid, sbhm$span) rr <- fmrihrf::regressor(onsets = onsets, hrf = hrf_B, duration = 0, span = 30, summate = FALSE) Xr <- fmrihrf::evaluate(rr, grid = sbhm$tgrid, precision = 0.1, method = "conv") alpha_true <- rnorm(ncol(sbhm$B)) Y <- matrix(rnorm(Tlen*V, sd = .6), Tlen, V) Y[,1] <- Y[,1] + Xr %*% alpha_true out <- lss_sbhm(Y, sbhm, design_spec) out2 <- lss_sbhm(Y, sbhm, design_spec, match = list(topK = 3, soft_blend = TRUE), return = "amplitude") names(out) ## End(Not run)## Not run: library(fmrihrf) set.seed(3) Tlen <- 180; V <- 4 sframe <- sampling_frame(blocklens = Tlen, TR = 1) H <- cbind(exp(-seq(0, 30, length.out = Tlen)/5), exp(-seq(0, 30, length.out = Tlen)/7)) sbhm <- sbhm_build(library_H = H, r = 4, sframe = sframe, normalize = TRUE) onsets <- seq(8, 140, by = 12) design_spec <- list(sframe = sframe, cond = list(onsets = onsets, duration = 0, span = 30)) hrf_B <- sbhm_hrf(sbhm$B, sbhm$tgrid, sbhm$span) rr <- fmrihrf::regressor(onsets = onsets, hrf = hrf_B, duration = 0, span = 30, summate = FALSE) Xr <- fmrihrf::evaluate(rr, grid = sbhm$tgrid, precision = 0.1, method = "conv") alpha_true <- rnorm(ncol(sbhm$B)) Y <- matrix(rnorm(Tlen*V, sd = .6), Tlen, V) Y[,1] <- Y[,1] + Xr %*% alpha_true out <- lss_sbhm(Y, sbhm, design_spec) out2 <- lss_sbhm(Y, sbhm, design_spec, match = list(topK = 3, soft_blend = TRUE), return = "amplitude") names(out) ## End(Not run)
Run the SBHM end-to-end pipeline using fmridesign's event_model and
optional baseline_model, mirroring the convenience of lss_design() but
producing SBHM coefficients and (optionally) scalar amplitudes.
lss_sbhm_design( Y, sbhm, event_model, baseline_model = NULL, prewhiten = NULL, prepass = list(), match = list(shrink = list(tau = 0, ref = NULL, snr = NULL), topK = 3, soft_blend = TRUE, blend_margin = 0.08, whiten = FALSE, sv_floor_rel = 0.05, whiten_power = 0.5, min_margin = NULL, min_beta_norm = NULL, fallback_ref = NULL, orient_ref = TRUE, alpha_source = "prepass", rank1_min = 0), oasis = list(), amplitude = list(method = "lss1", ridge = list(mode = "fractional", lambda = 0.02), ridge_frac = list(x = 0.02, b = 0.02), cond_gate = NULL, adaptive = list(enable = FALSE, base = 0.02, k0 = 1000, max = 0.08), return_se = FALSE), return = c("amplitude", "coefficients", "both"), validate = TRUE, ... )lss_sbhm_design( Y, sbhm, event_model, baseline_model = NULL, prewhiten = NULL, prepass = list(), match = list(shrink = list(tau = 0, ref = NULL, snr = NULL), topK = 3, soft_blend = TRUE, blend_margin = 0.08, whiten = FALSE, sv_floor_rel = 0.05, whiten_power = 0.5, min_margin = NULL, min_beta_norm = NULL, fallback_ref = NULL, orient_ref = TRUE, alpha_source = "prepass", rank1_min = 0), oasis = list(), amplitude = list(method = "lss1", ridge = list(mode = "fractional", lambda = 0.02), ridge_frac = list(x = 0.02, b = 0.02), cond_gate = NULL, adaptive = list(enable = FALSE, base = 0.02, k0 = 1000, max = 0.08), return_se = FALSE), return = c("amplitude", "coefficients", "both"), validate = TRUE, ... )
Y |
Numeric matrix T×V of fMRI time series (timepoints × voxels). |
sbhm |
SBHM object as returned by |
event_model |
An |
baseline_model |
Optional |
prewhiten |
Optional prewhitening options (see |
prepass |
Optional list forwarded to |
match |
Optional list forwarded to |
oasis |
Optional list forwarded to |
amplitude |
Amplitude options (see |
return |
One of |
validate |
Logical; when TRUE, performs basic checks (sampling frame
compatibility, temporal alignment) analogous to |
... |
Reserved for future use. |
This function wraps lss_sbhm() by converting the event_model into an
OASIS design_spec that uses the SBHM basis HRF, and by mapping
baseline_model terms to nuisance regressors for projection.
Same return contract as lss_sbhm().
## Not run: library(fmridesign) sframe <- fmrihrf::sampling_frame(blocklens = c(150, 150), TR = 2) trials <- data.frame(onset = c(10,30,50, 10,30,50), run = rep(1:2, each=3)) emod <- event_model(onset ~ trialwise(basis = "spmg1"), data = trials, block = ~run, sampling_frame = sframe) Y <- matrix(rnorm(300*100), 300, 100) out <- lss_sbhm_design(Y, sbhm, emod) ## End(Not run)## Not run: library(fmridesign) sframe <- fmrihrf::sampling_frame(blocklens = c(150, 150), TR = 2) trials <- data.frame(onset = c(10,30,50, 10,30,50), run = rep(1:2, each=3)) emod <- event_model(onset ~ trialwise(basis = "spmg1"), data = trials, block = ~run, sampling_frame = sframe) Y <- matrix(rnorm(300*100), 300, 100) out <- lss_sbhm_design(Y, sbhm, emod) ## End(Not run)
Computes trial-wise beta estimates using voxel-specific HRFs.
lss_with_hrf( Y, events, hrf_estimates, nuisance_regs = NULL, engine = "R", chunk_size = 5000, verbose = TRUE, backing_dir = NULL )lss_with_hrf( Y, events, hrf_estimates, nuisance_regs = NULL, engine = "R", chunk_size = 5000, verbose = TRUE, backing_dir = NULL )
Y |
Numeric matrix of BOLD data (time x voxels). |
events |
Data frame with |
hrf_estimates |
A VoxelHRF object returned by |
nuisance_regs |
Optional numeric matrix of nuisance regressors. |
engine |
Computational engine: "R" for pure R implementation (default), "C++" for optimized C++ (experimental). |
chunk_size |
Number of voxels to process per batch (C++ engine only). |
verbose |
Logical; display progress bar. |
backing_dir |
Directory for bigmemory backing files. If NULL, a temporary directory is used (C++ engine only). |
An object of class LSSBeta for C++ engine, or a numeric matrix (n_trials x n_vox) for R engine.
## Not run: set.seed(1) Y <- matrix(rnorm(100), 50, 2) events <- data.frame(onset = c(5, 25), duration = 1, condition = "A") basis <- fmrihrf::hrf_gamma() sframe <- fmrihrf::sampling_frame(blocklens = nrow(Y), TR = 1) times <- fmrihrf::samples(sframe, global = TRUE) rset <- fmrihrf::regressor_set(onsets = events$onset, fac = factor(1:nrow(events)), hrf = basis, duration = events$duration, span = 30) X <- fmrihrf::evaluate(rset, grid = times, precision = 0.1, method = "conv") coef <- matrix(rnorm(ncol(X) * ncol(Y)), ncol(X), ncol(Y)) Y <- X %*% coef + Y * 0.1 est <- estimate_voxel_hrf(Y, events, basis) betas <- lss_with_hrf(Y, events, est, verbose = FALSE, engine = "R") dim(betas) ## End(Not run)## Not run: set.seed(1) Y <- matrix(rnorm(100), 50, 2) events <- data.frame(onset = c(5, 25), duration = 1, condition = "A") basis <- fmrihrf::hrf_gamma() sframe <- fmrihrf::sampling_frame(blocklens = nrow(Y), TR = 1) times <- fmrihrf::samples(sframe, global = TRUE) rset <- fmrihrf::regressor_set(onsets = events$onset, fac = factor(1:nrow(events)), hrf = basis, duration = events$duration, span = 30) X <- fmrihrf::evaluate(rset, grid = times, precision = 0.1, method = "conv") coef <- matrix(rnorm(ncol(X) * ncol(Y)), ncol(X), ncol(Y)) Y <- X %*% coef + Y * 0.1 est <- estimate_voxel_hrf(Y, events, basis) betas <- lss_with_hrf(Y, events, est, verbose = FALSE, engine = "R") dim(betas) ## End(Not run)
Simple list-based S3 class returned by lss_with_hrf containing
trial-wise beta estimates.
No value itself. This topic documents the object returned by
lss_with_hrf(..., engine = "C++").
## Not run: Y <- matrix(rnorm(100), 50, 2) events <- data.frame(onset = c(5, 25), duration = 1, condition = "A") basis <- fmrihrf::HRF_SPMG1 est <- estimate_voxel_hrf(Y, events, basis) fit <- lss_with_hrf(Y, events, est, engine = "C++", verbose = FALSE) class(fit) ## End(Not run)## Not run: Y <- matrix(rnorm(100), 50, 2) events <- data.frame(onset = c(5, 25), duration = 1, condition = "A") basis <- fmrihrf::HRF_SPMG1 est <- estimate_voxel_hrf(Y, events, basis) fit <- lss_with_hrf(Y, events, est, engine = "C++", verbose = FALSE) class(fit) ## End(Not run)
Performs expensive matrix computations that don't depend on the response vector, allowing for efficient reuse across multiple voxels.
mixed_precompute(X, Z, K = NULL)mixed_precompute(X, Z, K = NULL)
X |
Fixed effects design matrix (n × p) |
Z |
Random effects design matrix (n × q) |
K |
Kinship/covariance matrix for random effects (q × q) |
Workspace object for use with mixed_solve_optimized
X <- matrix(1, 6, 1) Z <- diag(6) ws <- mixed_precompute(X, Z) names(ws)X <- matrix(1, 6, 1) Z <- diag(6) ws <- mixed_precompute(X, Z) names(ws)
Solves mixed models with random effects using REML or ML estimation. This function provides a unified interface to mixed model estimation, similar to the lss/lsa functions in this package.
mixed_solve( Y, X = NULL, Z = NULL, K = NULL, Nuisance = NULL, method = c("REML", "ML"), bounds = c(1e-09, 1e+09), SE = FALSE, return_Hinv = FALSE ) mixed_solve_cpp( Y, X = NULL, Z = NULL, K = NULL, Nuisance = NULL, method = c("REML", "ML"), bounds = c(1e-09, 1e+09), SE = FALSE, return_Hinv = FALSE )mixed_solve( Y, X = NULL, Z = NULL, K = NULL, Nuisance = NULL, method = c("REML", "ML"), bounds = c(1e-09, 1e+09), SE = FALSE, return_Hinv = FALSE ) mixed_solve_cpp( Y, X = NULL, Z = NULL, K = NULL, Nuisance = NULL, method = c("REML", "ML"), bounds = c(1e-09, 1e+09), SE = FALSE, return_Hinv = FALSE )
Y |
Response vector or matrix. If a matrix, each column is treated as a separate response variable. |
X |
Design matrix for fixed effects. If NULL, defaults to intercept only. |
Z |
Design matrix for random effects. If NULL, defaults to identity matrix. |
K |
Kinship matrix for random effects. If NULL, defaults to identity matrix. |
Nuisance |
An alias for X, provided for consistency with lss/lsa interface. If both X and Nuisance are provided, X takes precedence. |
method |
Character string specifying the estimation method:
|
bounds |
Numeric vector of length 2 specifying bounds for variance component optimization. Defaults to c(1e-9, 1e9). |
SE |
Logical, whether to compute and return standard errors. Defaults to FALSE. |
return_Hinv |
Logical, whether to return the inverse of the H matrix. Defaults to FALSE. |
This function fits the mixed model: Y = Xbeta + Zu + error, where u ~ N(0, VuK) and error ~ N(0, VeI). The variance components Vu and Ve are estimated using REML or ML.
A list containing:
Vu |
Estimated variance component for random effects. |
Ve |
Estimated variance component for residuals. |
beta |
Estimated fixed effects coefficients. |
u |
Estimated random effects coefficients. |
LL |
Log-likelihood of the model. |
beta.SE |
Standard errors of fixed effects coefficients (if SE = TRUE). |
u.SE |
Standard errors of random effects coefficients (if SE = TRUE). |
Hinv |
Inverse of H matrix (if return_Hinv = TRUE). |
## Not run: set.seed(123) n <- 100 Y <- rnorm(n) Z <- matrix(rnorm(n * 5), n, 5) K <- diag(5) X <- matrix(1, n, 1) result <- mixed_solve(Y, X, Z, K) ## End(Not run)## Not run: set.seed(123) n <- 100 Y <- rnorm(n) Z <- matrix(rnorm(n * 5), n, 5) K <- diag(5) X <- matrix(1, n, 1) result <- mixed_solve(Y, X, Z, K) ## End(Not run)
An optimized implementation of mixed model estimation that precomputes expensive matrix operations and can be reused across multiple voxels for significant performance improvements.
mixed_solve_optimized( X, Z, Y, K = NULL, workspace = NULL, compute_se = FALSE, n_threads = 0 )mixed_solve_optimized( X, Z, Y, K = NULL, workspace = NULL, compute_se = FALSE, n_threads = 0 )
X |
Fixed effects design matrix (n × p) |
Z |
Random effects design matrix (n × q) |
Y |
Response data - can be a vector (single voxel) or matrix (n × V for multiple voxels) |
K |
Kinship/covariance matrix for random effects (q × q). Defaults to identity. |
workspace |
Precomputed workspace (optional, will compute if NULL) |
compute_se |
Whether to compute standard errors (default: FALSE) |
n_threads |
Number of OpenMP threads for multi-voxel (0 = auto) |
List with estimated parameters and variance components
set.seed(1) X <- matrix(1, 6, 1) Z <- diag(6) Y <- matrix(rnorm(12), 6, 2) ws <- mixed_precompute(X, Z) fit <- mixed_solve_optimized(X, Z, Y, workspace = ws) names(fit)set.seed(1) X <- matrix(1, 6, 1) Z <- diag(6) Y <- matrix(rnorm(12), 6, 2) ws <- mixed_precompute(X, Z) fit <- mixed_solve_optimized(X, Z, Y, workspace = ws) names(fit)
Convenience constructor for the oasis= list accepted by lss(method="oasis").
Unknown fields are allowed via ... for forward compatibility.
oasis_options( design_spec = NULL, K = NULL, ridge_mode = c("fractional", "absolute"), ridge_x = 0.05, ridge_b = 0.05, block_cols = 4096L, return_se = FALSE, return_diag = FALSE, add_intercept = TRUE, hrf_mode = NULL, ... )oasis_options( design_spec = NULL, K = NULL, ridge_mode = c("fractional", "absolute"), ridge_x = 0.05, ridge_b = 0.05, block_cols = 4096L, return_se = FALSE, return_diag = FALSE, add_intercept = TRUE, hrf_mode = NULL, ... )
design_spec |
Optional design spec list used to build |
K |
Optional basis dimension override. |
ridge_mode |
|
ridge_x, ridge_b
|
Non-negative ridge penalties (defaults 0.05 each). |
block_cols |
Voxel block size for blocked products. |
return_se |
Logical; return standard errors. |
return_diag |
Logical; return diagnostics. |
add_intercept |
Logical; add intercept when |
hrf_mode |
Optional mode (e.g. |
... |
Additional options. |
A list with class "fmrilss_oasis_options".
oasis_options(ridge_mode = "fractional", ridge_x = 0.1, ridge_b = 0.1)oasis_options(ridge_mode = "fractional", ridge_x = 0.1, ridge_b = 0.1)
Creates visualization comparing true vs recovered HRFs
plot_hrf_comparison(results, save_path = NULL)plot_hrf_comparison(results, save_path = NULL)
results |
Output from compare_hrf_recovery |
save_path |
Optional path to save plot |
A ggplot2 plot object. When save_path is supplied, the same plot
is also written to disk.
## Not run: onsets <- generate_rapid_design(n_events = 4, total_time = 60, seed = 1) sim <- generate_lwu_data(onsets, total_time = 60, n_voxels = 2, seed = 1) grid <- create_lwu_grid(n_tau = 2, n_sigma = 2, n_rho = 2) res <- compare_hrf_recovery(sim, hrf_grid = grid) plot_hrf_comparison(res) ## End(Not run)## Not run: onsets <- generate_rapid_design(n_events = 4, total_time = 60, seed = 1) sim <- generate_lwu_data(onsets, total_time = 60, n_voxels = 2, seed = 1) grid <- create_lwu_grid(n_tau = 2, n_sigma = 2, n_rho = 2) res <- compare_hrf_recovery(sim, hrf_grid = grid) plot_hrf_comparison(res) ## End(Not run)
Convenience constructor for the prewhiten= list accepted by lss().
prewhiten_options( method = c("none", "ar", "arma"), p = "auto", q = 0L, p_max = 6L, pooling = c("global", "voxel", "run", "parcel"), runs = NULL, parcels = NULL, exact_first = c("ar1", "none"), compute_residuals = TRUE )prewhiten_options( method = c("none", "ar", "arma"), p = "auto", q = 0L, p_max = 6L, pooling = c("global", "voxel", "run", "parcel"), runs = NULL, parcels = NULL, exact_first = c("ar1", "none"), compute_residuals = TRUE )
method |
|
p |
AR order or |
q |
MA order for ARMA. |
p_max |
Maximum AR order when |
pooling |
|
runs |
Optional run identifiers. |
parcels |
Optional parcel ids. |
exact_first |
|
compute_residuals |
Logical. |
A list with class "fmrilss_prewhiten_options".
prewhiten_options(method = "ar", p = 1, pooling = "run")prewhiten_options(method = "ar", p = 1, pooling = "run")
Computes the orthogonal projection matrix Q = I - X(X'X)^(-1)X' that projects out the space spanned by confound regressors X. This is useful for advanced users who want to cache and reuse projection matrices.
project_confounds(X)project_confounds(X)
X |
Confound design matrix (n x p) where n is number of timepoints and p is number of confound regressors |
This function uses QR decomposition for numerical stability instead of computing the Moore-Penrose pseudoinverse directly. The resulting matrix Q can be applied to data to remove the influence of confound regressors.
Projection matrix Q (n x n) that projects out the column space of X
## Not run: n <- 100 X_confounds <- cbind(1, 1:n) Q <- project_confounds(X_confounds) Y_clean <- Q %*% Y_raw ## End(Not run)## Not run: n <- 100 X_confounds <- cbind(1, 1:n) Q <- project_confounds(X_confounds) Y_clean <- Q %*% Y_raw ## End(Not run)
Fast C++ implementation for projecting out confound variables from data and trial design matrices. This uses Cholesky decomposition for numerical stability and avoids creating large projection matrices.
project_confounds_cpp(X_confounds, Y_data, C_trials)project_confounds_cpp(X_confounds, Y_data, C_trials)
X_confounds |
Confound design matrix (n x k) |
Y_data |
Data matrix (n x V) where V is number of voxels |
C_trials |
Trial design matrix (n x T) where T is number of trials |
This function computes residuals Y - X(X'X)^(-1)X'Y and C - X(X'X)^(-1)X'C without explicitly forming the projection matrix Q = I - X(X'X)^(-1)X'. This approach uses ~100x less memory for large n and is numerically more stable.
List with projected data (residual_data) and projected trials (Q_dmat_ran)
## Not run: n <- 200; k <- 5; V <- 1000; T <- 50 X_confounds <- cbind(1, 1:n, rnorm(n*3)) Y_data <- matrix(rnorm(n*V), n, V) C_trials <- matrix(rnorm(n*T), n, T) result <- project_confounds_cpp(X_confounds, Y_data, C_trials) ## End(Not run)## Not run: n <- 200; k <- 5; V <- 1000; T <- 50 X_confounds <- cbind(1, 1:n, rnorm(n*3)) Y_data <- matrix(rnorm(n*V), n, V) C_trials <- matrix(rnorm(n*T), n, T) result <- project_confounds_cpp(X_confounds, Y_data, C_trials) ## End(Not run)
Learn a low-rank shared time basis from a parameterized HRF library using
fmrihrf::hrf_library(). The library is evaluated on the TR grid, optionally
baseline-removed and L2-normalized, and decomposed via SVD into
B = U_r (shared basis), singular values S, and library coordinates
A = diag(S) %*% t(V_r).
sbhm_build( library_spec = NULL, library_H = NULL, r = 6, sframe = NULL, tgrid = NULL, span = 32, normalize = TRUE, baseline = c(0, 0.5), shifts = NULL, ref = c("mean", "spmg1") )sbhm_build( library_spec = NULL, library_H = NULL, r = 6, sframe = NULL, tgrid = NULL, span = 32, normalize = TRUE, baseline = c(0, 0.5), shifts = NULL, ref = c("mean", "spmg1") )
library_spec |
Either NULL (when
|
library_H |
Optional precomputed TxK matrix of candidate HRFs, already
aligned to the TR grid |
r |
Target rank for the shared basis (default 6). Clipped to |
sframe |
Optional |
tgrid |
Optional numeric vector of global times (in seconds). If provided,
takes precedence over |
span |
HRF span in seconds (default 32). Used for reference HRF when needed. |
normalize |
Logical, L2-normalize library columns (default TRUE). |
baseline |
Numeric length-2 vector specifying a time window (in seconds) used for baseline removal (column-wise mean subtraction within this window). Set to NULL to skip. |
shifts |
Optional numeric vector of time shifts (in seconds). When provided,
shifted copies of the library are added by linear interpolation on |
ref |
Reference for coefficient-space shrinkage and orientation.
One of |
A list with components:
B (Txr): shared orthonormal time basis
S (length r): singular values
A (rxK): coordinates of library HRFs in the shared basis
tgrid: the global time grid used (seconds)
span: span used for reference HRF
ref: list with alpha_ref (length r) and name
meta: list with r, K, normalize, baseline
## Not run: library(fmrihrf) param_grid <- expand.grid(shape = c(6, 8, 10), rate = c(0.9, 1.0, 1.1)) gamma_fun <- function(shape, rate) fmrihrf::as_hrf( fmrihrf::hrf_gamma, params = list(shape = shape, rate = rate) ) sframe <- fmrihrf::sampling_frame(blocklens = 200, TR = 1) sbhm <- sbhm_build( library_spec = list(fun = gamma_fun, pgrid = param_grid, span = 32), r = 6, sframe = sframe, baseline = c(0, 0.5) ) hrf_B <- sbhm_hrf(sbhm$B, sbhm$tgrid, sbhm$span) ## End(Not run)## Not run: library(fmrihrf) param_grid <- expand.grid(shape = c(6, 8, 10), rate = c(0.9, 1.0, 1.1)) gamma_fun <- function(shape, rate) fmrihrf::as_hrf( fmrihrf::hrf_gamma, params = list(shape = shape, rate = rate) ) sframe <- fmrihrf::sampling_frame(blocklens = 200, TR = 1) sbhm <- sbhm_build( library_spec = list(fun = gamma_fun, pgrid = param_grid, span = 32), r = 6, sframe = sframe, baseline = c(0, 0.5) ) hrf_B <- sbhm_hrf(sbhm$B, sbhm$tgrid, sbhm$span) ## End(Not run)
Convert a shared time basis matrix B (T×r) into an fmrihrf::HRF object
so it can be used directly in OASIS design construction. The HRF returns the
r basis columns evaluated at arbitrary times by piecewise-linear
interpolation on the provided time grid.
sbhm_hrf(B, tgrid, span)sbhm_hrf(B, tgrid, span)
B |
A numeric matrix (T×r) with orthonormal columns (shared basis). |
tgrid |
Numeric vector of length T giving the global times (seconds)
corresponding to the rows of |
span |
Numeric HRF span passed to |
An fmrihrf::HRF object with nbasis = ncol(B).
## Not run: hrf_B <- sbhm_hrf(sbhm$B, sbhm$tgrid, sbhm$span) ## End(Not run)## Not run: hrf_B <- sbhm_hrf(sbhm$B, sbhm$tgrid, sbhm$span) ## End(Not run)
Given per-voxel aggregate coefficients beta_bar in the shared basis B,
and library coordinates A, perform shape-only matching in a whitened,
L2-normalized coefficient space (cosine similarity). Optionally apply a
simple shrinkage of beta_bar towards a reference coordinate before
matching, and an orientation fix.
sbhm_match( beta_bar, S, A, shrink = list(tau = 0, ref = NULL, snr = NULL), topK = 1, whiten = TRUE, sv_floor_rel = 1e-06, whiten_power = 1, orient_ref = TRUE )sbhm_match( beta_bar, S, A, shrink = list(tau = 0, ref = NULL, snr = NULL), topK = 1, whiten = TRUE, sv_floor_rel = 1e-06, whiten_power = 1, orient_ref = TRUE )
beta_bar |
Numeric matrix (r×V): per-voxel coefficients from a prepass GLM in the SBHM basis. Columns correspond to voxels. |
S |
Numeric vector (length r): singular values of the library SVD. |
A |
Numeric matrix (r×K): coordinates of library HRFs in SBHM basis. |
shrink |
List with optional shrinkage options:
|
topK |
Integer, return top-K scores/weights if >1 (default 1). |
whiten |
Logical, divide coefficients by S before normalization (default TRUE). |
sv_floor_rel |
Relative singular-value floor used when |
whiten_power |
Numeric in |
orient_ref |
Logical, flip beta_bar columns when their dot with |
A list with:
idx length-V integer indices of best-matching library HRF (1..K)
margin length-V numeric: score(top1) - score(top2)
alpha_hat r×V matrix: the selected library coordinates (unwhitened, unnormalized)
scores optional K×V cosine score matrix (returned when topK > 1)
weights optional top-K weights per voxel (when topK > 1)
## Not run: set.seed(42) r <- 4; K <- 12; V <- 3 A <- matrix(rnorm(r*K), r, K) S <- seq(1, 0.2, length.out = r) alpha2 <- A[,2] beta_bar <- cbind(alpha2 + rnorm(r, sd = 0.1), A[,7] + rnorm(r, sd = 0.1), A[,10] + rnorm(r, sd = 0.1)) m <- sbhm_match(beta_bar, S, A) m$idx ## End(Not run)## Not run: set.seed(42) r <- 4; K <- 12; V <- 3 A <- matrix(rnorm(r*K), r, K) S <- seq(1, 0.2, length.out = r) alpha2 <- A[,2] beta_bar <- cbind(alpha2 + rnorm(r, sd = 0.1), A[,7] + rnorm(r, sd = 0.1), A[,10] + rnorm(r, sd = 0.1)) m <- sbhm_match(beta_bar, S, A) m$idx ## End(Not run)
Compute per-voxel coefficients in the shared SBHM basis by fitting a single
aggregate GLM with one regressor per basis column (trials summed), optionally
residualizing by nuisances and prewhitening. This produces beta_bar (r×V)
that you can feed to sbhm_match().
sbhm_prepass( Y, sbhm, design_spec, Nuisance = NULL, prewhiten = NULL, ridge = list(mode = "fractional", lambda = 0.01, alpha_ref = NULL), data_fac = NULL )sbhm_prepass( Y, sbhm, design_spec, Nuisance = NULL, prewhiten = NULL, ridge = list(mode = "fractional", lambda = 0.01, alpha_ref = NULL), data_fac = NULL )
Y |
Numeric matrix T×V of fMRI time series. |
sbhm |
SBHM object from |
design_spec |
List describing events (same shape as |
Nuisance |
Optional T×P nuisance matrix (motion, drift, etc.). |
prewhiten |
Optional fmriAR prewhitening options (see |
ridge |
Optional list for targeted ridge shrinkage in the prepass solve:
|
data_fac |
Optional list for external factorization: |
Notes:
Aggregated per-basis regressors can be highly collinear, making G = A' A
ill-conditioned. A small ridge is recommended for stability. The default
uses fractional mode with lambda = 0.01 (scaled by mean(diag(G))).
When data_fac is provided (factorized data path), prewhitening is skipped
in this version; both dense and factorized paths perform nuisance
residualization consistently.
List with:
beta_bar r×V aggregate coefficients
A_agg T×r aggregated per-basis design (after any residualization/whitening)
G r×r crossprod of A_agg
diag list with K=r, ntrials, times, used_prewhiten
## Not run: library(fmrihrf) set.seed(1) Tlen <- 120; V <- 5; r <- 4 sframe <- sampling_frame(blocklens = Tlen, TR = 1) H <- cbind(exp(-seq(0, 30, length.out = Tlen)/4), exp(-seq(0, 30, length.out = Tlen)/6)) sbhm <- sbhm_build(library_H = H, r = r, sframe = sframe, normalize = TRUE) onsets <- seq(5, 95, by = 10) design_spec <- list(sframe = sframe, cond = list(onsets = onsets, duration = 0, span = 30)) hrf_B <- sbhm_hrf(sbhm$B, sbhm$tgrid, sbhm$span) rr <- fmrihrf::regressor(onsets = onsets, hrf = hrf_B, duration = 0, span = 30, summate = FALSE) X <- fmrihrf::evaluate(rr, grid = sbhm$tgrid, precision = 0.1, method = "conv") betas_true <- matrix(rnorm(r), r) Y <- matrix(rnorm(Tlen*V, sd = 0.5), Tlen, V) Y[,1] <- Y[,1] + X %*% betas_true pre <- sbhm_prepass(Y, sbhm, design_spec) str(pre) ## End(Not run)## Not run: library(fmrihrf) set.seed(1) Tlen <- 120; V <- 5; r <- 4 sframe <- sampling_frame(blocklens = Tlen, TR = 1) H <- cbind(exp(-seq(0, 30, length.out = Tlen)/4), exp(-seq(0, 30, length.out = Tlen)/6)) sbhm <- sbhm_build(library_H = H, r = r, sframe = sframe, normalize = TRUE) onsets <- seq(5, 95, by = 10) design_spec <- list(sframe = sframe, cond = list(onsets = onsets, duration = 0, span = 30)) hrf_B <- sbhm_hrf(sbhm$B, sbhm$tgrid, sbhm$span) rr <- fmrihrf::regressor(onsets = onsets, hrf = hrf_B, duration = 0, span = 30, summate = FALSE) X <- fmrihrf::evaluate(rr, grid = sbhm$tgrid, precision = 0.1, method = "conv") betas_true <- matrix(rnorm(r), r) Y <- matrix(rnorm(Tlen*V, sd = 0.5), Tlen, V) Y[,1] <- Y[,1] + X %*% betas_true pre <- sbhm_prepass(Y, sbhm, design_spec) str(pre) ## End(Not run)
Given trial-wise coefficients in the shared basis (rxntrialsxV) and the
voxel-specific matched library coordinates alpha_hat (rxV), compute scalar
amplitudes per trial and voxel via least-squares projection:
a = (alpha' beta) / (alpha' alpha).
sbhm_project(beta_rt, alpha_hat)sbhm_project(beta_rt, alpha_hat)
beta_rt |
3D array of shape r x ntrials x V containing per-trial coefficients in the SBHM basis (as returned by OASIS with K=r, reshaped). |
alpha_hat |
Numeric matrix rxV of matched library coordinates per voxel
(e.g., |
Numeric matrix ntrials x V of scalar amplitudes.
## Not run: r <- nrow(alpha_hat) ntrials <- nrow(beta_mat) / r beta_rt <- array(beta_mat, dim = c(r, ntrials, ncol(beta_mat))) amps <- sbhm_project(beta_rt, alpha_hat) ## End(Not run)## Not run: r <- nrow(alpha_hat) ntrials <- nrow(beta_mat) / r beta_rt <- array(beta_mat, dim = c(r, ntrials, ncol(beta_mat))) amps <- sbhm_project(beta_rt, alpha_hat) ## End(Not run)
Convenience constructor for the stglmnet= list accepted by
lss(method = "stglmnet").
Unknown fields are allowed via ... for forward compatibility.
stglmnet_options( mode = c("cv", "fixed"), alpha = 0.2, lambda = NULL, overlap_strategy = c("none", "multiplicative", "additive", "hybrid", "threshold"), pool_to_mean = FALSE, pool_strength = 1, pool_mean_penalty = 0, whiten = c("inherit", "auto", "never", "always"), cv_folds = 5L, cv_type.measure = c("auto", "mse", "correlation", "reliability", "composite"), cv_select = c("optimal", "1se"), return_fit = FALSE, ... )stglmnet_options( mode = c("cv", "fixed"), alpha = 0.2, lambda = NULL, overlap_strategy = c("none", "multiplicative", "additive", "hybrid", "threshold"), pool_to_mean = FALSE, pool_strength = 1, pool_mean_penalty = 0, whiten = c("inherit", "auto", "never", "always"), cv_folds = 5L, cv_type.measure = c("auto", "mse", "correlation", "reliability", "composite"), cv_select = c("optimal", "1se"), return_fit = FALSE, ... )
mode |
|
alpha |
Elastic-net mixing parameter passed to |
lambda |
Optional lambda sequence (or scalar in fixed mode). |
overlap_strategy |
Trial-overlap penalty mapping. One of |
pool_to_mean |
Logical; reparameterize trial effects into a pooled mean plus orthogonal contrasts. |
pool_strength |
Penalty multiplier applied to pooled contrasts. |
pool_mean_penalty |
Penalty applied to the pooled mean coefficient. |
whiten |
One of |
cv_folds |
Number of folds used when |
cv_type.measure |
Cross-validation objective. |
cv_select |
Lambda selection rule in CV mode. |
return_fit |
Logical; when |
... |
Additional backend options. |
A list with class "fmrilss_stglmnet_options".
stglmnet_options(mode = "fixed", lambda = 0.05, alpha = 0.5)stglmnet_options(mode = "fixed", lambda = 0.05, alpha = 0.5)
Simple list-based S3 class returned by estimate_voxel_hrf containing
voxel-wise HRF basis coefficients and related metadata.
No value itself. This topic documents the structure returned by
estimate_voxel_hrf().
## Not run: Y <- matrix(rnorm(100), 50, 2) events <- data.frame(onset = c(5, 25), duration = 1, condition = "A") basis <- fmrihrf::HRF_SPMG1 est <- estimate_voxel_hrf(Y, events, basis) class(est) ## End(Not run)## Not run: Y <- matrix(rnorm(100), 50, 2) events <- data.frame(onset = c(5, 25), duration = 1, condition = "A") basis <- fmrihrf::HRF_SPMG1 est <- estimate_voxel_hrf(Y, events, basis) class(est) ## End(Not run)