This guide takes you from an ordinary R matrix to PCA and nearest-neighbour results computed with CUDA. You stay in R, use familiar data structures, and do not need a deep-learning framework.
Before opening R, make sure you have:
Follow the GPU setup guide first if
nvidia-smi does not show a GPU or the diagnostic call below
reports missing libraries.
# install.packages("pak")
pak::pak("cudaverse/cudaverse@v0.4.1")
library(cudaverse)
check <- cuda_diagnostics()
check$summary
check$next_steps
# Stop here with a useful explanation if CUDA is not ready.
cuda_select_device("cuda")If the last command succeeds, cudaverse is ready to use the GPU. If it fails, follow the printed next step; the requested CUDA task is never silently run somewhere else.
This is the most useful first workflow. A numeric R matrix enters the package, PCA reduces it to 20 components, and exact kNN finds 15 neighbours per row.
set.seed(1)
x <- matrix(rnorm(10000 * 100), nrow = 10000, ncol = 100)
pca <- cuda_pca(
x,
n_components = 20,
center = TRUE,
scale. = FALSE,
device = "cuda"
)
neighbors <- cuda_knn(
pca$x,
k = 15,
metric = "euclidean",
device = "cuda"
)
dim(pca$x)
dim(neighbors$index)
head(neighbors$index)
head(neighbors$distance)The PCA scores can stay on the GPU while kNN uses them. You can confirm where the work ran:
Use a cudatensor when you want several matrix operations
to reuse the same data on the GPU. to_cpu() brings the
final result back as an ordinary R object.
x_gpu <- cuda_tensor(x, device = "cuda", dtype = "float32")
centered_gpu <- x_gpu - tensor_mean(x_gpu, dim = 1)
gram_gpu <- tensor_matmul(t(centered_gpu), centered_gpu)
column_totals_gpu <- tensor_sum(x_gpu, dim = 1)
tensor_device(gram_gpu)
gram <- to_cpu(gram_gpu)
column_totals <- to_cpu(column_totals_gpu)Subsetting, transpose, arithmetic, matrix multiplication, and summaries follow familiar R conventions while supported results remain on the GPU.
Sparse inputs begin as Matrix objects. The same workflow
can normalize the matrix, run PCA, and find neighbours without making
the user learn another API.
counts <- Matrix::rsparsematrix(10000, 100, density = 0.03)
counts@x <- abs(counts@x)
counts_gpu <- cuda_sparse(counts, device = "cuda")
normalized_gpu <- sparse_normalize(
counts_gpu,
margin = "rows",
scale_factor = 10000,
log1p = TRUE
)
sparse_pca <- cuda_pca(
normalized_gpu,
n_components = 20,
device = "cuda"
)
sparse_neighbors <- cuda_knn(
sparse_pca$x,
k = 15,
device = "cuda"
)
sparse_info(normalized_gpu)
cuda_provenance(sparse_neighbors)The same strict device argument applies to decompositions, distances, and k-means:
svd_fit <- cuda_svd(x, nu = 20, nv = 20, device = "cuda")
distances <- cuda_distance(
x[1:1000, ],
x[1001:2000, ],
batch_size = 256,
device = "cuda"
)
clusters <- cuda_kmeans(
pca$x,
centers = 20,
seed = 1,
device = "cuda"
)cuda_distance() returns a complete dense matrix, so its
host output grows as nrow(x) * nrow(y). Prefer
cuda_knn() when only the nearest neighbours are needed.
torch
backend.