Skip to main content

stanli

The Stan Language Interpreter. Compile and sample Stan models with no C++ toolchain on the machine.

PyPI Python License wheels

pip install stanli

That is the whole install. No compiler, no make, no CmdStan checkout, no multi-minute first-run build. One wheel, one shared library, under seven megabytes.

import stanli

model = stanli.Model(stan_file="eight_schools.stan", data="data.json")
draws = model.sample(seed=1, warmup=1000, samples=1000)

draws["mu"].mean()      # one numpy array of draws per constrained parameter

Model preparation takes milliseconds, so the first draw arrives about 20x sooner than a toolchain that compiles C++ per model.

How it works

Every Stan model is a composition of a fixed vocabulary of operations: densities, constraint transforms, linear algebra, elementwise math. stanli ships those precompiled and turns each model into data, a static graph of ops over flat preallocated buffers, instead of generating and compiling C++ per model.

model.stan + data.json
  |  stanc3, the official OCaml compiler, linked into the library
  v
transformed MIR
  |  lowering: transformed data evaluated eagerly, data-bound loops unrolled,
  |            then periodic regions re-rolled back into vectorized ops
  v
op graph over preallocated value/adjoint arenas
  |  forward sweep = log density, reverse sweep = gradient
  v
NUTS with diagonal-metric adaptation -> draws

The graph doubles as the autodiff tape, so a reverse sweep is a backwards loop over an array rather than a walk through a pointer-chasing tape, and steady-state gradient evaluation allocates nothing.

Two things are not reimplemented, which is what makes the results trustworthy: the compiler is the real stanc3, linked in-process, so the Stan language behaves as the official toolchain makes it behave; and the math is unmodified stan-math, the same code CmdStan runs.

Correctness

Nothing here ships on "looks close".

118 of 120 posteriordb models are differentially verified against CmdStan: same model, same data, same evaluation point, comparing the log density and every single gradient component. 45 agree bitwise. The worst deviation across the entire corpus is 2.6e-12 relative.

The two exceptions are documented rather than hidden. sir's ODE solution dips about 1e-9 below a declared lower bound at the shared evaluation point, where CmdStan rejects it too; kronecker_gp matches on the log density and 436 of 438 gradients, differing on the two that flow through eigenvectors of a nearly degenerate covariance matrix.

Full per-model accuracy table: docs/corpus-status.md

Performance

Per-gradient latency against CmdStan, same models, same evaluation point, both sides -O3 with FP contraction pinned off:

model params stanli CmdStan speedup
radon_pooled 3 52.9 us 320.9 us 6.1x
arK 7 2.4 us 12.5 us 5.2x
radon_hierarchical_intercept_centered 391 111.6 us 569.1 us 5.1x
radon_county_intercept 388 89.7 us 431.6 us 4.8x
nes 10 19.7 us 69.3 us 3.5x
eight_schools_noncentered 10 0.23 us 0.74 us 3.3x
election88_full 90 295.3 us 902.0 us 3.0x
bym2_offset_only 3845 39.6 us 114.6 us 2.9x
dogs 3 22.0 us 63.7 us 2.9x
kidscore_momiq 3 1.9 us 4.9 us 2.6x
lsat_model 1006 45.5 us 91.2 us 2.0x
state_space_stochastic_level_stochastic_seasonal 389 17.2 us 26.3 us 1.5x
normal_mixture 3 79.0 us 88.2 us 1.1x
low_dim_gauss_mix 5 88.9 us 98.3 us 1.1x
wells_dist100ars_model 3 17.4 us 19.0 us 1.1x
radon_county 389 83.2 us 82.1 us 1.0x
arma11 4 6.7 us 6.2 us 0.93x
diamonds 26 35.4 us 31.5 us 0.89x
garch11 4 11.2 us 9.7 us 0.86x
hmm_drive_0 6 173.0 us 132.8 us 0.77x
hmm_example 4 36.3 us 27.1 us 0.75x
ldaK2 7 145.9 us 104.1 us 0.71x
iohmm_reg 29 545.2 us 320.3 us 0.59x

The wins come from op granularity. CmdStan's var tape allocates, walks, and frees one node per scalar operation per leapfrog step; stanli pays a fixed cost per op, and a vectorized statement over N elements amortizes that to nothing. Across the whole posteriordb corpus the median is

2.07x and 93 of 119 models are at or above CmdStan.

The losses are honest and understood, and they are all one shape: a recurrence. hmm_*, garch11 and arma11 step through time with each step reading the last one's parameter-dependent result, which nothing can vectorize, so the work is scalar on both sides and CmdStan's generated C++ is the faster way to run scalar work. ldaK2 is a mixture over more than two components, which the fusion pass does not yet widen.

ODE models are the other place stanli is still behind. An ODE right-hand side is the one user function that cannot be inlined at lowering time, since the integrator picks the times; it now compiles into a flat register machine instead of being tree-walked, and the forward sweep keeps the sensitivities it was already computing instead of solving twice. Together that is 29x to 39x faster than the tree-walking interpreter it replaces, which puts lotka_volterra and soil_incubation at 0.58x and 0.63x of CmdStan rather than 0.015x.

Method and full table: docs/benchmarks.md

API

The surface is small on purpose.

import stanli

# A path to a .stan file, or the model source directly.
model = stanli.Model(stan_file="model.stan", data="data.json")
model = stanli.Model(stan_code=src, data={"J": 8, "y": y, "sigma": sigma})

