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 eight megabytes. Model preparation takes milliseconds, so the first draw arrives about 20x sooner than a toolchain that compiles C++ per model.

import stanli

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

fit["mu"].mean()        # every draw of a column, chains concatenated
fit.draws("mu")         # (chains, draws), for a trace plot

Chains and convergence

Four chains by default, run in parallel, because R-hat needs more than one and a single-chain run cannot be checked for convergence at all. Eight schools does all four in about 70 ms. Threading changes nothing about the answer: each chain owns its executor and its RNG stream, so the draws come out byte-identical to a sequential run.

print(fit.summary())
name                Mean       MCSE     StdDev         5%        50%        95%   ESS_bulk   ESS_tail      R_hat
mu                4.4600     0.0532     3.1705    -0.7414     4.5519     9.5384       3586       2847      1.000
tau               3.4752     0.0635     3.1612     0.2192     2.6680     9.6313       2160       1874      1.001

R-hat is rank-normalized split-R-hat and ESS is the bulk/tail pair (Vehtari et al. 2021), computed by stan's own estimators, so the numbers agree with stansummary rather than approximating it.

print(fit.diagnose())
No divergent transitions.
No transitions saturated the maximum treedepth of 10.
E-BFMI is above 0.3 in every chain.
R-hat is below 1.01 for every parameter (worst 1.002, theta.6).
Bulk ESS is at least 100 per chain for every parameter (worst 2160, tau).
Tail ESS is at least 100 per chain for every parameter (worst 1874, tau).
No problems detected.

Those are the checks a Bayesian workflow actually turns on, including E-BFMI, the one that catches a badly explored heavy tail, which R-hat and ESS are both blind to. The pieces are reachable individually too: fit.divergences, fit.max_treedepth_hits, fit.stepsize and fit.ebfmi() are per-chain arrays, and fit.to_arviz() hands off an InferenceData with the sampler stats attached.

The mode, and where to start

r = model.optimize(seed=1)
r["mu"], r.lp          # every CSV column at the mode, and the lp there
r.unconstrained        # the point on the sampler's scale

fit = model.sample(inits=r.unconstrained)   # start the chains there

L-BFGS, stan's own, the one behind CmdStan's optimize. It returns the posterior mode. CmdStan's optimize defaults to jacobian=0, the penalized maximum likelihood, and stanli cannot offer that: the change-of-variables Jacobian is folded into the graph when the model is lowered. jacobian=False raises rather than quietly handing back the other quantity.

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. The graph doubles as the autodiff tape, so a reverse sweep is a backwards loop over an array, 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, 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. 41 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 45.5 us 320.9 us 7.0x
arK 7 1.8 us 12.5 us 7.0x
radon_hierarchical_intercept_centered 391 97.1 us 569.1 us 5.9x
radon_county_intercept 388 81.5 us 431.6 us 5.3x
nes 10 16.1 us 69.3 us 4.3x
eight_schools_noncentered 10 0.27 us 0.74 us 2.8x
election88_full 90 256.1 us 902.0 us 3.5x
bym2_offset_only 3845 40.2 us 114.6 us 2.9x
dogs 3 8.0 us 63.7 us 8.0x
kidscore_momiq 3 1.5 us 4.9 us 3.2x
lsat_model 1006 41.9 us 91.2 us 2.2x
state_space_stochastic_level_stochastic_seasonal 389 17.3 us 26.3 us 1.5x
hmm_example 4 21.0 us 27.1 us 1.3x
garch11 4 6.9 us 9.7 us 1.4x
hmm_drive_0 6 119.7 us 132.8 us 1.1x
normal_mixture 3 79.8 us 88.2 us 1.1x
low_dim_gauss_mix 5 90.7 us 98.3 us 1.1x
wells_dist100ars_model 3 17.0 us 19.0 us 1.1x
iohmm_reg 29 492.9 us 320.3 us 0.65x
radon_county 389 73.4 us 82.1 us 1.1x
arma11 4 4.5 us 6.2 us 1.4x
diamonds 26 31.2 us 31.5 us 1.0x
ldaK2 7 99.5 us 104.1 us 1.1x

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.18x and

109 of 119 models are at or above CmdStan.

The former worst class -- recurrences -- mostly crossed parity when the runtime started compiling them. The hmm_* models and garch11 step through time with each step reading the last one's parameter-dependent result, which nothing can vectorize, so each model now compiles its recurrence into a register program with a generated derivative program alongside. iohmm_reg remains the exception at 0.65x. The largest other current losses are ODE models at 0.53-0.75x, whose right-hand side runs through the register machine where CmdStan runs native code, two latent-regression IRT shapes at 0.74-0.79x, and multi_occupancy at 0.79x. The smaller gaps are Mh_model at 0.89x, dogs_nonhierarchical at 0.92x, and Mb_model at 0.94x. Packed row reductions now put both LDA mixture widths at or above parity.

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

fit = model.sample(seed=1, warmup=1000, samples=1000, delta=0.8)
fit["theta.1"]                      # ndarray, chains concatenated

data accepts a path to a JSON file or a dict of Python scalars, lists, and numpy arrays. sample returns every column CmdStan's CSV would carry (constrained parameters, transformed parameters, generated quantities, with RNG draws streamed per chain), named the way CmdStan names them, so theta declared as vector[8] arrives as theta.1 through theta.8. Sampler columns (lp__, divergent__, ...) are reachable by name too.

