Website · Documentation · R package · Examples
ferx-core is an open-source, high-performance nonlinear mixed-effects (NLME) modeling engine for population pharmacokinetic and pharmacodynamic (PopPK/PD) analysis. Written in Rust, it implements FOCE/FOCEI, SAEM, importance sampling, analytical PK solutions, and ODE models for pharmacometric workflows.
Additional features:
- PK-PD and multi-analyte modeling
- BLQ likelihood modeling
- Importance Sampling & SIR
- Deep Compartmental Models & Neural ODEs
- Stochastic differential equations
- Simulation with uncertainty
- Various optimizers
- ... and more
# Build (the repo is a cargo workspace; the `ferx` binary lives in `ferx-cli`)
cargo build --release --workspace
# Fit a model
cargo run --release -p ferx-cli -- examples/warfarin.ferx --data data/warfarin.csv
# Fit with simulated data (uses [simulation] block)
cargo run --release -p ferx-cli -- examples/warfarin.ferx --simulateOutput files: {model}-fit.yaml (parameter estimates) and {model}-sdtab.csv (per-subject diagnostics).
Models are defined in a simple DSL. Here is a one-compartment oral PK model for warfarin:
[parameters]
theta TVCL(0.2, 0.001, 10.0) # name(initial, lower, upper)
theta TVV(10.0, 0.1, 500.0)
theta TVKA(1.5, 0.01, 50.0)
omega ETA_CL ~ 0.09 # between-subject variability (variance)
omega ETA_V ~ 0.04
omega ETA_KA ~ 0.30
sigma PROP_ERR ~ 0.02 # residual error
[individual_parameters]
CL = TVCL * exp(ETA_CL)
V = TVV * exp(ETA_V)
KA = TVKA * exp(ETA_KA)
[structural_model]
pk one_cpt_oral(cl=CL, v=V, ka=KA)
[error_model]
DV ~ proportional(PROP_ERR)
[fit_options]
method = foce
maxiter = 300
covariance = true
| Model | Syntax |
|---|---|
| 1-compartment IV (bolus and/or infusion) | pk one_cpt_iv(cl=CL, v=V) |
| 1-compartment oral | pk one_cpt_oral(cl=CL, v=V, ka=KA) |
| 2-compartment IV (bolus and/or infusion) | pk two_cpt_iv(cl=CL, v1=V1, q=Q, v2=V2) |
| 2-compartment oral | pk two_cpt_oral(cl=CL, v1=V1, q=Q, v2=V2, ka=KA) |
| 3-compartment IV (bolus and/or infusion) | pk three_cpt_iv(cl=CL, v1=V1, q2=Q2, v2=V2, q3=Q3, v3=V3) |
| 3-compartment oral | pk three_cpt_oral(cl=CL, v1=V1, q2=Q2, v2=V2, q3=Q3, v3=V3, ka=KA) |
| ODE-based | Define equations in an [odes] block |
For IV models, the closed form (bolus vs infusion) is chosen per dose event from the RATE column — a subject can mix bolus and infusion records.
Set via method in [fit_options]:
| Method | Description |
|---|---|
foce |
First-Order Conditional Estimation |
focei |
FOCE with Interaction (default) |
gn |
Gauss-Newton (BHHH) with Levenberg-Marquardt damping |
gn_hybrid |
Gauss-Newton followed by FOCEI polish |
saem |
Stochastic Approximation EM |
imp |
Importance Sampling (typically chained after another method for OFV evaluation) |
Methods can be chained (e.g. method = saem, focei, imp) to run sequentially.
For FOCE/FOCEI, the outer optimizer can be set via optimizer in [fit_options]:
| Optimizer | Description |
|---|---|
auto |
Per-model selection (default): L-BFGS when the exact analytic gradient is available, BOBYQA otherwise |
slsqp |
NLopt Sequential Least Squares Programming |
nlopt_lbfgs |
NLopt L-BFGS (lbfgs and bfgs are deprecated aliases) |
mma |
NLopt Method of Moving Asymptotes |
bobyqa |
NLopt BOBYQA (derivative-free) |
trust_region |
Newton trust-region (argmin + Steihaug CG) |
Input data uses NONMEM-format CSV with columns:
- Required:
ID,TIME,DV,EVID,AMT,CMT - Optional:
RATE,MDV,II,SS - Covariates: Any additional columns are auto-detected
EVID codes: 0 = observation, 1 = dose, 4 = reset + dose.
The examples/ directory contains ready-to-run models:
| File | Description |
|---|---|
warfarin.ferx |
1-compartment oral (warfarin PK) |
two_cpt_iv.ferx |
2-compartment IV bolus |
two_cpt_oral_cov.ferx |
2-compartment oral with covariates (WT, CRCL) |
mm_oral.ferx |
Michaelis-Menten elimination via ODE |
An R wrapper package (ferx) provides ferx_fit(), ferx_simulate(), and ferx_predict() functions that call into this Rust engine via extendr. Source is at ../ferx.
# Build the Rust backend and load the package
withr::with_dir("path/to/ferx", {
system("cd src/rust && cargo build --release")
devtools::load_all()
})result <- ferx_fit(
model = "warfarin.ferx",
data = "warfarin.csv",
method = "foce" # or "focei"
)
result # prints summary with estimates and SEs
result$theta # named vector of fixed-effect estimates
result$omega # BSV covariance matrix
result$sigma # residual error estimates
result$se_theta # standard errors (NULL if covariance step failed)
result$sdtab # data.frame with ID, TIME, DV, PRED, IPRED, CWRES, IWRES, ETA1..nsim <- ferx_simulate("warfarin.ferx", "warfarin.csv", n_sim = 100, seed = 42)
# Returns data.frame with SIM, ID, TIME, IPRED, DV_SIM
library(vpc)
obs <- read.csv("warfarin.csv")
vpc(obs = obs, sim = sim, sim_cols = list(dv = "DV_SIM"))preds <- ferx_predict("warfarin.ferx", "warfarin.csv")
# Returns data.frame with ID, TIME, PRED (predictions at eta = 0)MIT — see LICENSE.
One exception: the test fixture data/mbma_naproxen.csv is derived from a
CC BY-NC 4.0 publication and carries that licence instead, so commercial use
of that file is not granted. It is test data only — never linked into the
ferx binary, and excluded from the packaged crate along with its README and
licence companions, so the published crate carries no CC BY-NC file. See
data/mbma_naproxen.LICENSE.