Skip to contents

Introduction

The bHIVE package provides two equivalent interfaces: a functional API (bHIVE()) for quick, one-liner analysis and an R6 class API (AINet) for full compositional control. This vignette focuses on the R6 API and the 9 immune modules that can be injected into AINet to customize every stage of the algorithm.

Each module is an independent R6 class that encapsulates a single immunological mechanism. You create modules separately and pass them to AINet$new(), allowing you to mix and match any combination.

model <- AINet$new(
  nAntibodies = 20,
  shm            = SHMEngine$new(method = "adaptive"),
  idiotypic      = IdiotypicNetwork$new(),
  germinalCenter = GerminalCenter$new(),
  microenvironment = Microenvironment$new(),
  init           = VDJLibrary$new(method = "pca"),
  activation     = ActivationGate$new(),
  memory         = MemoryPool$new()
)

Somatic Hypermutation Engine

The SHMEngine controls how antibodies mutate after cloning. Five strategies are available, each grounded in a different aspect of SHM biology:

Method Biological Analogy When to Use
"uniform" Random point mutations Baseline; simple and robust
"airs" Affinity-proportional mutation (AIRS) When high-affinity antibodies should explore less
"hotspot" AID targets WRCY motifs When some features matter more than others
"energy" E_SHM ~ N_Mut^2 budget constraint When you want bounded total perturbation
"adaptive" Per-feature Adam-like moment tracking Best for complex landscapes; most novel

The adaptive method is the most distinctive in which somatic hypermutation and the Adam optimizer function the same. Each feature dimension maintains running mean and variance of past gradients.

data(iris)
X <- as.matrix(iris[, 1:4])
y <- iris$Species

methods <- c("uniform", "airs", "adaptive")
results <- lapply(methods, function(m) {
  shm <- SHMEngine$new(method = m)
  model <- AINet$new(nAntibodies = 15, 
                     maxIter = 15, 
                     shm = shm,
                     verbose = FALSE)
  model$fit(X, y, task = "classification")
  data.frame(
    method   = m,
    accuracy = mean(model$result$assignments == as.character(y)),
    n_ab     = nrow(model$repertoire$as_matrix())
  )
})
do.call(rbind, results)
##     method  accuracy n_ab
## 1  uniform 0.9600000   14
## 2     airs 0.9266667   15
## 3 adaptive 0.9533333   15

Idiotypic Network Regulation

In classical AIS, similar antibodies are suppressed via a simple distance threshold (epsilon). The IdiotypicNetwork module replaces this with principled network dynamics inspired by Varela & Coutinho’s (1991) second-generation immune network theory.

The key insight is a bell-shaped activation function: too little stimulation from other antibodies leads to death, moderate stimulation leads to activation, and excessive stimulation leads to suppression. This creates emergent self-organized repertoire structure.

# Standard epsilon suppression (default)
model_std <- AINet$new(nAntibodies = 25, maxIter = 15, verbose = FALSE)
model_std$fit(X, task = "clustering")

# Idiotypic network regulation
model_idi <- AINet$new(
  nAntibodies = 25, maxIter = 15,
  idiotypic = IdiotypicNetwork$new(
    theta_low = 0.01, 
    theta_high = 0.5,
    source_rate = 0.5, 
    timeSteps = 20
  ),
  verbose = FALSE
)
model_idi$fit(X, task = "clustering")

cat("Standard suppression:", model_std$repertoire$size(), "antibodies\n")
## Standard suppression: 24 antibodies
cat("Idiotypic regulation:", model_idi$repertoire$size(), "antibodies\n")
## Idiotypic regulation: 2 antibodies

The idiotypic network typically produces a more parsimonious repertoire because regulation is based on the network of antibody-antibody interactions rather than pairwise distance alone.

Germinal Center Selection

The GerminalCenter module models the competition for T follicular helper (Tfh) cell help that occurs in real germinal centers. Only antibodies that receive Tfh help survive – this implements quality-based selection pressure.

The quality metric is task-aware:

  • Classification: proportion of correctly matched labels
  • Clustering: local density (sum of affinities to assigned data points)
gc <- GerminalCenter$new(
  nTfh = 10,                 # number of Tfh selectors
  selectionPressure = 0.5,   # 0 = no selection, 1 = very strict
  rounds = 1
)

