KODAMA: Knowledge Discovery by Accuracy Maximization

Overview

KODAMA is the R interface to the standalone kodama-cpp library. KODAMA constructs a reusable nearest-neighbor graph, evolves independent candidate label vectors by maximizing cross-validated prediction accuracy, and uses the ensemble of label solutions to correct graph dissimilarities for downstream embedding. The supported classifiers are KNN and SIMPLS followed by LDA in latent-component space.

The numerical core uses float32 data and has CPU, NVIDIA CUDA, and Apple Metal backends. The R interface deliberately remains focused on the KODAMA workflow; general dimensionality-reduction and PLS modeling APIs are maintained in fastEmbedR and fastPLS.

Select default execution resources

Backend-capable functions use CPU unless a backend is selected for the R session. Pipeline functions also use a session-wide core count when supplied. Explicit function arguments have the highest priority.

# Session-wide defaults
old.backend <- KODAMA_backend("cpu")
old.cores <- getOption("n.cores")
options(n.cores = 4L)

# The equivalent direct option is options(backend = "cpu")
KODAMA_backend()
#> [1] "cpu"

# Explicit arguments override the session default. For example:
# KODAMA.graph(x, backend = "cuda")

options(backend = old.backend)
options(n.cores = old.cores)

For an accelerator session, set one of the following before the analysis:

KODAMA_backend("cuda")
KODAMA_backend("metal")
options(n.cores = 4L)

# Uses the session default
graph <- KODAMA.graph(x)

For non-interactive jobs, BACKEND=cpu|cuda|metal supplies the default when the R option is unset. The precedence is explicit argument, R option, environment variable, then CPU. Selecting an unavailable accelerator produces an error; KODAMA does not silently change the requested backend.

Basic workflow

data(MetRef)

x <- MetRef$data
x <- x[, colSums(x) != 0, drop = FALSE]
x <- normalization(x)$newXtrain
x <- scaling(x)$newXtrain

Build the expensive graph once and reuse it for either classifier:

g <- KODAMA.graph(x, k = 100, backend = "cpu", n.cores = 4)

fit.knn <- KODAMA.matrix(
  data = x, graph = g, classifier = "knn",
  M = 100, Tcycle = 100, knn.k = 30,
  backend = "cpu", n.cores = 4
)

fit.pls <- KODAMA.matrix(
  data = x, graph = g, classifier = "pls_lda",
  M = 100, Tcycle = 100, ncomp = 50,
  backend = "cpu", n.cores = 4
)

embedding <- KODAMA.visualization(
  fit.pls, method = "UMAP", raw.data = x,
  graph.mode = "fuzzy", backend = "cpu"
)

KODAMA.matrix() also accepts raw data without an explicit graph. In that case it calls KODAMA.graph() internally. A precomputed KNN graph can be used when the original feature matrix is unavailable; PLS-LDA then operates on self-tuning graph-Laplacian features.

Bioconductor objects

RunKODAMAgraph(), RunKODAMAmatrix(), and RunKODAMAvisualization() provide methods for SingleCellExperiment, SpatialExperiment, Seurat, and Giotto objects. Optional packages are checked at run time and remain in Suggests, so the base package can be installed without a complete single-cell ecosystem.

The following compact workflow keeps the analysis and its reusable graph inside a SpatialExperiment. It replaces the legacy extraction of separate data, coordinate, sample, and label objects. sample_id and spatialCoords() are read automatically by the object methods.

spe <- SpatialFeatureSelection(spe)
subject <- "Br5595"
spe_sub <- spe[, spe$subject == subject]
spe_sub <- RunFastPCA(
  spe_sub, nfeatures = gene_number, ncomp = 50,
  center = TRUE, scale = TRUE,
  backend = backend, n.cores = n.cores, seed = seed
)

