High-Throughput Generalized Phylogenetic Linear Models in Rust and R
gelimo is an ultra-fast, memory-efficient engine for Phylogenetic Generalized Least Squares (PGLS), designed for comparative genomics and evolutionary trait association. It offers full compatibility with the statistical specifications of R's caper package while accelerating whole-genome orthogroup screening by over
Available as both a standalone CLI binary and a native R package powered by extendr.
-
Ultra-Fast Genome-Wide PGLS: Fit
$>16,000$ PGLS models across 200+ species in under 0.6 seconds (compared to hours or days in traditional R loops). -
OrthoFinder Integration: Direct streaming ingestion of
Orthogroups.GeneCount.tsvtables with automatic species matching, QC filtering, and log transformations (log10(count + 1)). -
Multiple Statistical Modes:
-
Traditional Trait-as-Response Mode:
$\text{Trait} \sim \text{GFS} + \text{Covariates}$ -
Inverse Mode:
$\text{GFS} \sim \text{Trait}_1 + \text{Trait}_2 + \dots$ (utilizing single pre-inverted projection matrices). -
Partial
$r$ Effect Sizes: Exact partial correlation coefficients controlling for allometric covariates.
-
Traditional Trait-as-Response Mode:
-
Continuous Pagel's
$\lambda$ Optimization: Fast 1D Maximum Likelihood (ML) golden-section search. - Fuzzy Taxonomy Resolution: Handles underscore/space mismatches, subspecies strings, and automatic tree/table pruning.
-
Dual Interface: Full-featured Rust CLI and native R functions returning standard R
data.frameobjects.
Evaluated on a mammalian dataset (216 species, 16,916 QC-passed OrthoFinder orthogroups) testing gene family size evolution against Maximum Longevity and Adult Body Weight:
| Implementation / Engine | Methodology | CPU Cores | Time per Model | Total Runtime (16,916 OGs) | Throughput | Speedup vs gelimo
|
|---|---|---|---|---|---|---|
gelimo (Rust / R) |
Pre-factored Cholesky + Projection | Multi-thread | 33 µs | 0.57 seconds | 29,677 mod/s | Baseline ( |
R Multicore Loop (nlme::gls + mclapply) |
Fixed |
16 cores | 0.863 s | ~4.05 hours | 1.16 mod/s | |
Traditional R Loop (caper::pgls) |
Sequential per-model ML |
1 core | 74.93 s | ~352 hours (~14.6 days) | 0.013 mod/s |
All implementations yield analytically identical
$\beta$ coefficients,$t$ -statistics, and$p$ -values up to 64-bit floating-point precision (f64).
Requires a working Rust toolchain (
git clone https://github.com/Hectorjlo/gelimo.git
cd gelimo
cargo build --release
# The binary will be available at target/release/gelimo
./target/release/gelimo --helpInstall directly in R via devtools or from local source:
# From GitHub
devtools::install_github("Hectorjlo/gelimo")
# Or from local source directory
devtools::install(".")Fit a single PGLS model with Pagel's
gelimo fit \
--tree species_tree.tree \
--data traits.tsv \
--response "Maximum longevity (yrs)" \
--predictors "Log Adult weight (g)" \
--opt-lambda \
--pruneScreen all orthogroups from OrthoFinder with comprehensive QC filters and tree export:
gelimo orthogroups \
--tree species_tree.tree \
--data traits.tsv \
--orthogroups Orthogroups.GeneCount.tsv \
--response "Maximum longevity (yrs)" \
--covariates "Log Adult weight (g)" \
--output pgls_results.tsv \
--log-count \
--gfs-as-response \
--opt-lambda \
--min-prevalence 0.80 \
--min-count-spread 2.0 \
--save-tree pruned_species_tree.nwk \
--eval-ranges 0.5,1.0,2.0,3.0 \
--threads 8 \
--prunelibrary(gelimo)
# 1. Genome-Wide Orthogroup Screening (returns native R data.frame in < 0.6s)
results <- pgls_orthogroups(
tree = "species_tree.tree",
data = "traits.tsv",
orthogroups = "Orthogroups.GeneCount.tsv",
response = "Maximum longevity (yrs)",
covariates = c("Log Adult weight (g)"),
log_count = TRUE,
gfs_as_response = TRUE, # Inverse model mode: GFS ~ Phenotypes
opt_lambda = TRUE, # Optimize Pagel's lambda on baseline trait
min_prevalence = 0.80, # Require gene in >= 80% of species
min_count_spread = 2.0, # Interdecile count spread P95 - P05 >= 2.0
save_tree = "pruned_tree.nwk", # Save aligned pruned Newick tree
output_tsv = "results.tsv", # Optional TSV export
prune = TRUE
)
# View top genomic hits sorted by FDR q-value
head(results)
cat("Significant hits (FDR q < 0.05):", sum(results$q_value_fdr < 0.05), "\n")
# 2. Fit a Single Comparative Trait Model
fit <- pgls_fit(
tree = "species_tree.tree",
data = "traits.tsv",
response = "Maximum longevity (yrs)",
predictors = c("Log Adult weight (g)"),
opt_lambda = TRUE,
prune = TRUE
)
print(fit$coefficients)
cat("R-squared:", fit$r_squared, "AIC:", fit$aic, "Lambda:", fit$optimal_lambda, "\n")| Flag | R Argument | Default | Description |
|---|---|---|---|
-T, --threads |
— | All cores | Number of CPU threads for Rayon parallel execution |
--min-prevalence |
min_prevalence |
None |
Minimum prevalence proportion (0.80) or percentage (80%) of species sharing |
--min-species |
min_species |
5 |
Absolute minimum number of species possessing at least 1 copy of the gene |
--min-count-spread |
min_count_spread |
None |
Minimum interdecile count spread ( |
--min-count-range |
min_count_range |
None |
Minimum absolute count range ( |
--top-ogs |
top_ogs |
None |
Retain only top |
--save-tree |
save_tree |
None |
File path to export the pruned phylogenetic tree in Newick format |
--eval-ranges |
— | None |
Comma-separated thresholds (e.g. 0.5,1.0,2.0) to print a multi-threshold power & FDR summary table |
--log-count |
log_count |
false |
Apply |
--binary |
binary_mode |
false |
Convert gene counts to binary presence/absence ( |
--gfs-as-response |
gfs_as_response |
false |
Fit inverse PGLS model ( |
--opt-lambda |
opt_lambda |
false |
Optimize Pagel's |
- Format: Newick format (e.g.
SpeciesTree_rooted.txt,.nwk,.tree). - Structure: Tips must contain the species identifiers (e.g.
Homo_sapiens,Mus_musculus).
- Format: TSV (tab-separated) or CSV (comma-separated).
- Rows: Each row represents one species.
- Species Identification:
gelimoautomatically detects species names formatted as:- Single column:
species,Species,species_name, orSpp.Name(e.g.Homo_sapiensorHomo sapiens). - Two columns: Separate
GenusandSpeciescolumns (e.g.Homoandsapiens), which are merged and normalized automatically with fuzzy matching.
- Single column:
- Trait Columns: Numerical columns containing continuous variables (phenotypic traits and covariates).
Example --data TSV:
Genus Species Maximum longevity (yrs) Adult weight (g) Log Adult weight (g)
Homo sapiens 122.5 70000 4.845
Mus musculus 4.0 20 1.301
Canis lupus 20.0 35000 4.544-
Format: Direct, unmodified
Orthogroups.GeneCount.tsvfile from OrthoFinder. -
Structure:
- First column:
Orthogroupidentifier (e.g.OG0000001). - Columns 2 to
$N$ : Species names with integer gene copy counts. - Final column:
Total(detected and ignored automatically).
- First column:
-y, --response: The exact column name in--datato be used as the primary phenotype of interest (e.g.-y "Maximum longevity (yrs)").-x, --covariates: (Optional) Comma-separated list of column names in--dataused to control for confounding effects such as body mass or allometry (e.g.-x "Log Adult weight (g)").
gelimo supports two complementary modeling frameworks:
-
Mode A: Traditional Trait Response Mode (Default)
$$\text{Phenotype } (-y) \sim \text{GFS}_{\text{OG}} + \text{Covariate}_1 (-x) + \dots$$ Tests whether gene family expansions directly predict changes in the phenotypic trait controlling for covariates. -
Mode B: Inverse Mode (
--gfs-as-response/gfs_as_response = TRUE)$$\text{GFS}_{\text{OG}} \sim \text{Phenotype } (-y) + \text{Covariate}_1 (-x) + \dots$$ Tests whether phenotypic divergence drives gene family expansions/contractions across the phylogeny, computing the exact Partial$r$ effect size for the target trait.
| Column | Description |
|---|---|
Orthogroup |
OrthoFinder orthogroup identifier (e.g. OG0010602) |
Beta_OG |
Estimated effect size (slope) for the orthogroup gene count |
StdError |
Standard error of the |
t_stat |
Student's |
p_value |
Raw two-tailed |
q_value_fdr |
False Discovery Rate adjusted |
p_bonferroni |
Family-wise error rate adjusted |
Partial_r |
Partial correlation coefficient controlling for covariates |
R_squared |
Coefficient of determination ( |
Sigma2 |
Maximum Likelihood evolutionary rate ( |
Mean_Copies |
Mean gene copy count across aligned species |
Species_With_Gene |
Number of aligned species possessing at least 1 copy of the gene |
gelimo is engineered under the following core principles:
-
Zero-Copy Matrix Decomposition: The phylogenetic covariance matrix (
$V$ ) and phenotype design matrices are decomposed only once ($1 \times O(N^3)$), eliminating redundant inversions across 30,000 iterations. - Pre-Computed Linear Projections: In inverse mode, the projection operator $P = (X^{T} X^)^{-1} X^{*T}$ reduces each orthogroup evaluation to a single BLAS matrix-vector product in nanoseconds.
-
Decoupled Architecture:
extendr-apiis isolated behind the optionalr-bindingsCargo feature, keeping standalone CLI builds lightweight and free of R runtime dependencies.
- Special thanks to Dr. Benjamin for invaluable methodological guidance, statistical insights into high-throughput phylogenetic linear modeling, and the formulation of the inverse model and Type II framework.
- Honorable mention to Carlos García González for initial workflows and early exploratory scripts on comparative orthogroup analysis that inspired the development and optimization of this engine.
If you use gelimo in your research, please cite:
@software{lopez_ordaz_2026_gelimo,
author = {López-Ordaz, Héctor Jesús},
title = {gelimo: High-Throughput Generalized Phylogenetic Linear Models in Rust and R},
year = {2026},
version = {0.1.0},
publisher = {GitHub},
url = {https://github.com/Hectorjlo/gelimo}
}A machine-readable citation format is also available in CITATION.cff.
- Author: Héctor Jesús López-Ordaz (
<hectorjl@lcg.unam.mx>) — Licenciatura en Ciencias Genómicas, UNAM. - License: MIT License.