skoro v0.0.0

What is skoro?

A library for writing a probabilistic model once and running any inference algorithm on it, built for models that live over time.

Skoro is a Rust library for writing probabilistic models once, generatively, and running different inference algorithms on the same model. This page says what that means, who it is for, and the ideas the rest of the library follows from. The argument for each of them is in the charter.

One model, written generatively#

A model is written the way it is thought of: each uncertain quantity, and the conditional distribution it has given the quantities it depends on. Consider a range measurement zz between a vehicle at xx and a target at yy:

z∼μ(dz∣x,y) z \sim \mu(\mathrm{d}z \mid x, y)

That one statement serves three situations. The vehicle and the target can be estimated jointly from zz; the vehicle alone can be estimated from zz when the target is known; and zz can be simulated from given xx and yy. In skoro these are not three models. The model holds the joint distribution, and which quantities are observed is part of each question asked of it, so the same model answers all three, from different threads at once.

Factor-graph libraries, by contrast, describe a model by the residuals of one particular posterior. Each of the three situations needs a different set of factors, and the third, simulation, cannot be expressed at all.

Inference reads the model#

The library records a model’s structure as the model is built, much as automatic differentiation records a computation: which quantities depend on which, what each density term scores, which of its parts are a residual or a whitening factor, which operations can be differentiated to second order. An inference algorithm reads what it needs from that record. If the model meets the algorithm’s assumptions, the algorithm runs; if not, it refuses before it starts and names what is missing and where. The user never describes the model a second time for the algorithm’s benefit.

Models that live over time#

Skoro is built for sequential estimation, where a model is never finished:

  • It grows. Each tick appends new states and measurements, and each kind of measurement can be different.
  • It forgets. States that stop mattering are marginalized onto what remains and retired.
  • It branches. When it is uncertain which target a measurement came from, the model is held under several hypotheses at once, as cheap branches of one another, and an algorithm weighs them.
  • It is shared. One writer builds while estimators run on immutable snapshots of the model, without waiting for each other.

Sub-models are ordinary functions of quantities, placed under namespaces by the code that calls them, so the model of one tick is written once and reused for every tick.

Who it is for#

  1. People developing inference algorithms. Skoro’s first purpose is to make new algorithms easy to write against real models: what an algorithm can discover about a model matters as much as how a model is built.
  2. People building real-time estimators for robots and autonomous systems, on CPU or GPU, from Rust, often under several hypotheses at once.
  3. People doing Bayesian inference with quantities that live on manifolds, such as rotations and poses, or that need second-order derivatives.

What it is not#

  • Not a general probabilistic programming language. Each model has a fixed structure, built forward. Structure that is itself random is handled one level up, over a family of models, rather than by programs whose shape changes as they run, as in Gen.jl.
  • Not a replacement for mature solvers. It does not compete with GTSAM or Ceres on the maturity of its least-squares solvers, nor with Stan or NumPyro on the breadth of its distributions. What it offers is the combination: one generative model, a structure algorithms can read, manifold-aware derivatives of second order, and a model that lives across threads and over time.

The ideas everything follows from#

The charter argues each of these in full:

  • Conditioning belongs to the query, never to the model.
  • Derivatives come from composition. A few primitive operations carry hand-written derivatives, and everything else, distributions and manifold retractions included, is built from them; each operation reports the order of derivative it supports.
  • Static at the edges, dynamic inside. Models are written with typed handles the compiler checks, while everything stored inside is nameable, so models can be serialized and driven from other languages.
  • Randomness is explicit. Every random draw comes from a key, so one seed reproduces a run.
  • Precision belongs to the query. A model commits to no float precision; each estimator chooses its own, and the solves where conditioning matters run on the host in double precision.

How this came to be

The development behind this page, newest first: