Skip to content

Commit 2a6e067

Browse files
penelopeysmmhauru
andauthored
[breaking] v0.40.0 (#2587)
* [no ci] Bump to v0.40.0 * Uncomment tests that should be there * Support DPPL 0.37 (#2550) * First efforts towards DPPL 0.37 compat, WIP * More DPPL 0.37 compat work, WIP * Add [sources] for [email protected] * Remove context argument from `LogDensityFunction` * Fix MH * Remove spurious logging * Remove residual OptimizationContext * Delete files that were removed in previous releases * Fix typo * Simplify ESS * Fix LDF * Fix Prior(), fix a couple more imports * fixes * actually fix prior * Remove extra return value from tilde_assume * fix ldf * actually fix prior * fix HMC log-density * fix ldf * fix make_evaluate_... * more fixes for evaluate!! * fix hmc * fix run_ad * even more fixes (oh goodness when will this end) * more fixes * fix * more fix fix fix * fix return values of tilde pipeline * even more fixes * Fix missing import * More MH fixes * Fix conversion * don't think it really needs those type params * implement copy for LogPriorWithoutJacAcc * Even more fixes * More fixes; I think the remaining failures are pMCMC related * Fix merge * DPPL 0.37 compat for particle MCMC (#2625) * Progress in DPPL 0.37 compat for particle MCMC * WIP PMCMC work * Gibbs fixes for DPPL 0.37 (plus tiny bugfixes for ESS + HMC) (#2628) * Obviously this single commit will make Gibbs work * Fixes for ESS * Fix HMC call * improve some comments * Fixes to ProduceLogLikelihoodAccumulator * Use LogProbAccumulator for ProduceLogLikelihoodAccumulator * use get_conditioned_gibbs --------- Co-authored-by: Penelope Yong <[email protected]> * "Fixes" for PG-in-Gibbs (#2629) * WIP PMCMC work * Fixes to ProduceLogLikelihoodAccumulator * inline definition of `set_retained_vns_del!` * Fix ProduceLogLikelihoodAcc * Remove all uses of `set_retained_vns_del!` * Use nice functions * Remove PG tests with dynamic number of Gibbs-conditioned-observations * Fix essential/container tests * Update pMCMC implementation as per discussion * remove extra printing statements * revert unneeded changes * Add back (some kind of) dynamic model test * fix rebase * Add a todo comment for dynamic model tests --------- Co-authored-by: Markus Hauru <[email protected]> * Use accumulators to fix all logp calculations when sampling (#2630) * Use new `getlogjoint` for optimisation * Change getlogjoint -> getlogjoint_internal where needed * Enforce re-evaluation when constructing `Transition` * fix tests * Remove extra evaluations from SGLD and SGHMC * Remove dead `transitions_from_chain` method (used to be part of `predict`) * metadata -> getstats_with_lp * Clean up some stray getlogp * InitContext isn't for 0.37, update comments * Fix merge * Do not re-evaluate model for Prior (#2644) * Allow Prior to skip model re-evaluation * remove unneeded `default_chain_type` method * add a test * add a likelihood term too * why not test correctness while we're at it * No need to test AD for SamplingContext{<:HMC} (#2645) * change breaking -> main * Remove calls to resetlogp!! & add changelog (#2650) * Remove calls to resetlogp!! * Add a changelog for 0.40 * Update HISTORY.md Co-authored-by: Markus Hauru <[email protected]> --------- Co-authored-by: Markus Hauru <[email protected]> * Remove `[sources]` * Unify Turing `Transition`s, fix some tests (#2651) * Unify `Transition` methods * Add tests * Add same test for SGLD/SGHMC * Refactor so that it's nice and organised * Fix failing test on 1.10 * just increase the atol * Make addlogprob test more robust * Remove stray `@show` Co-authored-by: Markus Hauru <[email protected]> --------- Co-authored-by: Markus Hauru <[email protected]> * Update changelog for PG in Gibbs --------- Co-authored-by: Penelope Yong <[email protected]> --------- Co-authored-by: Markus Hauru <[email protected]>
1 parent d75e6f2 commit 2a6e067

34 files changed

+952
-902
lines changed

HISTORY.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,84 @@
1+
# 0.40.0
2+
3+
## Breaking changes
4+
5+
**DynamicPPL 0.37**
6+
7+
Turing.jl v0.40 updates DynamicPPL compatibility to 0.37.
8+
The summary of the changes provided here is intended for end-users of Turing.
9+
If you are a package developer, or would otherwise like to understand these changes in-depth, please see [the DynamicPPL changelog](https://github.com/TuringLang/DynamicPPL.jl/blob/main/HISTORY.md#0370).
10+
11+
- **`@submodel`** is now completely removed; please use `to_submodel`.
12+
13+
- **Prior and likelihood calculations** are now completely separated in Turing. Previously, the log-density used to be accumulated in a single field and thus there was no clear way to separate prior and likelihood components.
14+
15+
+ **`@addlogprob! f`**, where `f` is a float, now adds to the likelihood by default.
16+
+ You can instead use **`@addlogprob! (; logprior=x, loglikelihood=y)`** to control which log-density component to add to.
17+
+ This means that usage of `PriorContext` and `LikelihoodContext` is no longer needed, and these have now been removed.
18+
- The special **`__context__`** variable has been removed. If you still need to access the evaluation context, it is now available as `__model__.context`.
19+
20+
**Log-density in chains**
21+
22+
When sampling from a Turing model, the resulting `MCMCChains.Chains` object now contains not only the log-joint (accessible via `chain[:lp]`) but also the log-prior and log-likelihood (`chain[:logprior]` and `chain[:loglikelihood]` respectively).
23+
24+
These values now correspond to the log density of the sampled variables exactly as per the model definition / user parameterisation and thus will ignore any linking (transformation to unconstrained space).
25+
For example, if the model is `@model f() = x ~ LogNormal()`, `chain[:lp]` would always contain the value of `logpdf(LogNormal(), x)` for each sampled value of `x`.
26+
Previously these values could be incorrect if linking had occurred: some samplers would return `logpdf(Normal(), log(x))` i.e. the log-density with respect to the transformed distribution.
27+
28+
**Gibbs sampler**
29+
30+
When using Turing's Gibbs sampler, e.g. `Gibbs(:x => MH(), :y => HMC(0.1, 20))`, the conditioned variables (for example `y` during the MH step, or `x` during the HMC step) are treated as true observations.
31+
Thus the log-density associated with them is added to the likelihood.
32+
Previously these would effectively be added to the prior (in the sense that if `LikelihoodContext` was used they would be ignored).
33+
This is unlikely to affect users but we mention it here to be explicit.
34+
This change only affects the log probabilities as the Gibbs component samplers see them; the resulting chain will include the usual log prior, likelihood, and joint, as described above.
35+
36+
**Particle Gibbs**
37+
38+
Previously, only 'true' observations (i.e., `x ~ dist` where `x` is a model argument or conditioned upon) would trigger resampling of particles.
39+
Specifically, there were two cases where resampling would not be triggered:
40+
41+
- Calls to `@addlogprob!`
42+
- Gibbs-conditioned variables: e.g. `y` in `Gibbs(:x => PG(20), :y => MH())`
43+
44+
Turing 0.40 changes this such that both of the above cause resampling.
45+
(The second case follows from the changes to the Gibbs sampler, see above.)
46+
47+
This release also fixes a bug where, if the model ended with one of these statements, their contribution to the particle weight would be ignored, leading to incorrect results.
48+
49+
The changes above also mean that certain models that previously worked with PG-within-Gibbs may now error.
50+
Specifically this is likely to happen when the dimension of the model is variable.
51+
For example:
52+
53+
```julia
54+
@model function f()
55+
x ~ Bernoulli()
56+
if x
57+
y1 ~ Normal()
58+
else
59+
y1 ~ Normal()
60+
y2 ~ Normal()
61+
end
62+
# (some likelihood term...)
63+
end
64+
sample(f(), Gibbs(:x => PG(20), (:y1, :y2) => MH()), 100)
65+
```
66+
67+
This sampler now cannot be used for this model because depending on which branch is taken, the number of observations will be different.
68+
To use PG-within-Gibbs, the number of observations that the PG component sampler sees must be constant.
69+
Thus, for example, this will still work if `x`, `y1`, and `y2` are grouped together under the PG component sampler.
70+
71+
If you absolutely require the old behaviour, we recommend using Turing.jl v0.39, but also thinking very carefully about what the expected behaviour of the model is, and checking that Turing is sampling from it correctly (note that the behaviour on v0.39 may in general be incorrect because of the fact that Gibbs-conditioned variables did not trigger resampling).
72+
We would also welcome any GitHub issues highlighting such problems.
73+
Our support for dynamic models is incomplete and is liable to undergo further changes.
74+
75+
## Other changes
76+
77+
- Sampling using `Prior()` should now be about twice as fast because we now avoid evaluating the model twice on every iteration.
78+
- `Turing.Inference.Transition` now has different fields.
79+
If `t isa Turing.Inference.Transition`, `t.stat` is always a NamedTuple, not `nothing` (if it genuinely has no information then it's an empty NamedTuple).
80+
Furthermore, `t.lp` has now been split up into `t.logprior` and `t.loglikelihood` (see also 'Log-density in chains' section above).
81+
182
# 0.39.10
283

384
Added a compatibility entry for DataStructures v0.19.

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "Turing"
22
uuid = "fce5fe82-541a-59a6-adf8-730c64b5f9a0"
3-
version = "0.39.10"
3+
version = "0.40.0"
44

55
[deps]
66
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
@@ -64,7 +64,7 @@ Distributions = "0.25.77"
6464
DistributionsAD = "0.6"
6565
DocStringExtensions = "0.8, 0.9"
6666
DynamicHMC = "3.4"
67-
DynamicPPL = "0.36.3"
67+
DynamicPPL = "0.37"
6868
EllipticalSliceSampling = "0.5, 1, 2"
6969
ForwardDiff = "0.10.3, 1"
7070
Libtask = "0.9.3"

ext/TuringDynamicHMCExt.jl

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,12 @@ function DynamicPPL.initialstep(
5858
# Ensure that initial sample is in unconstrained space.
5959
if !DynamicPPL.islinked(vi)
6060
vi = DynamicPPL.link!!(vi, model)
61-
vi = last(DynamicPPL.evaluate!!(model, vi, DynamicPPL.SamplingContext(rng, spl)))
61+
vi = last(DynamicPPL.evaluate!!(model, vi))
6262
end
6363

6464
# Define log-density function.
6565
= DynamicPPL.LogDensityFunction(
66-
model,
67-
vi,
68-
DynamicPPL.SamplingContext(spl, DynamicPPL.DefaultContext());
69-
adtype=spl.alg.adtype,
66+
model, DynamicPPL.getlogjoint_internal, vi; adtype=spl.alg.adtype
7067
)
7168

7269
# Perform initial step.
@@ -76,12 +73,9 @@ function DynamicPPL.initialstep(
7673
steps = DynamicHMC.mcmc_steps(results.sampling_logdensity, results.final_warmup_state)
7774
Q, _ = DynamicHMC.mcmc_next_step(steps, results.final_warmup_state.Q)
7875

79-
# Update the variables.
80-
vi = DynamicPPL.unflatten(vi, Q.q)
81-
vi = DynamicPPL.setlogp!!(vi, Q.ℓq)
82-
8376
# Create first sample and state.
84-
sample = Turing.Inference.Transition(model, vi)
77+
vi = DynamicPPL.unflatten(vi, Q.q)
78+
sample = Turing.Inference.Transition(model, vi, nothing)
8579
state = DynamicNUTSState(ℓ, vi, Q, steps.H.κ, steps.ϵ)
8680

8781
return sample, state
@@ -100,12 +94,9 @@ function AbstractMCMC.step(
10094
steps = DynamicHMC.mcmc_steps(rng, spl.alg.sampler, state.metric, ℓ, state.stepsize)
10195
Q, _ = DynamicHMC.mcmc_next_step(steps, state.cache)
10296

103-
# Update the variables.
104-
vi = DynamicPPL.unflatten(vi, Q.q)
105-
vi = DynamicPPL.setlogp!!(vi, Q.ℓq)
106-
10797
# Create next sample and state.
108-
sample = Turing.Inference.Transition(model, vi)
98+
vi = DynamicPPL.unflatten(vi, Q.q)
99+
sample = Turing.Inference.Transition(model, vi, nothing)
109100
newstate = DynamicNUTSState(ℓ, vi, Q, state.metric, state.stepsize)
110101

111102
return sample, newstate

ext/TuringOptimExt.jl

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ function Optim.optimize(
3434
options::Optim.Options=Optim.Options();
3535
kwargs...,
3636
)
37-
ctx = Optimisation.OptimizationContext(DynamicPPL.LikelihoodContext())
38-
f = Optimisation.OptimLogDensity(model, ctx)
37+
f = Optimisation.OptimLogDensity(model, DynamicPPL.getloglikelihood)
3938
init_vals = DynamicPPL.getparams(f.ldf)
4039
optimizer = Optim.LBFGS()
4140
return _mle_optimize(model, init_vals, optimizer, options; kwargs...)
@@ -57,8 +56,7 @@ function Optim.optimize(
5756
options::Optim.Options=Optim.Options();
5857
kwargs...,
5958
)
60-
ctx = Optimisation.OptimizationContext(DynamicPPL.LikelihoodContext())
61-
f = Optimisation.OptimLogDensity(model, ctx)
59+
f = Optimisation.OptimLogDensity(model, DynamicPPL.getloglikelihood)
6260
init_vals = DynamicPPL.getparams(f.ldf)
6361
return _mle_optimize(model, init_vals, optimizer, options; kwargs...)
6462
end
@@ -74,8 +72,8 @@ function Optim.optimize(
7472
end
7573

7674
function _mle_optimize(model::DynamicPPL.Model, args...; kwargs...)
77-
ctx = Optimisation.OptimizationContext(DynamicPPL.LikelihoodContext())
78-
return _optimize(Optimisation.OptimLogDensity(model, ctx), args...; kwargs...)
75+
f = Optimisation.OptimLogDensity(model, DynamicPPL.getloglikelihood)
76+
return _optimize(f, args...; kwargs...)
7977
end
8078

8179
"""
@@ -104,8 +102,7 @@ function Optim.optimize(
104102
options::Optim.Options=Optim.Options();
105103
kwargs...,
106104
)
107-
ctx = Optimisation.OptimizationContext(DynamicPPL.DefaultContext())
108-
f = Optimisation.OptimLogDensity(model, ctx)
105+
f = Optimisation.OptimLogDensity(model, DynamicPPL.getlogjoint)
109106
init_vals = DynamicPPL.getparams(f.ldf)
110107
optimizer = Optim.LBFGS()
111108
return _map_optimize(model, init_vals, optimizer, options; kwargs...)
@@ -127,8 +124,7 @@ function Optim.optimize(
127124
options::Optim.Options=Optim.Options();
128125
kwargs...,
129126
)
130-
ctx = Optimisation.OptimizationContext(DynamicPPL.DefaultContext())
131-
f = Optimisation.OptimLogDensity(model, ctx)
127+
f = Optimisation.OptimLogDensity(model, DynamicPPL.getlogjoint)
132128
init_vals = DynamicPPL.getparams(f.ldf)
133129
return _map_optimize(model, init_vals, optimizer, options; kwargs...)
134130
end
@@ -144,9 +140,10 @@ function Optim.optimize(
144140
end
145141

146142
function _map_optimize(model::DynamicPPL.Model, args...; kwargs...)
147-
ctx = Optimisation.OptimizationContext(DynamicPPL.DefaultContext())
148-
return _optimize(Optimisation.OptimLogDensity(model, ctx), args...; kwargs...)
143+
f = Optimisation.OptimLogDensity(model, DynamicPPL.getlogjoint)
144+
return _optimize(f, args...; kwargs...)
149145
end
146+
150147
"""
151148
_optimize(f::OptimLogDensity, optimizer=Optim.LBFGS(), args...; kwargs...)
152149
@@ -166,7 +163,9 @@ function _optimize(
166163
# whether initialisation is really necessary at all
167164
vi = DynamicPPL.unflatten(f.ldf.varinfo, init_vals)
168165
vi = DynamicPPL.link(vi, f.ldf.model)
169-
f = Optimisation.OptimLogDensity(f.ldf.model, vi, f.ldf.context; adtype=f.ldf.adtype)
166+
f = Optimisation.OptimLogDensity(
167+
f.ldf.model, f.ldf.getlogdensity, vi; adtype=f.ldf.adtype
168+
)
170169
init_vals = DynamicPPL.getparams(f.ldf)
171170

172171
# Optimize!
@@ -184,7 +183,7 @@ function _optimize(
184183
vi = f.ldf.varinfo
185184
vi_optimum = DynamicPPL.unflatten(vi, M.minimizer)
186185
logdensity_optimum = Optimisation.OptimLogDensity(
187-
f.ldf.model, vi_optimum, f.ldf.context
186+
f.ldf.model, f.ldf.getlogdensity, vi_optimum; adtype=f.ldf.adtype
188187
)
189188
vals_dict = Turing.Inference.getparams(f.ldf.model, vi_optimum)
190189
iters = map(DynamicPPL.varname_and_value_leaves, keys(vals_dict), values(vals_dict))

src/Turing.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ using DynamicPPL:
7171
unfix,
7272
prefix,
7373
conditioned,
74-
@submodel,
7574
to_submodel,
7675
LogDensityFunction,
7776
@addlogprob!
@@ -81,7 +80,6 @@ using OrderedCollections: OrderedDict
8180
# Turing essentials - modelling macros and inference algorithms
8281
export
8382
# DEPRECATED
84-
@submodel,
8583
generated_quantities,
8684
# Modelling - AbstractPPL and DynamicPPL
8785
@model,

0 commit comments

Comments
 (0)