model_gc <- AINet$new(
  nAntibodies = 30, 
  maxIter = 15,
  germinalCenter = gc,
  verbose = FALSE
)
model_gc$fit(X, y, task = "classification")
cat("Antibodies after GC selection:", model_gc$repertoire$size(), "\n")
## Antibodies after GC selection: 10
cat("Accuracy:", mean(model_gc$result$assignments == as.character(y)), "\n")
## Accuracy: 0.9333333

Microenvironment

The Microenvironment module classifies the local context around each antibody into three zones and adapts mutation rates accordingly:

  • Stable (high local density): reduce mutation rate to preserve good antibodies
  • Explore (low local density): increase mutation rate to search sparse regions
  • Boundary (intermediate): moderate mutation, potential trigger for class switching
me <- Microenvironment$new(
  high_density_threshold = 0.75,
  low_density_threshold = 0.25,
  stabilization_factor = 0.3,   # reduce mutation to 30% in stable zones
  exploration_factor = 2.0      # double mutation in exploration zones
)

rep <- ImmuneRepertoire$new(X[sample(150, 15), ])
env <- me$assess(rep, X)
table(env$zones)
## 
## boundary  explore   stable 
##        7        4        4

V(D)J Gene Library Initialization

Instead of random sampling, VDJLibrary generates antibodies by combinatorial assembly of gene segments, mimicking V(D)J recombination. The feature space is split into three segments (V, D, J), each discretized into alleles, and new antibodies are built by combining one allele from each segment.

vdj <- VDJLibrary$new(nV = 5, 
                      nD = 3, 
                      nJ = 3, 
                      method = "pca")
A <- vdj$generate(20, X)
dim(A)  # 20 antibodies x 4 features
## [1] 20  4
print(vdj)
## <VDJLibrary> method='pca' V=5 D=3 J=3
##   Combinatorial space: 45 antibodies

Three decomposition methods are available:

  • "pca": PCA components define gene segments (best for correlated features)
  • "cluster": k-means within dimension groups creates alleles
  • "random_partition": random feature grouping (simplest)

Two-Signal Activation Gate

In real immunity, B cells require two signals to activate: antigen recognition (Signal 1) and costimulatory context (Signal 2). The ActivationGate module prevents spurious cloning on isolated outliers.

Signal 2 options:

  • "density": local data density around the antibody
  • "danger": user-provided danger score per data point
  • "entropy": local label entropy (classification only)
gate <- ActivationGate$new(
  signal2_type = "density",
  threshold1 = 0.1,    # minimum affinity (Signal 1)
  threshold2 = 0.3     # density percentile (Signal 2)
)

A <- X[sample(150, 10), ]
rep <- ImmuneRepertoire$new(A)
aff <- rep$affinity_matrix(X, "gaussian")
activated <- gate$evaluate(aff, X, A)
cat("Activated interactions:", sum(activated), "/", length(activated), "\n")
## Activated interactions: 501 / 1500

Memory Pool

The MemoryPool archives high-performing antibodies and recalls them when relevant to current data. This is useful for streaming or non-stationary data where distribution shifts may occur.

mp <- MemoryPool$new(
  archive_threshold = 0.01,  # minimum average affinity to archive
  max_memory = 50,
  recall_threshold = 0.01
)

rep <- ImmuneRepertoire$new(X[sample(150, 10), ])
n_archived <- mp$archive(rep, X)
cat("Archived:", n_archived, "memory cells\n")
## Archived: 10 memory cells
cat("Pool size:", mp$size(), "\n")
## Pool size: 10
# Recall memories relevant to a subset
recalled <- mp$recall(X[1:30, ])
cat("Recalled:", nrow(recalled), "cells for the query\n")
## Recalled: 3 cells for the query

Class Switcher

The ClassSwitcher module changes the effective kernel width of antibodies based on their microenvironment zone, analogous to real B cell isotype switching:

Isotype Kernel Width Matching Style
IgM alpha = 0.1 Broad (exploration)
IgG alpha = 5.0 Specific (discrimination)
IgA alpha = 1.0 Intermediate (boundary patrol)
cs <- ClassSwitcher$new(alpha_IgM = 0.1, alpha_IgG = 5.0, alpha_IgA = 1.0)

rep <- ImmuneRepertoire$new(X[sample(150, 10), ])
zones <- sample(c("stable", "explore", "boundary"), 10, replace = TRUE)
alphas <- cs$switch_isotypes(rep, zones)
data.frame(zone = zones, isotype = rep$metadata$isotype, alpha = alphas)
##        zone isotype alpha
## 1  boundary     IgA   1.0
## 2    stable     IgG   5.0
## 3    stable     IgG   5.0
## 4    stable     IgG   5.0
## 5  boundary     IgA   1.0
## 6    stable     IgG   5.0
## 7   explore     IgM   0.1
## 8    stable     IgG   5.0
## 9    stable     IgG   5.0
## 10  explore     IgM   0.1

