Per-gene centering and scaling to unit variance, matching Seurat's
ScaleData: (x - rowMeans) / rowSds, then clamp to
[-Inf, max_value] (Seurat clips at +10 by default). The dominant cost
— elementwise subtract/divide/clamp over the full dense matrix — runs on the
GPU; the per-gene mean and sd are cheap row reductions.
.ggmlr_scale_gpu(
mat,
max_value = 10,
backend = c("vulkan", "cpu"),
scale_backend = c("cpu", "vulkan"),
chunk_size = NULL
)A ggml_result whose embedding is the scaled
features x cells matrix; metadata$kind = "transform",
metadata$layer = "scale.data".
Dense numeric matrix, features x cells (log-normalised data).
Upper clip after scaling (default 10; Seurat's default).
"vulkan" or "cpu" (dispatch resolves "auto").
Which backend actually runs the z-score: "cpu"
(default) or "vulkan". Defaults to CPU even under
backend = "vulkan", because ScaleData is a memory-bound elementwise
O(nnz) pass (centre / divide / clamp) with almost no arithmetic per element:
the GPU pays for the host<->VRAM copy but has nothing to accelerate, so it is
slower than the CPU here (measured ~0.4x). Same rationale and pattern as
UMAP's sgd_backend. Pass "vulkan" to force the GPU path. Note
that a sparse (dgCMatrix) input is always streamed in cell-blocks on
the CPU (see below), so scale_backend = "vulkan" only takes effect
for a dense mat.