---
title: "Your first CUDA workflow with cudaverse"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Your first CUDA workflow with cudaverse}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE)
```

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 you start

Before opening R, make sure you have:

1. Windows or Linux;
2. a CUDA-capable NVIDIA GPU and working NVIDIA driver;
3. a CUDA 12.x installation from NVIDIA; and
4. the current cudaverse release.

Follow the [GPU setup guide](gpu-setup.html) first if `nvidia-smi` does not
show a GPU or the diagnostic call below reports missing libraries.

```{r install}
# 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.

## PCA and exact nearest neighbours on CUDA

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.

```{r dense-pca-knn}
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:

```{r dense-provenance}
cuda_provenance(pca)
cuda_provenance(neighbors)
cuda_memory_info("cuda")
```

## GPU-resident matrix operations

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.

```{r tensors}
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 normalization, PCA, and kNN

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.

```{r sparse-pipeline}
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)
```

## Other CUDA tasks

The same strict device argument applies to decompositions, distances, and
k-means:

```{r more-tasks}
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.

## Where to go next

- [GPU setup and troubleshooting](gpu-setup.html) provides exact Windows and
  Linux dependency checks.
- [GPU residency and provenance](backend-provenance.html) shows how to avoid
  unnecessary transfers.
- [CUDA operation coverage](backend-support.html) shows which tasks run fully
  on the GPU and which currently include an R stage.
- [Performance](performance.html) explains the retained comparisons with base
  R and the optional R `torch` backend.
