Skip to main content

A functional probabilistic programming language that compiles to PyTorch.

Project description

Quivers

A functional probabilistic programming language that compiles to PyTorch.

CI Docs PyPI Python 3.14+ License: MIT

Tutorial · Examples · Guides · API · Semantics


You write Bayesian models in a small, readable DSL and fit them with stochastic variational inference (SVI), NUTS, HMC, or any of nine automatic guides. The program surface should look familiar if you have used Pyro, NumPyro, Stan, or PyMC: declare variables with <-, score observations with observe, integrate out discrete latents with marginalize, get a trainable PyTorch module back. Three things make it different:

  • A weight matrix can carry a real matrix-valued prior (Matrix-Normal, Wishart, LKJ, GP) via an axis-role clause that names which axes the family's joint covariance lives on. Factor analysis, PPCA, BNNs, and the like are written the way they're drawn on the board.
  • Marginalization is a control-flow construct. marginalize z : K <- Categorical(p) in { ... } runs the body once per discrete value of z and aggregates by logsumexp. Standard Rao-Blackwellization, spelled as syntax instead of a runtime flag.
  • Effects are checked at compile time. Every program declares a signature like ! Sample, Score, Marginal, Pure. A ! Pure block that tries to observe is rejected with a typed error before training begins.
object Item : 100

program regression : Item -> Item ! Sample, Score
    sigma  <- HalfNormal(1.0)
    beta_0 <- Normal(0.0, 5.0)
    beta_1 <- Normal(0.0, 2.0)
    let mu = beta_0 + beta_1 * x
    observe y <- Normal(mu, sigma)
    return y

export regression
from quivers.dsl import loads
from quivers.inference import AutoNormalGuide, ELBO, SVI
import torch

program = loads(open("regression.qvr").read())
model   = program.morphism
guide   = AutoNormalGuide(model, observed_names={"y"})
optim   = torch.optim.Adam(guide.parameters(), lr=1e-2)
svi     = SVI(model, guide, optim, ELBO())
for _ in range(2000):
    svi.step(x_data, {"y": y_data})

The full walkthrough is in the tutorial.

What you get

The everyday PPL features you would expect, on a PyTorch backend:

  • Forty distribution families (Normal, Beta, Gamma, Dirichlet, MVN, LKJ, MatrixNormal, GP, Horseshoe, mixtures, normalizing flows, and more).
  • Nine variational guides from mean-field through full-rank multivariate normal, low-rank, mixture, IAF, neural-spline flow, and AutoDAIS.
  • Four inference objectives (ELBO, IWAE, Renyi, VR-IWAE) with reparameterised / score-function / sticking-the-landing / DReG gradient estimators.
  • NUTS and HMC with dual-averaging step-size adaptation and Welford mass-matrix adaptation, plus a WarmupThenHMC hybrid sampler.
  • Marginalized discrete latents as a first-class block (marginalize z : K <- Categorical(p) in { ... }), with logsumexp aggregation handled for you.
  • Plates and grouped marginalization for hierarchical models with vectorized observations and per-row fibration into shared random effects.
  • A 36-example gallery covering regression (Bayesian, Beta, Dirichlet, NegBin, horseshoe, ZIP), latent variable (factor analysis, PPCA, LDA, IRT, PMF, BNN, GMM, VAE), state space (HMM discrete and continuous, linear-Gaussian SSM, deep Markov, AR1, stochastic volatility, changepoint, Weibull survival), language models (RNN, LSTM, GRU, bidirectional, transformer), seq2seq with encoder/decoder, and formal grammars (PCFG, CCG, Lambek, multimodal TLG).

What's distinctive