spe_sub <- RunKODAMAgraph(
  spe_sub, dims = 50, backend = backend, n.cores = n.cores
)
spe_sub <- RunKODAMAmatrix(
  spe_sub, dims = 50, classifier = "pls_lda", M = 100, Tcycle = 100,
  landmarks = 100000, splitting = splitting,
  spatial.resolution = spatial.resolution,
  backend = backend, n.cores = n.cores, seed = seed
)
spe_sub <- RunKODAMAvisualization(
  spe_sub, dims = 50, method = "UMAP",
  backend = backend, n.cores = n.cores
)

labels <- factor(spe_sub$layer_guess_reordered)
plot(
  SingleCellExperiment::reducedDim(spe_sub, "KODAMA"),
  pch = 20, col = cols_cluster[labels]
)

Migration from KODAMA 2.4

This package is a new implementation, not a byte-for-byte continuation of the legacy R code. The previous implementation is archived as KODAMAlegacy.

Innovations

  • The mathematical core is a standalone C++17 library that can be wrapped by R and Python without embedding either language runtime.
  • CPU, CUDA, and Metal backends share the same KODAMA evolution contract and use float32 analysis buffers.
  • KODAMA.graph() separates graph construction from accuracy maximization, so KNN and PLS-LDA analyses can reuse the same graph and PCA initialization.
  • KODAMA.matrix() accepts raw matrices, reusable graph handles, or explicit KNN indices and distances.
  • KNN and SIMPLS plus latent-space LDA are explicit classifiers. Independent M runs retain cycle diagnostics, timing, and backend provenance.
  • Landmark strata are constructed once as a shared coarse atlas, preserving coverage while avoiding repeated full-data partitioning.
  • Native UMAP and openTSNE consume the corrected KODAMA graph and use raw-data PCA initialization by default when available.

Renamed or deprecated interfaces

Legacy interface New location or replacement
KODAMA.matrix() Retained with graph reuse, explicit classifier, backend, and diagnostics arguments.
KODAMA.visualization() Retained; configuration is supplied as named arguments rather than config.*.default lists.
core_cpp() Replaced by CoreKNN() and CorePLSLDA().
pca() Use RunFastPCA() for backend-native float32 PCA on matrices or supported containers.
config.tsne.default and config.umap.default Use fastEmbedR for general embedding or named arguments to KODAMA.visualization().
MDS.defaults and the legacy MDS visualization path Deprecated. Use the graph-based UMAP or openTSNE methods in KODAMA.visualization(), or a dedicated MDS implementation when metric MDS is specifically required.
legacy PLS-DA helpers Use fastPLS::pls() for general PLS modeling; KODAMA supports KNN and PLS-LDA classifiers.
floyd(), transformy(), kabsch(), mcplot() Deprecated legacy utilities. Diagnostics are available through KODAMA.diagnostics() and KODAMA.timing().
ancestry = TRUE Replaced by the explicit spatial.mode = "population".

The synthetic manifold generators, normalization(), and scaling() remain available for compatibility. The historical MetRef, USA, and lymphoma datasets are included so published examples remain reproducible.

Session information

sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 LTS
#> 
#> Matrix products: default
#> BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so;  LAPACK version 3.12.0
#> 
#> locale:
#>  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
#>  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
#>  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
#>  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
#>  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
#> 
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] KODAMA_0.99.7    BiocStyle_2.41.0
#> 
#> loaded via a namespace (and not attached):
#>  [1] digest_0.6.39       R6_2.6.1            fastmap_1.2.0      
#>  [4] xfun_0.61           maketools_1.3.2     cachem_1.1.0       
#>  [7] knitr_1.52          htmltools_0.5.9     rmarkdown_2.32     
#> [10] buildtools_1.0.0    lifecycle_1.0.5     cli_3.6.6          
#> [13] sass_0.4.10         jquerylib_0.1.4     compiler_4.6.1     
#> [16] sys_3.4.3           tools_4.6.1         bslib_0.12.0       
#> [19] evaluate_1.0.5      Rcpp_1.1.2          yaml_2.3.12        
#> [22] otel_0.2.0          BiocManager_1.30.27 jsonlite_2.0.0     
#> [25] rlang_1.3.0