Convergent Selection

The ConvergentSelector identifies “public antibodies” – antibodies that appear across multiple independent bHIVE runs. This implements the immunological concept of public clonotypes as a biologically-motivated ensemble method.

# Run 3 independent bHIVE analyses
set.seed(42)
results <- lapply(1:3, function(i) {
  m <- AINet$new(nAntibodies = 15, maxIter = 10, verbose = FALSE)
  m$fit(X, task = "clustering")
  m$result
})

conv <- ConvergentSelector$new(tolerance = 1.0, min_appearances = 2)
public <- conv$from_results(results)
cat("Public antibodies:", nrow(public), "\n")
## Public antibodies: 14

Putting It All Together

Here is a full example combining multiple modules for a classification task:

model <- AINet$new(
  nAntibodies = 25,
  maxIter = 20,
  k = 3,
  beta = 5,
  shm = SHMEngine$new(method = "adaptive", base_rate = 0.1),
  idiotypic = IdiotypicNetwork$new(theta_low = 0.01, theta_high = 0.5),
  germinalCenter = GerminalCenter$new(nTfh = 8, selectionPressure = 0.4),
  verbose = FALSE
)

model$fit(X, y, task = "classification")

cat("Final antibodies:", model$repertoire$size(), "\n")
## Final antibodies: 1
cat("Accuracy:", mean(model$result$assignments == as.character(y)), "\n")
## Accuracy: 0.3333333

Not every module combination will improve every dataset. The modular design lets you experiment with different biological mechanisms and find the combination that works best for your data and task.

Session Information

## R version 4.6.1 (2026-06-24)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.4 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.26.so;  LAPACK version 3.12.0
## 
## locale:
##  [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8       
##  [4] LC_COLLATE=C.UTF-8     LC_MONETARY=C.UTF-8    LC_MESSAGES=C.UTF-8   
##  [7] LC_PAPER=C.UTF-8       LC_NAME=C              LC_ADDRESS=C          
## [10] LC_TELEPHONE=C         LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C   
## 
## time zone: UTC
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] viridis_0.6.5     viridisLite_0.4.3 ggplot2_4.0.3     bHIVE_0.99.6     
## [5] BiocStyle_2.40.0 
## 
## loaded via a namespace (and not attached):
##  [1] sass_0.4.10         generics_0.1.4      lattice_0.22-9     
##  [4] digest_0.6.39       magrittr_2.0.5      evaluate_1.0.5     
##  [7] grid_4.6.1          RColorBrewer_1.1-3  bookdown_0.47      
## [10] fastmap_1.2.0       jsonlite_2.0.0      Matrix_1.7-5       
## [13] umap_0.2.10.0       RSpectra_0.16-2     gridExtra_2.3.1    
## [16] BiocManager_1.30.27 scales_1.4.0        codetools_0.2-20   
## [19] textshaping_1.0.5   jquerylib_0.1.4     cli_3.6.6          
## [22] rlang_1.2.0         withr_3.0.3         cachem_1.1.0       
## [25] yaml_2.3.12         otel_0.2.0          Rtsne_0.17         
## [28] tools_4.6.1         parallel_4.6.1      BiocParallel_1.46.0
## [31] dplyr_1.2.1         reticulate_1.46.0   png_0.1-9          
## [34] vctrs_0.7.3         R6_2.6.1            lifecycle_1.0.5    
## [37] fs_2.1.0            htmlwidgets_1.6.4   ragg_1.5.2         
## [40] cluster_2.1.8.2     pkgconfig_2.0.3     desc_1.4.3         
## [43] pkgdown_2.2.0       pillar_1.11.1       bslib_0.11.0       
## [46] gtable_0.3.6        glue_1.8.1          Rcpp_1.1.1-1.1     
## [49] systemfonts_1.3.2   xfun_0.59           tibble_3.3.1       
## [52] tidyselect_1.2.1    knitr_1.51          farver_2.1.2       
## [55] htmltools_0.5.9     rmarkdown_2.31      clusterCrit_1.3.0  
## [58] compiler_4.6.1      S7_0.2.2            askpass_1.2.1      
## [61] openssl_2.4.2