Skip to content

Commit 25b5087

Browse files
committed
Merge branch 'main' of github.com:TuringLang/Turing.jl into bump_advancedvi_0.5
2 parents 115802d + 19bf7d6 commit 25b5087

File tree

7 files changed

+135
-39
lines changed

7 files changed

+135
-39
lines changed

.github/workflows/Tests.yml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,20 @@ jobs:
3535
- name: "everything else"
3636
args: "--skip mcmc/gibbs.jl mcmc/Inference.jl ad.jl"
3737
runner:
38-
# TODO(mhauru) All the ones below that run on 1.11 should actually be run on 1.
39-
# The current setup is a temporary arrangement to deal with issues where Mooncake
40-
# and Libtask are broken on 1.12.
4138
# Default
42-
- version: '1.11'
39+
- version: '1'
4340
os: ubuntu-latest
4441
num_threads: 1
4542
# Multithreaded
46-
- version: '1.11'
43+
- version: '1'
4744
os: ubuntu-latest
4845
num_threads: 2
4946
# Windows
50-
- version: '1.11'
47+
- version: '1'
5148
os: windows-latest
5249
num_threads: 1
5350
# macOS
54-
- version: '1.11'
51+
- version: '1'
5552
os: macos-latest
5653
num_threads: 1
5754
# Minimum supported Julia version

HISTORY.md

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# 0.42.0
22

3-
## Breaking Changes
3+
## **AdvancedVI 0.6**
44