Most PPLs let you write observe y ~ Normal(mu, sigma). Quivers lets you write the same thing AND a few things ordinary PPLs do not.

  • Typed scoped marginalization. marginalize z : K <- Categorical(p) in { ... } is a syntactic block whose body runs once per discrete value of z, with the per-value scores aggregated by logsumexp. This is the standard Rao-Blackwellisation trick, but spelled as a control-flow construct instead of a runtime flag.
  • Axis-role priors on weights. A weight matrix latent W : Euclidean(D) -> Euclidean(K) can carry a structured prior whose covariance is genuinely matrix-valued: ~ MatrixNormal(loc, row_cov, col_cov) over (dom, cod). The over <axes> clause says which axes the family's joint covariance lives on; the rest are iid. This is the right surface for factor analysis, PPCA, Bayesian neural nets, and other "matrix of weights with prior" models.
  • Exact-likelihood structured families. HMMs and Kalman smoothers compose like ordinary distributions; the forward / forward-backward / smoother passes are wrapped.
  • Compile-time effects. Programs carry an effect signature ! Sample, Score, Marginal, Pure that the compiler checks against the body. A ! Pure block that contains an observe is rejected with a typed error before training begins.
  • Weighted deduction. Chart algorithms (CKY, Earley, Viterbi, A*, Knuth's algorithm, semi-naive Datalog) are exposed as a deduction { atoms ... rule ... semiring ... start ... } block whose chart is a differentiable tensor. Drops in alongside the rest of the language.
  • Structural compression. A four-block pattern (signature { ... } encoder { ... } decoder { ... } loss { ... }) factors out transformers, tree LSTMs, graph NNs, autoregressive LMs, and the vector inside-outside parser as instances of one interface.

What's under the hood (optional reading)

The DSL is a thin layer over a typed categorical surface in src/quivers/. If you want to extend the library, write a new family, prove anything about a model, or read the type errors fluently, the categorical layer is what you read. If you just want to fit models, you can ignore it. The denotational semantics (docs) gives every well-typed program a formal meaning in a $\mathcal{V}$-enriched symmetric monoidal closed category. The implementation rests on enriched category theory (Kelly, 1982), the categorical foundations of probability (Giry, 1982; Fritz, 2020), and the SVI / HMC inference substrate (Hoffman, Blei, Wang & Paisley, 2013; Neal, 2011; Hoffman & Gelman, 2014).

Installation

pip install quivers

Or install from source:

git clone https://github.com/FACTSlab/quivers
cd quivers
pip install -e ".[dev]"

Requirements: Python 3.14+, PyTorch 2.0+, didactic 0.6.0+, panproto 0.45.0+, panproto-grammars-all 0.45.0+.

Learning path

Two parallel tracks, depending on what you want:

  • QVR DSL tutorial for probabilistic-programming users. Seven chapters, model development through inference, side-by-side with PyMC / NumPyro / Stan.
  • Python API tutorial for library developers and category-theory-fluent users. Seven chapters covering the typed categorical surface end to end.

Then:

Project structure

src/quivers/
├── core/           objects, algebras, morphisms, tensor ops, wiring
├── categorical/    functors, natural transformations, adjunctions, monoidal, traced
├── monadic/        monads, comonads, algebras, distributive laws
├── enriched/       ends/coends, Kan extensions, profunctors, Yoneda, Day, optics
├── stochastic/     Markov kernels, Giry monad, grammar parsers, chart algorithms
├── continuous/     distribution families, spaces, flows, monadic programs
├── dsl/            parser (panproto / tree-sitter), AST (didactic Models),
│                   compiler, resolution lenses, Program Theory
├── inference/      registry, guides, objectives, estimators, MCMC, hybrids
├── program.py      Program: wraps morphisms as nn.Module
└── giry.py         GiryMonad, FinStoch

The tree-sitter grammar lives at grammars/qvr/ and is vendored by panproto's panproto-grammars-all distribution.

Contributing

See CONTRIBUTING.md for development setup, code style, and the git workflow. Issues and pull requests welcome at github.com/FACTSlab/quivers.

License

MIT. See LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

quivers-0.8.0.tar.gz (1.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

quivers-0.8.0-py3-none-any.whl (535.3 kB view details)

Uploaded Python 3

File details

Details for the file quivers-0.8.0.tar.gz.

File metadata

  • Download URL: quivers-0.8.0.tar.gz
  • Upload date:
  • Size: 1.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for quivers-0.8.0.tar.gz
Algorithm Hash digest
SHA256 0fbbd11b52398de5a63386d8254aa4a969a197beabb86e0061c730633260ccf4
MD5 8135136deafc33e64dac761de9ea3351
BLAKE2b-256 1cc83eca3a0c90c2f2b8a62152edcc86db66cfa95e13d25ca91935417a28b065

See more details on using hashes here.

Provenance

The following attestation bundles were made for quivers-0.8.0.tar.gz:

Publisher: release.yml on FACTSlab/quivers

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file quivers-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: quivers-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 535.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for quivers-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 59a25afe66ee0fd4d70c3ded83c0b3c0e5375d405d367ea9a35a0193b18be401
MD5 08f397aedf1f980cee54d77583e10a0a
BLAKE2b-256 f14c82cfc4b6450189b5cbd582f07949d2cf2c41fbcd64d9c250e801823d18a8

See more details on using hashes here.

Provenance

The following attestation bundles were made for quivers-0.8.0-py3-none-any.whl:

Publisher: release.yml on FACTSlab/quivers

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page