model.n_unconstrained               # length of the unconstrained vector
model.constrained_names             # ['mu', 'tau', 'theta.1', ...]

lp, grad = model.log_prob_grad(q)   # sampling log density and its gradient

draws = model.sample(seed=1, warmup=1000, samples=1000, delta=0.8)
draws["mu"]                         # ndarray of length `samples`

data accepts a path to a JSON file or a dict of Python scalars, lists, and numpy arrays. sample returns one array of constrained draws per scalar parameter, named the way CmdStan names them, so theta declared as vector[8] arrives as theta.1 through theta.8.

Platforms

Wheels for macOS (arm64 and x86_64) and Linux (x86_64 and aarch64, manylinux_2_28). Windows is not built yet; it needs a mingw-w64 toolchain, because stan-math does not build under MSVC.

The installed library is 21.3 MB, which is the trade this design makes: ship the compiler and every kernel once, so that nothing is ever built on the user's machine. Roughly half of that is the embedded stanc3 and somewhat under half is stan-math. The interpreter and NUTS together are about 410 KB.

Status

Early, and deliberately narrow. The sampler is Stan's own NUTS with diagonal-metric adaptation. Known limits, stated plainly:

  • sample() returns declared parameters only. Transformed parameters and generated quantities are computed by the runtime and written by the command line tool, but are not exposed through the Python API yet, so the non-centered eight schools gives you mu, tau, and theta_tilde, not theta.
  • No variational inference, no optimization, no multi-chain threading.
  • No convergence diagnostics. Pair it with ArviZ or similar for now.

What is here is verified against CmdStan model by model, and every number on this page is reproducible from the repository.

Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

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

stanli-0.3.0-py3-none-win_amd64.whl (9.4 MB view details)

Uploaded Python 3Windows x86-64

stanli-0.3.0-py3-none-manylinux_2_28_x86_64.whl (8.4 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ x86-64

stanli-0.3.0-py3-none-manylinux_2_28_aarch64.whl (8.1 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ARM64

stanli-0.3.0-py3-none-macosx_11_0_arm64.whl (7.6 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

stanli-0.3.0-py3-none-macosx_10_15_x86_64.whl (10.4 MB view details)

Uploaded Python 3macOS 10.15+ x86-64

File details

Details for the file stanli-0.3.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: stanli-0.3.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 9.4 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for stanli-0.3.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 4850dfe6b555a1569191b210ae8621c22c5b97d986b25d90850d7dd1685806dd
MD5 b6667e6df10abe25eccaa3a8ddfa165d
BLAKE2b-256 eeb8c1d87adaf4b4bb341689beda9d9d84c5446b064d39d3fcb3668dc0e336a2

See more details on using hashes here.

Provenance

The following attestation bundles were made for stanli-0.3.0-py3-none-win_amd64.whl:

Publisher: wheels.yml on seantalts/stanli

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

File details

Details for the file stanli-0.3.0-py3-none-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for stanli-0.3.0-py3-none-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 94890dc58ae50480472967e2b50576b401609dd9e6338afc0c36ad7d2ab88dc7
MD5 7ce3dac9a5b8bb89181b880b05a458d8
BLAKE2b-256 c24346f3673ff8d90ba8e6aad509012191dea9fa62ff1f1e2158c7d39a99c405

See more details on using hashes here.

Provenance

The following attestation bundles were made for stanli-0.3.0-py3-none-manylinux_2_28_x86_64.whl:

Publisher: wheels.yml on seantalts/stanli

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

File details

Details for the file stanli-0.3.0-py3-none-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for stanli-0.3.0-py3-none-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 5464e9cd1a65702cd8d90d44fadffe6c42d1005d6ce5715c19a176948a42f9b5
MD5 02d4cf0360812351762bc979c5d79056
BLAKE2b-256 8e1ae4b05170a0017dc3aad9fe78e932bc4139d71c0381b81b0d26f1fa0ccf76

See more details on using hashes here.

Provenance

The following attestation bundles were made for stanli-0.3.0-py3-none-manylinux_2_28_aarch64.whl:

Publisher: wheels.yml on seantalts/stanli

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

File details

Details for the file stanli-0.3.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for stanli-0.3.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f3906d1282cbf1e406bcf794596b53b9e6feeaafdf65d0f53f2e49859fef8a68
MD5 4a0c171c6208a7373f593fc6a4d21fc1
BLAKE2b-256 f170c70fe524f5fdd11a45e50399ac1f6b70b3b61128c34f6f0c7df41358c763

See more details on using hashes here.

Provenance

The following attestation bundles were made for stanli-0.3.0-py3-none-macosx_11_0_arm64.whl:

Publisher: wheels.yml on seantalts/stanli

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

File details

Details for the file stanli-0.3.0-py3-none-macosx_10_15_x86_64.whl.

File metadata

File hashes

Hashes for stanli-0.3.0-py3-none-macosx_10_15_x86_64.whl
Algorithm Hash digest
SHA256 90952e68ced08508988c8024c81bc8cac8650cb5e3ee2e2c7c6840111bb6dec0
MD5 2efd81930c7db51d22d5f97ba28be6a9
BLAKE2b-256 58c87e1fffbdbb17f86d07737f17b27eacfaaf59eba3baaf2dc9daa7f901bf63

See more details on using hashes here.

Provenance

The following attestation bundles were made for stanli-0.3.0-py3-none-macosx_10_15_x86_64.whl:

Publisher: wheels.yml on seantalts/stanli

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 Sentry Error logging StatusPage Status page