5-
**AdvancedVI 0.5**
6-
7-
Turing.jl v0.42 updates `AdvancedVI.jl` compatibility to 0.5.
8-
Most of the changes introduced in `[email protected]` are structural, with some changes spilling out into the interface.
5+
Turing.jl v0.42 updates `AdvancedVI.jl` compatibility to 0.6 (we skipped the breaking 0.5 update as it does not introduce new features).
6+
`[email protected]` introduces major structural changes including breaking changes to the interface and multiple new features.
97
The summary of the changes below are the things that affect the end-users of Turing.
108
For a more comprehensive list of changes, please refer to the [changelogs](https://github.com/TuringLang/AdvancedVI.jl/blob/main/HISTORY.md) in `AdvancedVI`.
119

10+
### Breaking Changes
11+
1212
A new level of interface for defining different variational algorithms has been introduced in `AdvancedVI` v0.5. As a result, the function `Turing.vi` now receives a keyword argument `algorithm`. The object `algorithm <: AdvancedVI.AbstractVariationalAlgorithm` should now contain all the algorithm-specific configurations. Therefore, keyword arguments of `vi` that were algorithm-specific such as `objective`, `operator`, `averager` and so on, have been moved as fields of the relevant `<: AdvancedVI.AbstractVariationalAlgorithm` structs.
1313
For example,
1414

@@ -49,6 +49,40 @@ Additionally,
4949

5050
- The default hyperparameters of `DoG`and `DoWG` have been altered.
5151
- The deprecated `[email protected]`-era interface is now removed.
52+
- `estimate_objective` now returns the value to be minimized by the optimization algorithm. For example, for ELBO maximization algorithms, `estimate_objective` will return the *negative ELBO*. This is breaking change from the previous behavior where the ELBO was returns.
53+
54+
### New Features
55+
56+
`[email protected]` adds numerous new features including the following new VI algorithms:
57+
58+
- `KLMinWassFwdBwd`: Also known as "Wasserstein variational inference," this algorithm minimizes the KL divergence under the Wasserstein-2 metric.
59+
- `KLMinNaturalGradDescent`: This algorithm, also known as "online variational Newton," is the canonical "black-box" natural gradient variational inference algorithm, which minimizes the KL divergence via mirror descent under the KL divergence as the Bregman divergence.
60+
- `KLMinSqrtNaturalGradDescent`: This is a recent variant of `KLMinNaturalGradDescent` that operates in the Cholesky-factor parameterization of Gaussians instead of precision matrices.
61+
- `FisherMinBatchMatch`: This algorithm called "batch-and-match," minimizes the variation of the 2nd order fisher divergence via a proximal point-type algorithm.
62+
63+
Any of the new algorithms above can readily be used by simply swappin the `algorithm` keyword argument of `vi`.
64+
For example, to use batch-and-match:
65+
```julia
66+
vi(model, q, n_iters; algorithm=FisherMinBatchMatch())
67+
```
68+
69+
# 0.41.1
70+
71+
The `ModeResult` struct returned by `maximum_a_posteriori` and `maximum_likelihood` can now be wrapped in `InitFromParams()`.
72+
This makes it easier to use the parameters in downstream code, e.g. when specifying initial parameters for MCMC sampling.
73+
For example:
74+
75+
```julia
76+
@model function f()
77+
# ...
78+
end
79+
model = f()
80+
opt_result = maximum_a_posteriori(model)
81+
82+
sample(model, NUTS(), 1000; initial_params=InitFromParams(opt_result))
83+
```
84+
85+
If you need to access the dictionary of parameters, it is stored in `opt_result.params` but note that this field may change in future breaking releases as that Turing's optimisation interface is slated for overhaul in the near future.
5286

5387
# 0.41.0
5488

README.md

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
<p align="center"><img src="https://raw.githubusercontent.com/TuringLang/turinglang.github.io/refs/heads/main/assets/logo/turing-logo.svg" alt="Turing.jl logo" width="200" /></p>
2-
<h1 align="center">Turing.jl</h1>
3-
<p align="center"><i>Probabilistic programming and Bayesian inference in Julia</i></p>
1+
<p align="center">
2+
<picture>
3+
<source media="(prefers-color-scheme: dark)" srcset="https://turinglang.org/assets/logo/turing-logo-dark.svg">
4+
<img src="https://turinglang.org/assets/logo/turing-logo-light.svg" alt="Turing.jl logo" width="300">
5+
</picture>
6+
</p>
7+
<p align="center"><i>Bayesian inference with probabilistic programming</i></p>
48
<p align="center">
59
<a href="https://turinglang.org/"><img src="https://img.shields.io/badge/docs-tutorials-blue.svg" alt="Tutorials" /></a>
610
<a href="https://turinglang.org/Turing.jl/stable"><img src="https://img.shields.io/badge/docs-API-blue.svg" alt="API docs" /></a>
@@ -9,7 +13,7 @@
913
<a href="https://github.com/SciML/ColPrac"><img src="https://img.shields.io/badge/ColPrac-Contributor%27s%20Guide-blueviolet" alt="ColPrac: Contributor's Guide on Collaborative Practices for Community Packages" /></a>
1014
</p>
1115

12-
## 🚀 Get started
16+
## Get started
1317

1418
Install Julia (see [the official Julia website](https://julialang.org/install/); you will need at least Julia 1.10 for the latest version of Turing.jl).
1519
Then, launch a Julia REPL and run:
@@ -23,22 +27,29 @@ You can define models using the `@model` macro, and then perform Markov chain Mo
2327
```julia
2428
julia> using Turing
2529

26-
julia> @model function my_first_model(data)
27-
mean ~ Normal(0, 1)
28-
sd ~ truncated(Cauchy(0, 3); lower=0)
29-
data ~ Normal(mean, sd)
30+
julia> @model function linear_regression(x)
31+
# Priors
32+
α ~ Normal(0, 1)
33+
β ~ Normal(0, 1)
34+
σ² ~ truncated(Cauchy(0, 3); lower=0)
35+
36+
# Likelihood
37+
μ = α .+ β .* x
38+
y ~ MvNormal(μ, σ² * I)
3039
end
3140

32-
julia> model = my_first_model(randn())
41+
julia> x, y = rand(10), rand(10)
3342

34-
julia> chain = sample(model, NUTS(), 1000)
43+
julia> posterior = linear_regression(x) | (; y = y)
44+
45+
julia> chain = sample(posterior, NUTS(), 1000)
3546
```
3647

3748
You can find the main TuringLang documentation at [**https://turinglang.org**](https://turinglang.org), which contains general information about Turing.jl's features, as well as a variety of tutorials with examples of Turing.jl models.
3849

3950
API documentation for Turing.jl is specifically available at [**https://turinglang.org/Turing.jl/stable**](https://turinglang.org/Turing.jl/stable/).
4051

41-
## 🛠️ Contributing
52+
## Contributing
4253

4354
### Issues
4455

@@ -55,20 +66,20 @@ Breaking releases (minor version) should target the `breaking` branch.
5566

5667
If you have not received any feedback on an issue or PR for a while, please feel free to ping `@TuringLang/maintainers` in a comment.
5768

58-
## 💬 Other channels
69+
## Other channels
5970

6071
The Turing.jl userbase tends to be most active on the [`#turing` channel of Julia Slack](https://julialang.slack.com/archives/CCYDC34A0).
6172
If you do not have an invitation to Julia's Slack, you can get one from [the official Julia website](https://julialang.org/slack/).
6273

6374
There are also often threads on [Julia Discourse](https://discourse.julialang.org) (you can search using, e.g., [the `turing` tag](https://discourse.julialang.org/tag/turing)).
6475

65-
## 🔄 What's changed recently?
76+
## What's changed recently?
6677

6778
We publish a fortnightly newsletter summarising recent updates in the TuringLang ecosystem, which you can view on [our website](https://turinglang.org/news/), [GitHub](https://github.com/TuringLang/Turing.jl/issues/2498), or [Julia Slack](https://julialang.slack.com/archives/CCYDC34A0).
6879

6980
For Turing.jl specifically, you can see a full changelog in [`HISTORY.md`](https://github.com/TuringLang/Turing.jl/blob/main/HISTORY.md) or [our GitHub releases](https://github.com/TuringLang/Turing.jl/releases).
7081

71-
## 🧩 Where does Turing.jl sit in the TuringLang ecosystem?
82+
## Where does Turing.jl sit in the TuringLang ecosystem?
7283

7384
Turing.jl is the main entry point for users, and seeks to provide a unified, convenient interface to all of the functionality in the TuringLang (and broader Julia) ecosystem.
7485

@@ -125,5 +136,3 @@ month = feb,
125136
```
126137

127138
</details>
128-
129-
You can see the full list of publications that have cited Turing.jl on [Google Scholar](https://scholar.google.com/scholar?cites=11803241473159708991).

ext/TuringOptimExt.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ function _optimize(
192192
varnames = map(Symbol first, vns_vals_iter)
193193
vals = map(last, vns_vals_iter)
194194
vmat = NamedArrays.NamedArray(vals, varnames)
195-
return Optimisation.ModeResult(vmat, M, -M.minimum, logdensity_optimum)
195+
return Optimisation.ModeResult(vmat, M, -M.minimum, logdensity_optimum, vals_dict)
196196
end
197197

198198
end # module

src/optimisation/Optimisation.jl

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ using ..Turing
44
using NamedArrays: NamedArrays
55
using AbstractPPL: AbstractPPL
66
using DynamicPPL: DynamicPPL
7+
using DocStringExtensions: TYPEDFIELDS
78
using LogDensityProblems: LogDensityProblems
89
using Optimization: Optimization
910
using OptimizationOptimJL: OptimizationOptimJL
@@ -154,13 +155,22 @@ end
154155
V<:NamedArrays.NamedArray,
155156
M<:NamedArrays.NamedArray,
156157
O<:Optim.MultivariateOptimizationResults,
157-
S<:NamedArrays.NamedArray
158+
S<:NamedArrays.NamedArray,
159+
P<:AbstractDict{<:VarName,<:Any}
158160
}
159161
160162
A wrapper struct to store various results from a MAP or MLE estimation.
163+
164+
## Fields
165+
166+
$(TYPEDFIELDS)
161167
"""
162-
struct ModeResult{V<:NamedArrays.NamedArray,O<:Any,M<:OptimLogDensity} <:
163-
StatsBase.StatisticalModel
168+
struct ModeResult{
169+
V<:NamedArrays.NamedArray,
170+
O<:Any,
171+
M<:OptimLogDensity,
172+
P<:AbstractDict{<:AbstractPPL.VarName,<:Any},
173+
} <: StatsBase.StatisticalModel
164174
"A vector with the resulting point estimates."
165175
values::V
166176
"The stored optimiser results."
@@ -169,6 +179,8 @@ struct ModeResult{V<:NamedArrays.NamedArray,O<:Any,M<:OptimLogDensity} <:
169179
lp::Float64
170180
"The evaluation function used to calculate the output."
171181
f::M
182+
"Dictionary of parameter values"
183+
params::P
172184
end
173185

174186
function Base.show(io::IO, ::MIME"text/plain", m::ModeResult)
@@ -182,6 +194,15 @@ function Base.show(io::IO, m::ModeResult)
182194
return show(io, m.values.array)
183195
end
184196

197+
"""
198+
InitFromParams(m::ModeResult)
199+
200+
Initialize a model from the parameters stored in a `ModeResult`.
201+
"""
202+
function DynamicPPL.InitFromParams(m::ModeResult)
203+
return DynamicPPL.InitFromParams(m.params)
204+
end
205+
185206
# Various StatsBase methods for ModeResult
186207

187208
"""
@@ -355,9 +376,13 @@ function ModeResult(log_density::OptimLogDensity, solution::SciMLBase.Optimizati
355376
iters = map(AbstractPPL.varname_and_value_leaves, keys(vals), values(vals))
356377
vns_vals_iter = mapreduce(collect, vcat, iters)
357378
syms = map(Symbol first, vns_vals_iter)
358-
vals = map(last, vns_vals_iter)
379+
split_vals = map(last, vns_vals_iter)
359380
return ModeResult(
360-
NamedArrays.NamedArray(vals, syms), solution, -solution.objective, log_density
381+
NamedArrays.NamedArray(split_vals, syms),
382+
solution,
383+
-solution.objective,
384+
log_density,
385+
vals,
361386
)
362387
end
363388

test/ad.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ using Test
1010
using ..Models: gdemo_default
1111
import ForwardDiff, ReverseDiff
1212

13-
# Detect if prerelease version, if so, we skip some tests
14-
const IS_PRERELEASE = !isempty(VERSION.prerelease)
15-
const INCLUDE_MOONCAKE = !IS_PRERELEASE
16-
13+
# Skip Mooncake on 1.12 as it is not compatible yet
14+
const INCLUDE_MOONCAKE = VERSION < v"1.12"
1715
if INCLUDE_MOONCAKE
1816
import Pkg
1917
Pkg.add("Mooncake")

test/optimisation/Optimisation.jl

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ using Turing
101101
@test result.optim_result.retcode == Optimization.ReturnCode.Success
102102
end
103103
@test isapprox(result.lp, true_logp, atol=0.01)
104+
# check that the parameter dict matches the NamedArray
105+
# NOTE: This test only works for models where all parameters are identity
106+
# varnames AND real-valued. Thankfully, this is true for `gdemo`.
107+
@test length(only(result.values.dicts)) == length(result.params)
108+
for (k, index) in only(result.values.dicts)
109+
@test result.params[AbstractPPL.VarName{k}()] == result.values.array[index]
110+
end
104111
end
105112

106113
@testset "MLE" begin
@@ -546,6 +553,26 @@ using Turing
546553
end
547554
end
548555

556+
@testset "using ModeResult to initialise MCMC" begin
557+
@model function f(y)
558+
μ ~ Normal(0, 1)
559+
σ ~ Gamma(2, 1)
560+
return y ~ Normal(μ, σ)
561+
end
562+
model = f(randn(10))
563+
mle = maximum_likelihood(model)
564+
# TODO(penelopeysm): This relies on the fact that HMC does indeed
565+
# use the initial_params passed to it. We should use something
566+
# like a StaticSampler (see test/mcmc/Inference) to make this more
567+
# robust.
568+
chain = sample(
569+
model, HMC(0.1, 10), 2; initial_params=InitFromParams(mle), num_warmup=0
570+
)
571+
# Check that those parameters were indeed used as initial params
572+
@test chain[][1] == mle.params[@varname(µ)]
573+
@test chain[][1] == mle.params[@varname(σ)]
574+
end
575+
549576
# Issue: https://discourse.julialang.org/t/turing-mixture-models-with-dirichlet-weightings/112910
550577
@testset "Optimization with different linked dimensionality" begin
551578
@model demo_dirichlet() = x ~ Dirichlet(2 * ones(3))
@@ -621,7 +648,13 @@ using Turing
621648
m = saddle_model()
622649
optim_ld = Turing.Optimisation.OptimLogDensity(m, DynamicPPL.getloglikelihood)
623650
vals = Turing.Optimisation.NamedArrays.NamedArray([0.0, 0.0])
624-
m = Turing.Optimisation.ModeResult(vals, nothing, 0.0, optim_ld)
651+
m = Turing.Optimisation.ModeResult(
652+
vals,
653+
nothing,
654+
0.0,
655+
optim_ld,
656+
Dict{AbstractPPL.VarName,Float64}(@varname(x) => 0.0, @varname(y) => 0.0),
657+
)
625658
ct = coeftable(m)
626659
@assert isnan(ct.cols[2][1])
627660
@assert ct.colnms[end] == "Error notes"

0 commit comments

Comments
 (0)