The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
Inverse optimal transport for single-cell state transitions and pseudotime
bioIOT is an R implementation of semi-relaxed
inverse optimal transport (IOT) for single-cell trajectory analysis.
Given state-transition features, source/target state masses and observed
transitions, it learns feature weights θ such that the soft-marginal OT
plan induced by the linear cost C = -einsum(φ, θ)
reproduces the data — and turns the fit into state transition matrices,
random-walk pseudotime and ggplot2 visualisation.
The solver was developed and validated as part of a research project on treatment-resistance state transitions, and is packaged here for general use.
# from GitHub
remotes::install_github("XTSgreen/bioIOT-R")install.packages(".", repos = NULL, type = "source")
# or: R CMD build . && install.packages("bioIOT_0.2.2.tar.gz", repos = NULL)library(bioIOT)
# 1) A reproducible synthetic dataset with ground truth
sim <- simulate_iot_states(K = 6, seed = 1) # or data(demo_iot_states)
# 2) Fit feature weights: exact implicit gradients + two-stage debias +
# multi-restart. Scenarios may differ in the number of states.
fit <- fit_iot(sim$phi, sim$a, sim$b, sim$T_true, n_restart = 2)
fit; summary(fit)
# 3) Trajectory layer
Q <- transition_matrix(fit) # (K, K) transitions
pt <- pseudotime_from_transition(Q, root = "S1") # random-walk pseudotime
# 4) Visualisation
plot_transition_heatmap(Q) # annotated heatmap
plot_transition_flow(Q, sim$embedding) # CellRank-style arrows
plot_theta(fit) # weights + support
# 5) Straight from cell-level objects
res <- runIOT(sim$cell_embedding, sim$cell_state,
from = sim$cell_time == "t0", to = sim$cell_time == "t1",
root = "S1")
# SingleCellExperiment: runIOT(sce, state_col = "state", time_col = "time",
# from = "t0", to = "t1", dimred = "PCA")
# Seurat: runIOT(obj, group.by = "state", split.by = "time",
# from = "t0", to = "t1", reduction = "pca")Without observed transitions T_obs,
runIOT() solves the plan with uniform feature weights; with
T_obs (e.g. from lineage or clone data) it fits the weights
first.
The showcase directory runs the full pipeline on the human skeletal-muscle myoblast time course (HSMMSingleCell, 271 cells × 47k genes, 0/24/48/72 h; Shin et al. 2015) and benchmarks it against Slingshot:
| Pseudotime metric (Spearman) | value |
|---|---|
| bioIOT pseudotime ~ known Hours (cells) | 0.251 |
| Slingshot pseudotime ~ known Hours (cells) | 0.253 |
| bioIOT state pseudotime ~ Slingshot state pseudotime | 0.600 |
With 30 cells of sampling noise per state, fit_iot
recovers the true transition matrix ~3× more accurately than the raw
noisy transitions (mean per-row L1 0.013 vs 0.039; 50 replicates).
Provenance and licensing of all showcase data are documented in showcase/LICENSE_AUDIT.md.
| Layer | Functions |
|---|---|
| Core solver | soft_sinkhorn(), row_conditional(),
make_cost(), row_ce_loss(),
zscore_phi() |
| Fitting | fit_iot() (with print/summary
methods) |
| Trajectory | transition_matrix(),
pseudotime_from_transition(),
build_state_features() |
| Single-cell | runIOT() — matrix / SingleCellExperiment / Seurat |
| Visualisation | plot_transition_heatmap(),
plot_transition_flow(), plot_theta(),
plot_pathway_trend() |
| Demo data | simulate_iot_states(),
demo_iot_states |
| Bulk cohorts | pathway_markers, score_pathways(),
collapse_probes(), gsm_id() |
bioIOT solves
min_P <C, P> − eps·H(P) + mu·KL(col(P) ‖ b) s.t. P·1 = a
with a hard source-side row marginal and a KL-anchored column marginal:
mu → ∞ recovers hard-marginal OT (pure column features
unidentifiable);mu → 0 recovers a plain row-softmax (no
target-composition anchoring);mu interpolates the two — the paper’s working
point is mu = 0.5, eps = 1.0, lam = 0.05.A vignette is bundled with the package
(browseVignettes("bioIOT") after installation).
library(testthat); library(bioIOT)
test_dir("tests/testthat") # from the repository rootIf you use bioIOT, please cite:
@misc{dong2026bioiotr,
author = {Dong, Han},
title = {bioIOT: Inverse Optimal Transport for Single-Cell
Trajectory Analysis},
year = {2026},
howpublished = {\url{https://github.com/XTSgreen/bioIOT-R}},
note = {R package version 0.2.2}
}MIT © 2026 Han Dong (XTSgreen)
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.