Open-source project
TuringLang/Turing.jl avatar
TuringLang/Turing.jl

Turing.jl binds its data after the model is written

Bayesian inference with probabilistic programming.

2,265 stars251 forksJuliaMIT

At a glance

What is it?
A Julia package for Bayesian and likelihood-based inference whose worked example separates the model from its observations, and whose contribution rules put every breaking change on a separate long lived branch. The citation block on the page stops mid field, and the DoodlePPL link points at a path named after a different package.
Who is it for?
Turing.jl suits a group doing Bayesian or likelihood based inference who wants one inference layer shared across several model front ends, and who values a small supported surface over a long feature list. Three things to know.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Julia, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • TuringLang/Turing.jl2,265 ★ · +11
Star history
Repository2026-09-112026-11-13
TuringLang/Turing.jl2.3k2.3k

11 Stars in the last 63 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The model function takes predictors, and the observations arrive afterwards

The regression example on the page is short enough to be the clearest statement of the API's central idea.

julia
julia> @model function linear_regression(x)
           α ~ Normal(0, 1)
           β ~ Normal(0, 1)
           σ ~ truncated(Cauchy(0, 3); lower=0)
           μ = α .+ β .* x
           y ~ MvNormal(μ, σ^2 * I)
       end

julia> posterior = linear_regression(x) | (; y = y)

The function is declared with the predictor values as its only argument. The observations are not passed in; they are bound afterwards with the conditioning operator, naming the variable from the model body and supplying the value from the enclosing scope. So a model is written once and conditioned on any data later, which is what makes it possible to reuse a model definition across datasets without re-declaring it, and it is also what lets you build a model object before you have touched your data at all. The sampler is handed the same random generator used to synthesise the observations, so the whole example is reproducible from the seed alone.

The likelihood is a multivariate normal written as a scalar times the identity

One line in that model is worth slowing down on, because it is written in a way that hides what it means. The observation is drawn from a multivariate normal whose covariance is the scale parameter squared multiplied by the identity matrix. That is the diagonal case of a multivariate normal: twenty observations with independent noise of a common variance, written with the multivariate constructor rather than a scalar normal repeated. Nothing is wrong with it, and it generalises, but a reader expecting a correlated twenty dimensional normal would misread it, and the identity matrix in that position is doing the work of a simpler expression. The prior for the scale is a Cauchy distribution truncated at zero, which is a heavy tailed choice for a noise scale: it puts far less mass on very large values than a normal prior would, while still permitting them.

Two differentiation backends are preferred and the rest go through an interface

The page names its preferred automatic differentiation backends for gradient based algorithms: a forward mode library and one built for compiled kernels. Other backends, and it gives a reverse mode library as the example, are not listed directly but are reached through a separate differentiation interface package. So there is a default pair and a fallback route, and the fallback is an abstraction layer rather than a list you maintain yourself. That is the arrangement you would expect from a project whose scope statement says it optimises for correctness and stability over breadth: the supported surface is small and named, and everything else arrives through a shared interface that can change underneath without breaking callers. The same reasoning shows up in the project's description of itself as grant funded research software.

Three ways to write a model, and one link pointing at the wrong path

A model can be written three ways. The primary one is a macro from the separate dynamic probabilistic programming package that the model itself is built on. A second path imports the classic BUGS syntax through a companion package, so a model written for that language can be brought across. The third is graphical, through a browser based language whose link on this page resolves to a documentation path named after the BUGS package rather than after the graphical language, which is the kind of link that works today and will not survive a documentation restructure. All three feed the same inference machinery. Alongside them sit the inference methods themselves: sampling, variational inference, maximum likelihood and maximum a posteriori estimation, plus a documented path for writing a customised algorithm against the package's log density and gradient interface.

Breaking changes accumulate on a branch of their own

The contribution rules are unusually specific and the branch policy is the part worth internalising.

text
Pull requests for non-breaking changes target `main`; breaking changes target `breaking`.

