| Title: | Optional Compiled Kernels for the Giotto Suite |
|---|---|
| Description: | Small compiled compute kernels for the Giotto suite. Each takes plain vectors or an Arrow C stream and returns plain vectors, and knows nothing about Giotto's classes. Every kernel is optional: the calling package checks has_kernel() and otherwise runs its own R implementation, so installing this package only changes speed, never results beyond floating-point rounding. |
| Authors: | George Chen [aut, cre] |
| Maintainer: | George Chen <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.2.0 |
| Built: | 2026-09-28 22:44:03 UTC |
| Source: | https://github.com/giotto-suite/GiottoKernels |
Computes G = sum_c x_c x_c^T and s = sum_c x_c over cells c, where
x_c is cell c's feature vector, reading sparse (row_id, col_id, value)
triplets from an Arrow stream: row = cell, col = feature. Only nonzero pairs
within a cell are multiplied, so the cost is the sum over cells of
nnz_c^2 rather than n_cells * n_features^2, and memory stays at one
batch plus the n_features x n_features result.
gram_stream(stream, n_features, n_threads = NULL)gram_stream(stream, n_features, n_threads = NULL)
stream |
anything |
n_features |
number of features |
n_threads |
worker threads, resolved by |
This is the first pass of Gram-eigen PCA: centering, scaling and the
eigendecomposition follow from G, s and the cell count.
a list with G, the n_features x n_features symmetric Gram
matrix, and s, the length-n_features feature sums.
Batch boundaries may fall anywhere, including inside a cell. What is
required is that no cell interior to one batch also appears in another.
A stream sorted by row_id always satisfies this. Two checks enforce it,
and the call errors rather than returning a wrong result if either fails:
each batch must be sorted by row_id, and batches' row_id ranges may
overlap only at one shared edge cell.
Features within a cell need not be sorted, but each (row_id, col_id) pair
must appear at most once, as in any sparse store: a repeated pair is not
summed first, so its diagonal term would be wrong.
tri <- data.frame( row_id = c(1L, 1L, 2L), col_id = c(1L, 2L, 2L), value = c(1, 2, 3) ) gram_stream(tri, n_features = 2L)tri <- data.frame( row_id = c(1L, 1L, 2L), col_id = c(1L, 2L, 2L), value = c(1, 2, 3) ) gram_stream(tri, n_features = 2L)
Checks the kernels compiled into the installed package, so a caller can fall back on an older install instead of calling a kernel it does not have:
has_kernel(name)has_kernel(name)
name |
kernel name, e.g. |
use_kernel <- requireNamespace("GiottoKernels", quietly = TRUE) &&
GiottoKernels::has_kernel("gram_stream")
TRUE or FALSE.
has_kernel("gram_stream")has_kernel("gram_stream")
An explicit n_threads is used as given. NULL falls back to
options(gkernels.n_threads), and to 1 when that is unset, so a kernel runs
single-threaded unless the caller or the session asks for more.
kernel_threads(n_threads = NULL)kernel_threads(n_threads = NULL)
n_threads |
|
a positive integer.
kernel_threads() # 1, or options(gkernels.n_threads) kernel_threads(4) # 4kernel_threads() # 1, or options(gkernels.n_threads) kernel_threads(4) # 4
Computes A %*% V, where A is the n_rows x nrow(V) sparse matrix encoded
by (row_id, col_id, value) triplets in an Arrow stream (row = cell, col =
feature) and V is a dense matrix, one column per component. Only stored
entries are touched, so the cost is nnz * ncol(V), and memory is one batch
plus the n_rows x ncol(V) result.
project_stream(stream, V, n_rows, n_threads = NULL)project_stream(stream, V, n_rows, n_threads = NULL)
stream |
anything |
V |
numeric matrix, one row per feature. |
n_rows |
number of rows in the result; rows with no stored values are 0. |
n_threads |
worker threads, resolved by |
This is the second pass of Gram-eigen PCA: cell coordinates from the
loadings. Centering is left to the caller, since it is one subtraction per
component: (A - 1 mu^T) V = A V - 1 (mu^T V).
the n_rows x ncol(V) matrix A %*% V.
The same as gram_stream(): batch boundaries may fall anywhere, but no cell
interior to one batch may also appear in another, which a stream sorted by
row_id always satisfies. The call checks this and errors rather than
return a wrong result. Each (row_id, col_id) pair must appear at most once.
Every output row is built by one thread from its entries in stream order, so the result is bit-identical at any thread count.
tri <- data.frame( row_id = c(1L, 1L, 3L), col_id = c(1L, 2L, 2L), value = c(1, 2, 3) ) project_stream(tri, V = diag(2), n_rows = 3L)tri <- data.frame( row_id = c(1L, 1L, 3L), col_id = c(1L, 2L, 2L), value = c(1, 2, 3) ) project_stream(tri, V = diag(2), n_rows = 3L)