A modular Rust workspace for Bayesian inference with symbolic models, autodiff,
MCMC, MAP optimization, diagnostics, simulation-based calibration, and trace/data I/O.
Priori was written as a modern Rust alternative to Rainier from Stripe for the same broad class
of Bayesian workflows.
Why Priori · Rainier Context · Use Cases · Quick Start · Workspace · CLI · Library
Priori is pre-
1.0and currently organized as a focused workspace of crates rather than a single monolith.
- Immutable symbolic DAG for real-valued model expressions
- Reverse-mode symbolic autodiff over model graphs
- HMC, eHMC, and NUTS sampling backends
- L-BFGS-based MAP optimization
- Diagnostics including R-hat, ESS, and BFMI summaries
- Simulation-based calibration tooling
- CSV, TSV, JSON, JSONL, Parquet, and Arrow/IPC data support
- CLI workflows for
fit,diagnose,predict, andsbc
- Rainier is Stripe's Scala/JVM library for Bayesian inference over fixed-structure, continuous-parameter generative models, with a static computation graph and gradient-based inference. Its overview and paper are published at rainier.fit/docs/intro and rainier.fit/docs/probprog.
- Priori follows that design lineage in Rust: symbolic graphs, reverse-mode autodiff, and HMC-family samplers for the same general class of models.
- Priori expands the surface area with a modular crate layout, eHMC and NUTS backends, MAP optimization, diagnostics, simulation-based calibration, and modern tabular data/trace I/O.
- The workspace includes Rainier parity checks and Rainier-based SBC regression tests to keep the comparison concrete.
Use Priori when you have repeated observations of the same quantity and want a posterior over the unknown mean instead of a single point estimate.
cargo run -p priori-cli -- fit \
--data /path/to/train.csv \
--response y \
--model "normal(mu, 1)" \
--prior "mu ~ normal(0, 10)" \
--chains 4 \
--iterations 500 \
--warmup 200 \
--output /path/to/trace.csv \
--summaryGood fits:
- Sensor or measurement calibration
- Baseline-rate estimation
- Small Bayesian building blocks inside a larger workflow
Use Priori for small-to-medium regression problems where you want uncertainty over coefficients and predictions, not just least-squares estimates.
cargo run -p priori-cli -- fit \
--data /path/to/linreg.csv \
--response y \
--model "normal(alpha + beta * X, 1)" \
--prior "alpha ~ normal(0, 10)" \
--prior "beta ~ normal(0, 10)" \
--chains 2 \
--iterations 300 \
--warmup 150 \
--output /path/to/trace.parquetGood fits:
- Demand or pricing models
- Risk-score coefficient estimation
- Product or marketing lift analysis with uncertainty intervals
Use Priori when your workflow needs machine-readable trace files plus a fast convergence check step.
cargo run -p priori-cli -- diagnose --trace /path/to/trace.parquetThis is useful when you want to gate downstream analysis on diagnostics like R-hat, ESS, and BFMI instead of assuming every sampler run converged.
Use Priori's SBC tooling when you are developing a model family, checking inference correctness, or regression-testing sampler changes.
cargo run -p priori-cli -- sbc \
--model "normal(mu, 1)" \
--prior "mu ~ normal(0, 1)" \
--repetitions 100 \
--synthetic-samples 100 \
--iterations 200 \
--warmup 100 \
--chains 2 \
--bins 10 \
--output /path/to/sbc.jsonGood fits:
- Model validation before production use
- Sampler regression tests
- CI checks for probabilistic code changes
Use the library API when you want inference inside a Rust service, CLI, or offline job instead of shelling out to an external tool.
use priori::core::{ParameterId, Real};
use priori::dist::{Distribution, Normal};
use priori::model::{Generator, Model};
use priori::sampler::SamplerConfig;
let data = vec![1.0, 2.0, 3.0, 2.5];
let mu = Real::parameter(ParameterId(0));
let prior = Normal::new(Real::scalar(0.0), Real::scalar(10.0)).log_density_at(mu.clone());
let likelihood = Normal::new(mu.clone(), Real::scalar(1.0));
let model = Model::new()
.observe(&data, &likelihood)
.observe_with(&[0.0], |_| prior.clone());
let trace = model.sample(&SamplerConfig::default(), 4);
let mu_draws = trace.predict(&Generator::real(mu));Requirements:
- Rust
1.85+ - Cargo
Build and verify from the repository root:
cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspaceInspect the CLI:
cargo run -p priori-cli -- --helpConsume the facade crate directly from GitHub:
[dependencies]
priori = { git = "https://github.com/winfunc/priori" }| Crate | Purpose |
|---|---|
crates/priori-core |
Symbolic real expressions, bounds, and evaluation |
crates/priori-autodiff |
Reverse-mode gradient derivation over symbolic graphs |
crates/priori-dist |
Continuous and discrete distributions plus support transforms |
crates/priori-sampler |
Samplers, warmup adaptation, and chain orchestration |
crates/priori-optim |
MAP estimation and L-BFGS utilities |
crates/priori-model |
Model assembly, compiled densities, traces, and SBC helpers |
crates/priori-io |
DataFrame loading and prediction/trace serialization |
crates/priori-diag |
Diagnostics summaries and simple visual outputs |
crates/priori-cli |
CLI executable |
crates/priori |
Unified public facade crate |
cargo run -p priori-cli -- fit \
--data /path/to/train.csv \
--response y \
--model "normal(mu, 1)" \
--prior "mu ~ normal(0, 10)" \
--chains 4 \
--iterations 500 \
--warmup 200 \
--seed 2026 \
--output /path/to/trace.csv \
--summaryExample summary output:
name mean std hdi_low hdi_high
mu 2.1700 0.4100 1.4400 2.9900
cargo run -p priori-cli -- fit \
--data /path/to/linreg.csv \
--response y \
--model "normal(alpha + beta * X, 1)" \
--prior "alpha ~ normal(0, 10)" \
--prior "beta ~ normal(0, 10)" \
--chains 2 \
--iterations 300 \
--warmup 150 \
--seed 7 \
--output /path/to/trace.parquetcargo run -p priori-cli -- diagnose --trace /path/to/trace.csvExample output:
param r_hat ess
0 1.001 245.3
1 1.005 198.8
predict currently normalizes a saved trace into Priori's prediction payload and writes it in another format. It does not yet generate posterior predictive quantities from a separate model specification.
cargo run -p priori-cli -- predict \
--trace /path/to/trace.csv \
--output /path/to/predictions.jsoncargo run -p priori-cli -- sbc \
--model "normal(mu, 1)" \
--prior "mu ~ normal(0, 1)" \
--repetitions 100 \
--synthetic-samples 100 \
--iterations 200 \
--warmup 100 \
--chains 2 \
--bins 10 \
--seed 42 \
--output /path/to/sbc.json--model must use the form:
distribution(arg1, arg2, ...)
Supported CLI model distributions:
normal(mu, sigma)poisson(lambda)bernoulli(p)binomial(p, n)wherenmust be a non-negative integer literalgamma(alpha, beta)exponential(rate)lognormal(mu, sigma)laplace(mu, sigma)cauchy(mu, sigma)studentt(nu, mu, sigma)orstudent_t(nu, mu, sigma)
Supported expression features inside model arguments:
- Operators:
+,-,*,/ - Functions:
exp(...),log(...) - Identifiers: prior-defined parameters and input-data column names
Each prior must use the form:
parameter ~ distribution(numeric_args...)
Repeat --prior for multi-parameter models.
Supported CLI prior distributions:
normalcauchylaplacestudenttorstudent_tgammaexponentialbetalognormalorlog_normaluniform
--data and --trace accept:
.csv.tsv.jsonarrays of records.jsonlor.ndjson.parquet.arrowor.ipc-for stdin with format auto-detection
fit --output and predict --output support:
.csv.json.parquet.arrowor.ipc
Output semantics:
.csv,.parquet,.arrow, and.ipccontain tabular samples.jsoncontains a nested payload with bothsamplesandsummariesfitandpredictinclude__chainand__drawcolumns in tabular outputs
Trace-aware commands recognize these metadata aliases:
- Chain:
__chain,chain,chain_id - Draw:
__draw,draw,sample,iteration - Warmup marker:
__warmup,warmup,is_warmup
Interoperability note:
diagnoseandpredictexpect a tabular trace filefit --output *.csv|*.parquet|*.arrow|*.ipcis directly consumable bydiagnoseandpredictfit --output *.jsonis not directly consumable bydiagnoseorpredict
Consume the facade crate directly from GitHub, or use a local path if you are developing inside a checked-out workspace:
[dependencies]
priori = { git = "https://github.com/winfunc/priori" }
# priori = { path = "crates/priori" }Example:
use priori::core::{ParameterId, Real};
use priori::dist::{Distribution, Normal};
use priori::model::{Generator, Model};
use priori::sampler::SamplerConfig;
fn main() {
let data = vec![1.0, 2.0, 3.0, 2.5];
let mu = Real::parameter(ParameterId(0));
let prior = Normal::new(Real::scalar(0.0), Real::scalar(10.0)).log_density_at(mu.clone());
let likelihood = Normal::new(mu.clone(), Real::scalar(1.0));
let model = Model::new()
.observe(&data, &likelihood)
.observe_with(&[0.0], |_| prior.clone());
let trace = model.sample(&SamplerConfig::default(), 4);
let mu_draws = trace.predict(&Generator::real(mu));
println!("draws={}", mu_draws.len());
}- Use
--seedto make CLI runs deterministic - If
--seedis omitted, the CLI generates a fresh random seed and logs it - Use
--verbosefor debug logging - Use
--quietto reduce output to warnings and errors RUST_LOGcan override the default log filter
Standard workspace checks:
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warningsAdditional parity and heavier validation suites:
cargo test -p priori --test rainier_parity -- --ignored
cargo test -p priori --test sbc_rainier_models -- --ignored
cargo test -p priori --test integration_strict -- --ignoredBenchmarks:
cargo bench -p priori- The CLI
sbccommand currently requires exactly one prior specification - The CLI
sbccommand currently supports scalar models without covariate columns - CLI prior arguments must be numeric literals rather than symbolic expressions
- Legacy dense mass matrix construction fails closed on invalid inputs;
MassMatrix::try_denseis the explicit validation path - The public API is still evolving and should be treated as pre-
1.0
MIT. See LICENSE.