So there is a long lived branch where incompatible changes pile up instead of landing, which means a user who tracks the default branch is insulated from them indefinitely, and a user who wants them has to opt in explicitly. That is a gentler release policy than versioned breaking changes, and it costs something too: the main branch is not the codebase that any single release corresponds to unless the maintainers cut from the other branch. The rest of the rules are equally concrete. Proposed features are discussed in an issue before anyone writes code, focused fixes and small changes may go straight in as pull requests, and maintainers will move a bug report filed in the wrong repository of the organisation to the right one. Review rights are reserved for sustained contributors and people invited by a team member, and an unanswered issue or pull request can be escalated by tagging the maintainers group.

The citation block stops in the middle of a field

At the bottom of the page, under a heading asking you to expand for the reference text, the citation entry ends mid sentence. The digital object identifier field opens with a brace and then the number, and there is no closing brace for the field and no closing brace for the entry. So a reader who copies the block gets invalid reference text, which is a small annoyance for anyone automating the copy. The citation is also maintained in two places: inline on the readme and in a reference file at the repository root. Two papers are cited, and they carry different titles. The newer one, from 2025, describes the package under its Julia name with a long author list. The older one, from a 2018 conference, uses a shorter name and three authors. Nothing wrong with citing both, but a reader who wants the current citation has to know which of the two is current.

A minimum interpreter version given to three decimal places

The setup instruction is precise: install a specific language version or later from the language's own site, open a session, and add the package with the package manager. The version is given as a patch release rather than a major, which tells you the floor was picked for a specific fix and is tested at that level. The repository carries the standard Julia project file, a source directory, an extensions directory, tests, documentation, a formatter configuration, a history file, a reference file, and two agent instruction files. The licence file uses the British spelling of the word while the readme's own licence text uses the standard licence wording, which is harmless. Changes are recorded in two places, a history file in the repository and the release list on the forge, and the badge row links to a coverage service and to a shared community practices document rather than to any company infrastructure.

Editorial conclusion

Turing.jl suits a group doing Bayesian or likelihood based inference who wants one inference layer shared across several model front ends, and who values a small supported surface over a long feature list. Three things to know. Breaking changes are kept on a separate branch rather than cut as versions, so the default branch will not break but no single release corresponds to it. The model's data is bound after the model is declared, which is the design to internalise before writing anything, and it means a model can be built with no data in hand. And the citation block on the page is truncated, so take the reference from the file in the repository root rather than from the readme.

Frequently asked questions

what is turing jl

A general-purpose probabilistic programming package for Bayesian and likelihood-based inference, written in Julia and designed to interoperate with the language's scientific ecosystem. It covers sampling, variational inference, maximum likelihood and maximum a posteriori estimation, plus a documented route to writing a customised inference algorithm.

How does Turing.jl condition a model on its observations?

The model function is declared with only the predictor values as its argument, and the observations are bound afterwards with a conditioning operator that names the variable inside the model body and supplies the value from the enclosing scope. A model can therefore be written before any data is in hand and reused across datasets.

Which version of Julia does Turing.jl require?

A specific patch release or later, installed from the official language site, after which the package is added from a Julia session using the package manager. The readme gives the exact patch number rather than a major version.

Which automatic differentiation backends does Turing.jl use?

Two are named as preferred for gradient based algorithms. Other backends, with a reverse mode library given as the example, are not listed directly but reached through a separate differentiation interface package, so the supported surface stays small and named.

What scope does the Turing.jl project claim for itself?

It describes itself as grant-funded research software that prioritises correctness and stability over broad feature coverage, with new features often developed through research projects or collaborations and reproducible reports of incorrect results guiding further work.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. TuringLang/Turing.jl on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/turinglang-turing-jl.svg)](https://hysenlabs.com/projects/turinglang-turing-jl)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/turinglang-turing-jl.svg)](https://hysenlabs.com/projects/turinglang-turing-jl)

Was this useful?