Platforms

Wheels for macOS (arm64 and x86_64), Linux (x86_64 and aarch64, manylinux_2_28) and Windows (x86_64). The Windows wheel is built under mingw-w64, because stan-math does not build under MSVC (the same reason RStan ships through RTools), and bundles stanc.exe as a subprocess instead of embedding the compiler; the API works the same way either way.

The installed library is 29.7 MB: over half of it is the density kernels, about a quarter the embedded stanc3, and the interpreter and NUTS together are about 410 KB. That is the trade this design makes: ship the compiler and every kernel once, so nothing is ever built on the user's machine.

Limits

Stated plainly:

  • The sampler is Stan's own NUTS with diagonal-metric adaptation, and optimize() is Stan's L-BFGS. No variational inference or Pathfinder yet.
  • inits are on the unconstrained scale. Constrained inits would need the inverse parameter transforms, which do not exist here yet.
  • optimize(jacobian=False) (CmdStan's default penalized maximum likelihood) raises; see above.

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

Metadata

Release files for stanli 0.8.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for stanli 0.8.5
File
stanli-0.8.5-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
stanli-0.8.5-py3-none-manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.28+ x86-64 Details
stanli-0.8.5-py3-none-manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.28+ ARM64 Details
stanli-0.8.5-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details
stanli-0.8.5-py3-none-macosx_10_15_x86_64.whl Python 3 none macOS 10.15+ x86-64 Details

Total release size: 55.8 MB

Release files / stanli-0.8.5-py3-none-win_amd64.whl

Download URL stanli-0.8.5-py3-none-win_amd64.whl
Size 12.3 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
25c7ef5f1c2de322aa0cd3814706f995373dc13dc2cc13b85998e5b1622f90a8
BLAKE2b-256 checksum
How to use checksums
7aea1f831fd89ab3a84180e6aa8a171671209af293fd3126ebf59b7c065fd10c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

Release files / stanli-0.8.5-py3-none-manylinux_2_28_x86_64.whl

Download URL stanli-0.8.5-py3-none-manylinux_2_28_x86_64.whl
Size 11.4 MB
Tags Linux glibc 2.28+ x86-64 Python 3
SHA-256 checksum
How to use checksums
cb890949bac3a575cac7a33e25c5056b99e0037ae5382e385490f7c179e4bfbe
BLAKE2b-256 checksum
How to use checksums
de6a89b21f4265a31ff838df88cc0f93bb93312415fd80ac89315ddc410574da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

Release files / stanli-0.8.5-py3-none-manylinux_2_28_aarch64.whl

Download URL stanli-0.8.5-py3-none-manylinux_2_28_aarch64.whl
Size 10.8 MB
Tags Linux glibc 2.28+ ARM64 Python 3
SHA-256 checksum
How to use checksums
ab14e6365ee1738b3f69b2bd967cf29354fe0b8bad440e1605859aedd81b130a
BLAKE2b-256 checksum
How to use checksums
4934a3f78f4333414d8bce4306a02fb7469fe59e6cc348a9191aa05eb3f6b597
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

Release files / stanli-0.8.5-py3-none-macosx_11_0_arm64.whl

Download URL stanli-0.8.5-py3-none-macosx_11_0_arm64.whl
Size 9.1 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
b1f7335429c129c5d557b8afd0284e0f43f39329a7d8014fab5219a93311389f
BLAKE2b-256 checksum
How to use checksums
22348e72f2ace93d6b2767ca4aee37c9ffb31ee69be96e1ed16922c7582c0673
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

Release files / stanli-0.8.5-py3-none-macosx_10_15_x86_64.whl

Download URL stanli-0.8.5-py3-none-macosx_10_15_x86_64.whl
Size 12.3 MB
Tags Python 3 macOS 10.15+ x86-64
SHA-256 checksum
How to use checksums
63fcf1f6839e2cba06f6258897427d8493c6dbea1c325b8199a683d97548866a
BLAKE2b-256 checksum
How to use checksums
78fa0ba02b974f70f64f8c8ba5c3ff2c7f44ca5b82c90675eba7f1645514ed30
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

Release history Release notifications | RSS feed

0.18.1

5 release files

0.17.1

5 release files

0.17.0

5 release files

0.16.0

5 release files

0.15.0

5 release files

0.14.4

5 release files

0.14.3

5 release files

0.14.1

5 release files

0.14.0

5 release files

0.13.0

5 release files

0.10.0

5 release files

0.9.6

5 release files

0.9.5

5 release files

0.9.4

5 release files

0.9.3

5 release files

0.9.2

5 release files

0.9.1

5 release files

This release

0.8.5 This release

5 release files

0.8.4

5 release files

0.8.3

5 release files

0.8.2

5 release files

0.8.1

5 release files

0.8.0

5 release files

0.7.2

5 release files

0.7.1

5 release files

0.7.0

5 release files

0.6.1

5 release files

0.6.0

5 release files

0.5.1

5 release files

0.5.0

5 release files

0.4.0

5 release files

0.3.0

5 release files

0.2.0

4 release files

0.1.0

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page