|
| 1 | +# DynamicPPL Changelog |
| 2 | + |
| 3 | +## 0.35.0 |
| 4 | + |
| 5 | +**Breaking changes** |
| 6 | + |
| 7 | +### `.~` right hand side must be a univariate distribution |
| 8 | + |
| 9 | +Previously we allowed statements like |
| 10 | + |
| 11 | +```julia |
| 12 | +x .~ [Normal(), Gamma()] |
| 13 | +``` |
| 14 | + |
| 15 | +where the right hand side of a `.~` was an array of distributions, and ones like |
| 16 | + |
| 17 | +```julia |
| 18 | +x .~ MvNormal(fill(0.0, 2), I) |
| 19 | +``` |
| 20 | + |
| 21 | +where the right hand side was a multivariate distribution. |
| 22 | + |
| 23 | +These are no longer allowed. The only things allowed on the right hand side of a `.~` statement are univariate distributions, such as |
| 24 | + |
| 25 | +```julia |
| 26 | +x = Array{Float64,3}(undef, 2, 3, 4) |
| 27 | +x .~ Normal() |
| 28 | +``` |
| 29 | + |
| 30 | +The reasons for this are internal code simplification and the fact that broadcasting where both sides are multidimensional but of different dimensions is typically confusing to read. |
| 31 | + |
| 32 | +If the right hand side and the left hand side have the same dimension, one can simply use `~`. Arrays of distributions can be replaced with `product_distribution`. So instead of |
| 33 | + |
| 34 | +```julia |
| 35 | +x .~ [Normal(), Gamma()] |
| 36 | +x .~ Normal.(y) |
| 37 | +x .~ MvNormal(fill(0.0, 2), I) |
| 38 | +``` |
| 39 | + |
| 40 | +do |
| 41 | + |
| 42 | +```julia |
| 43 | +x ~ product_distribution([Normal(), Gamma()]) |
| 44 | +x ~ product_distribution(Normal.(y)) |
| 45 | +x ~ MvNormal(fill(0.0, 2), I) |
| 46 | +``` |
| 47 | + |
| 48 | +This is often more performant as well. Note that using `~` rather than `.~` does change the internal storage format a bit: With `.~` `x[i]` are stored as separate variables, with `~` as a single multivariate variable `x`. In most cases this does not change anything for the user, but if it does cause issues, e.g. if you are dealing with `VarInfo` objects directly and need to keep the old behavior, you can always expand into a loop, such as |
| 49 | + |
| 50 | +```julia |
| 51 | +dists = Normal.(y) |
| 52 | +for i in 1:length(dists) |
| 53 | + x[i] ~ dists[i] |
| 54 | +end |
| 55 | +``` |
| 56 | + |
| 57 | +Cases where the right hand side is of a different dimension than the left hand side, and neither is a scalar, must be replaced with a loop. For example, |
| 58 | + |
| 59 | +```julia |
| 60 | +x = Array{Float64,3}(undef, 2, 3, 4) |
| 61 | +x .~ MvNormal(fill(0, 2), I) |
| 62 | +``` |
| 63 | + |
| 64 | +should be replaced with something like |
| 65 | + |
| 66 | +```julia |
| 67 | +x = Array{Float64,3}(2, 3, 4) |
| 68 | +for i in 1:3, j in 1:4 |
| 69 | + x[:, i, j] ~ MvNormal(fill(0, 2), I) |
| 70 | +end |
| 71 | +``` |
| 72 | + |
| 73 | +This release also completely rewrites the internal implementation of `.~`, where from now on all `.~` statements are turned into loops over `~` statements at macro time. However, the only breaking aspect of this change is the above change to what's allowed on the right hand side. |
| 74 | + |
| 75 | +### Remove indexing by samplers |
| 76 | + |
| 77 | +This release removes the feature of `VarInfo` where it kept track of which variable was associated with which sampler. This means removing all user-facing methods where `VarInfo`s where being indexed with samplers. In particular, |
| 78 | + |
| 79 | + - `link` and `invlink`, and their `!!` versions, no longer accept a sampler as an argument to specify which variables to (inv)link. The `link(varinfo, model)` methods remain in place, and as a new addition one can give a `Tuple` of `VarName`s to (inv)link only select variables, as in `link(varinfo, varname_tuple, model)`. |
| 80 | + - `set_retained_vns_del_by_spl!` has been replaced by `set_retained_vns_del!` which applies to all variables. |
| 81 | + - `getindex`, `setindex!`, and `setindex!!` no longer accept samplers as arguments |
| 82 | + - `unflatten` no longer accepts a sampler as an argument |
| 83 | + - `eltype(::VarInfo)` no longer accepts a sampler as an argument |
| 84 | + - `keys(::VarInfo)` no longer accepts a sampler as an argument |
| 85 | + - `VarInfo(::VarInfo, ::Sampler, ::AbstractVector)` no longer accepts the sampler argument. |
| 86 | + - `push!!` and `push!` no longer accept samplers or `Selector`s as arguments |
| 87 | + - `getgid`, `setgid!`, `updategid!`, `getspace`, and `inspace` no longer exist |
| 88 | + |
| 89 | +### Reverse prefixing order |
| 90 | + |
| 91 | + - For submodels constructed using `to_submodel`, the order in which nested prefixes are applied has been changed. |
| 92 | + Previously, the order was that outer prefixes were applied first, then inner ones. |
| 93 | + This version reverses that. |
| 94 | + To illustrate: |
| 95 | + |
| 96 | + ```julia |
| 97 | + using DynamicPPL, Distributions |
| 98 | + |
| 99 | + @model function subsubmodel() |
| 100 | + return x ~ Normal() |
| 101 | + end |
| 102 | + |
| 103 | + @model function submodel() |
| 104 | + x ~ to_submodel(prefix(subsubmodel(), :c), false) |
| 105 | + return x |
| 106 | + end |
| 107 | + |
| 108 | + @model function parentmodel() |
| 109 | + x1 ~ to_submodel(prefix(submodel(), :a), false) |
| 110 | + return x2 ~ to_submodel(prefix(submodel(), :b), false) |
| 111 | + end |
| 112 | + |
| 113 | + keys(VarInfo(parentmodel())) |
| 114 | + ``` |
| 115 | + |
| 116 | + Previously, the final line would return the variable names `c.a.x` and `c.b.x`. |
| 117 | + With this version, it will return `a.c.x` and `b.c.x`, which is more intuitive. |
| 118 | + (Note that this change brings `to_submodel`'s behaviour in line with the now-deprecated `@submodel` macro.) |
| 119 | + |
| 120 | + This change also affects sampling in Turing.jl. |
| 121 | +
|
| 122 | +### `LogDensityFunction` argument order |
| 123 | +
|
| 124 | + - The method `LogDensityFunction(varinfo, model, sampler)` has been removed. |
| 125 | + The only accepted order is `LogDensityFunction(model, varinfo, context; adtype)`. |
| 126 | + (For an explanation of `adtype`, see below.) |
| 127 | + The varinfo and context arguments are both still optional. |
| 128 | +
|
| 129 | +**Other changes** |
| 130 | +
|
| 131 | +### New exports |
| 132 | +
|
| 133 | +`LogDensityFunction` and `predict` are now exported from DynamicPPL. |
| 134 | +
|
| 135 | +### `LogDensityProblems` interface |
| 136 | +
|
| 137 | +LogDensityProblemsAD is now removed as a dependency. |
| 138 | +Instead of constructing a `LogDensityProblemAD.ADgradient` object, we now directly use `DifferentiationInterface` to calculate the gradient of the log density with respect to model parameters. |
| 139 | +
|
| 140 | +Note that if you wish, you can still construct an `ADgradient` out of a `LogDensityFunction` object (there is nothing preventing this). |
| 141 | +
|
| 142 | +However, in this version, `LogDensityFunction` now takes an extra AD type argument. |
| 143 | +If this argument is not provided, the behaviour is exactly the same as before, i.e. you can calculate `logdensity` but not its gradient. |
| 144 | +However, if you do pass an AD type, that will allow you to calculate the gradient as well. |
| 145 | +You may thus find that it is easier to instead do this: |
| 146 | +
|
| 147 | +```julia |
| 148 | +@model f() = ... |
| 149 | +
|
| 150 | +ldf = LogDensityFunction(f(); adtype=AutoForwardDiff()) |
| 151 | +``` |
| 152 | +
|
| 153 | +This will return an object which satisfies the `LogDensityProblems` interface to first-order, i.e. you can now directly call both |
| 154 | +
|
| 155 | +``` |
| 156 | +LogDensityProblems.logdensity(ldf, params) |
| 157 | +LogDensityProblems.logdensity_and_gradient(ldf, params) |
| 158 | +``` |
| 159 | +
|
| 160 | +without having to construct a separate `ADgradient` object. |
| 161 | +
|
| 162 | +If you prefer, you can also construct a new `LogDensityFunction` with a new AD type afterwards. |
| 163 | +The model, varinfo, and context will be taken from the original `LogDensityFunction`: |
| 164 | +
|
| 165 | +```julia |
| 166 | +@model f() = ... |
| 167 | +
|
| 168 | +ldf = LogDensityFunction(f()) # by default, no adtype set |
| 169 | +ldf_with_ad = LogDensityFunction(ldf, AutoForwardDiff()) |
| 170 | +``` |
| 171 | +
|
| 172 | +## 0.34.2 |
| 173 | +
|
| 174 | + - Fixed bugs in ValuesAsInModelContext as well as DebugContext where underlying PrefixContexts were not being applied. |
| 175 | + From a user-facing perspective, this means that for models which use manually prefixed submodels, e.g. |
| 176 | + |
| 177 | + ```julia |
| 178 | + using DynamicPPL, Distributions |
| 179 | + |
| 180 | + @model inner() = x ~ Normal() |
| 181 | + |
| 182 | + @model function outer() |
| 183 | + x1 ~ to_submodel(prefix(inner(), :a), false) |
| 184 | + return x2 ~ to_submodel(prefix(inner(), :b), false) |
| 185 | + end |
| 186 | + ``` |
| 187 | + |
| 188 | + will: (1) no longer error when sampling due to `check_model_and_trace`; and (2) contain both submodel's variables in the resulting chain (the behaviour before this patch was that the second `x` would override the first `x`). |
| 189 | + |
| 190 | + - More broadly, implemented a general `prefix(ctx::AbstractContext, ::VarName)` which traverses the context tree in `ctx` to apply all necessary prefixes. This was a necessary step in fixing the above issues, but it also means that `prefix` is now capable of handling context trees with e.g. multiple prefixes at different levels of nesting. |
| 191 | + |
| 192 | +## 0.34.1 |
| 193 | + |
| 194 | + - Fix an issue that prevented merging two VarInfos if they had different dimensions for a variable. |
| 195 | + |
| 196 | + - Upper bound the compat version of KernelAbstractions to work around an issue in determining the right VarInfo type to use. |
| 197 | + |
| 198 | +## 0.34.0 |
| 199 | + |
| 200 | +**Breaking** |
| 201 | + |
| 202 | + - `rng` argument removed from `values_as_in_model`, and `varinfo` made non-optional. This means that the only signatures allowed are |
| 203 | + |
| 204 | + ``` |
| 205 | + values_as_in_model(::Model, ::Bool, ::AbstractVarInfo) |
| 206 | + values_as_in_model(::Model, ::Bool, ::AbstractVarInfo, ::AbstractContext) |
| 207 | + ``` |
| 208 | + |
| 209 | + If you aren't using this function (it's probably only used in Turing.jl) then this won't affect you. |
| 210 | + |
| 211 | +## 0.33.1 |
| 212 | + |
| 213 | +Reworked internals of `condition` and `decondition`. |
| 214 | +There are no changes to the public-facing API, but internally you can no longer use `condition` and `decondition` on an `AbstractContext`, you can only use it on a `DynamicPPL.Model`. If you want to modify a context, use `ConditionContext` and `decondition_context`. |
| 215 | + |
| 216 | +## 0.33.0 |
| 217 | + |
| 218 | +**Breaking** |
| 219 | + |
| 220 | + - `values_as_in_model()` now requires an extra boolean parameter, specifying whether variables on the lhs of `:=` statements are to be included in the resulting `OrderedDict` of values. |
| 221 | + The type signature is now `values_as_in_model([rng,] model, include_colon_eq::Bool [, varinfo, context])` |
| 222 | + |
| 223 | +**Other** |
| 224 | + |
| 225 | + - Moved the implementation of `predict` from Turing.jl to DynamicPPL.jl; the user-facing behaviour is otherwise the same |
| 226 | + - Improved error message when a user tries to initialise a model with parameters that don't correspond strictly to the underlying VarInfo used |
0 commit comments