O(N log N)
or optimized O(N*L*K)
) compared to R loops or less optimized filtering.Matrix::bandSparse
is suitable for efficient sparse representation.+amp
at onset, -amp
at offset using cumsum(h_tr)*TR
) correctly handles non-zero durations.T_b
) for each basis (h_b
) and convolving the same impulse data (S
) is correct.engine
argument to event_model
for internal dispatching is a clean approach, maintaining API stability.term_spans
, col_indices
attributes) with the classic engine.R/engine_toeplitz.R
toeplitz_engine
function:toeplitz_engine(terms, sampling_frame, precision)
sampling_frame
.k
, N_tr
scans, TR
):block_dm_parts = list()
.term
in terms
:hrf_obj = attr(term, "hrfspec")$hrf
.nb = nbasis(hrf_obj)
.S
: (N_tr
rows, N_cond
columns) for this term
within this block.design_matrix(term, drop.empty=FALSE)
for the unconvolved amplitudes per condition, restricted to the current block's events.onsets
, durations
, blockids
for the current block.onsets
to TR indices (idx_on = floor(global_onset / TR) + 1
).idx_off = floor((global_onset + duration) / TR) + 1
). Create sparse matrix S
using Matrix::sparseMatrix
with +amp
at idx_on
and -amp
at idx_off
for each condition column. Clamp indices within 1:N_tr
.term_basis_parts = list()
.b
from 1 to nb
):h_b
: Evaluate hrf_obj$basis[[b]]
at fine precision
.h_b
to TR resolution (h_b_tr
). Determine kernel length L
.k_eff
: Use cumsum(h_b_tr) * TR
if durations were used, otherwise use h_b_tr
.T_b = Matrix::bandSparse(N_tr, N_tr, k = 0:(L-1), diagonals = list_of_diagonals_from(k_eff))
.X_b = as.matrix(T_b %*% S)
. (N_tr
x N_cond
).X_b
in term_basis_parts
.term_dm_block = do.call(cbind, term_basis_parts)
.term_dm_block
matching the classic engine (using conditions(term)
, term name prefix, _b
if nb > 1
, make.names(unique=TRUE)
).term_dm_block
(as tibble) in block_dm_parts
, using the unique term name as the key.block_dm = do.call(cbind, block_dm_parts)
.block_dm
for all blocks in final_dm_list
. Use dplyr::bind_rows(final_dm_list)
to create final_dm
.term_spans
and col_indices
attributes to final_dm
, matching the structure based on attr(terms, "interface")
.final_dm
.event_model
(R/event_model.R
):engine = c("classic", "toeplitz")
argument (default "classic"
).engine <- match.arg(engine)
.if (engine == "toeplitz") toeplitz_engine(...) else build_event_model_design_matrix(...)
.event_model
docs for engine
. Add internal docs for toeplitz_engine
.testthat
tests in tests/testthat/test_engine_toeplitz.R
comparing outputs and benchmarking.R/engine_toeplitz.R
.toeplitz_engine
structure (signature, block loop).S
generation within toeplitz_engine
, handling term-specific conditions and block subsetting.cumsum
trick) for impulse matrix S
.h_b_tr
).T_b
) using Matrix::bandSparse
and the effective kernel.T_b %*% S
and basis combination loop.toeplitz_engine
to exactly match the classic engine's output.dplyr::bind_rows
.term_spans
and col_indices
attributes.event_model
in R/event_model.R
to add the engine
argument and the internal dispatch logic.tests/testthat/test_engine_toeplitz.R
, comparing numerical outputs against the classic engine.engine
argument in event_model
.toeplitz_engine
for potential further speedup, perhaps activated by a sub-option engine = "toeplitz-fft"
. Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.