|
1 | 1 | # DynamicPPL Changelog
|
2 | 2 |
|
| 3 | +## 0.37.0 |
| 4 | + |
| 5 | +DynamicPPL 0.37 comes with a substantial reworking of its internals. |
| 6 | +Fundamentally, there is no change to the actual modelling syntax: if you are a Turing.jl user, for example, this release will not affect you too much (apart from the changes to `@addlogprob!`). |
| 7 | +Any such changes will be covered separately in the Turing.jl changelog when a release is made. |
| 8 | +However, if you are a package developer or someone who uses DynamicPPL's functionality directly, you will notice a number of changes. |
| 9 | + |
| 10 | +To avoid overwhelming the reader, we begin by listing the most important, user-facing changes, before explaining the changes to the internals in more detail. |
| 11 | + |
| 12 | +Note that virtually all changes listed here are breaking. |
| 13 | + |
| 14 | +**Public-facing changes** |
| 15 | + |
| 16 | +### Submodel macro |
| 17 | + |
| 18 | +The `@submodel` macro is fully removed; please use `to_submodel` instead. |
| 19 | + |
| 20 | +### `DynamicPPL.TestUtils.AD.run_ad` |
| 21 | + |
| 22 | +The three keyword arguments, `test`, `reference_backend`, and `expected_value_and_grad` have been merged into a single `test` keyword argument. |
| 23 | +Please see the API documentation for more details. |
| 24 | +(The old `test=true` and `test=false` values are still valid, and you only need to adjust the invocation if you were explicitly passing the `reference_backend` or `expected_value_and_grad` arguments.) |
| 25 | + |
| 26 | +There is now also an `rng` keyword argument to help seed parameter generation. |
| 27 | + |
| 28 | +Finally, instead of specifying `value_atol` and `grad_atol`, you can now specify `atol` and `rtol` which are used for both value and gradient. |
| 29 | +Their semantics are the same as in Julia's `isapprox`; two values are equal if they satisfy either `atol` or `rtol`. |
| 30 | + |
| 31 | +### `DynamicPPL.TestUtils.check_model` |
| 32 | + |
| 33 | +You now need to explicitly pass a `VarInfo` argument to `check_model` and `check_model_and_trace`. |
| 34 | +Previously, these functions would generate a new VarInfo for you (using an optionally provided `rng`). |
| 35 | + |
| 36 | +### Evaluating model log-probabilities in more detail |
| 37 | + |
| 38 | +Previously, during evaluation of a model, DynamicPPL only had the capability to store a _single_ log probability (`logp`) field. |
| 39 | +`DefaultContext`, `PriorContext`, and `LikelihoodContext` were used to control what this field represented: they would accumulate the log joint, log prior, or log likelihood, respectively. |
| 40 | + |
| 41 | +In this version, we have overhauled this quite substantially. |
| 42 | +The technical details of exactly _how_ this is done is covered in the 'Accumulators' section below, but the upshot is that the log prior, log likelihood, and log Jacobian terms (for any linked variables) are separately tracked. |
| 43 | + |
| 44 | +Specifically, you will want to use the following functions to access these log probabilities: |
| 45 | + |
| 46 | + - `getlogprior(varinfo)` to get the log prior. **Note:** This version introduces new, more consistent behaviour for this function, in that it always returns the log-prior of the values in the original, untransformed space, even if the `varinfo` has been linked. |
| 47 | + - `getloglikelihood(varinfo)` to get the log likelihood. |
| 48 | + - `getlogjoint(varinfo)` to get the log joint probability. **Note:** Similar to `getlogprior`, this function now always returns the log joint of the values in the original, untransformed space, even if the `varinfo` has been linked. |
| 49 | + |
| 50 | +If you are using linked VarInfos (e.g. if you are writing a sampler), you may find that you need to obtain the log probability of the variables in the transformed space. |
| 51 | +To this end, you can use: |
| 52 | + |
| 53 | + - `getlogjac(varinfo)` to get the log Jacobian of the link transforms for any linked variables. |
| 54 | + - `getlogprior_internal(varinfo)` to get the log prior of the variables in the transformed space. |
| 55 | + - `getlogjoint_internal(varinfo)` to get the log joint probability of the variables in the transformed space. |
| 56 | + |
| 57 | +Since transformations only apply to random variables, the likelihood is unaffected by linking. |
| 58 | + |
| 59 | +### Removal of `PriorContext` and `LikelihoodContext` |
| 60 | + |
| 61 | +Following on from the above, a number of DynamicPPL's contexts have been removed, most notably `PriorContext` and `LikelihoodContext`. |
| 62 | +Although these are not the only _exported_ contexts, we consider unlikely that anyone was using _other_ contexts manually: if you have a question about contexts _other_ than these, please continue reading the 'Internals' section below. |
| 63 | + |
| 64 | +If you were evaluating a model with `PriorContext`, you can now just evaluate it with `DefaultContext`, and instead of calling `getlogp(varinfo)`, you can call `getlogprior(varinfo)` (and similarly for the likelihood). |
| 65 | + |
| 66 | +If you were constructing a `LogDensityFunction` with `PriorContext`, you can now stick to `DefaultContext`. |
| 67 | +`LogDensityFunction` now has an extra field, called `getlogdensity`, which represents a function that takes a `VarInfo` and returns the log density you want. |
| 68 | +Thus, if you pass `getlogprior_internal` as the value of this parameter, you will get the same behaviour as with `PriorContext`. |
| 69 | +(You should consider whether your use case needs the log prior in the transformed space, or the original space, and use (respectively) `getlogprior_internal` or `getlogprior` as needed.) |
| 70 | + |
| 71 | +The other case where one might use `PriorContext` was to use `@addlogprob!` to add to the log prior. |
| 72 | +Previously, this was accomplished by manually checking `__context__ isa DynamicPPL.PriorContext`. |
| 73 | +Now, you can write `@addlogprob (; logprior=x, loglikelihood=y)` to add `x` to the log-prior and `y` to the log-likelihood. |
| 74 | + |
| 75 | +**Internals** |
| 76 | + |
| 77 | +### Accumulators |
| 78 | + |
| 79 | +This release overhauls how VarInfo objects track variables such as the log joint probability. The new approach is to use what we call accumulators: Objects that the VarInfo carries on it that may change their state at each `tilde_assume!!` and `tilde_observe!!` call based on the value of the variable in question. They replace both variables that were previously hard-coded in the `VarInfo` object (`logp` and `num_produce`) and some contexts. This brings with it a number of breaking changes: |
| 80 | + |
| 81 | + - `PriorContext` and `LikelihoodContext` no longer exist. By default, a `VarInfo` tracks both the log prior and the log likelihood separately, and they can be accessed with `getlogprior` and `getloglikelihood`. If you want to execute a model while only accumulating one of the two (to save clock cycles), you can do so by creating a `VarInfo` that only has one accumulator in it, e.g. `varinfo = setaccs!!(varinfo, (LogPriorAccumulator(),))`. |
| 82 | + - `MiniBatchContext` does not exist anymore. It can be replaced by creating and using a custom accumulator that replaces the default `LikelihoodContext`. We may introduce such an accumulator in DynamicPPL in the future, but for now you'll need to do it yourself. |
| 83 | + - `tilde_observe` and `observe` have been removed. `tilde_observe!!` still exists, and any contexts should modify its behaviour. We may further rework the call stack under `tilde_observe!!` in the near future. |
| 84 | + - `tilde_assume` no longer returns the log density of the current assumption as its second return value. We may further rework the `tilde_assume!!` call stack as well. |
| 85 | + - For literal observation statements like `0.0 ~ Normal(blahblah)` we used to call `tilde_observe!!` without the `vn` argument. This method no longer exists. Rather we call `tilde_observe!!` with `vn` set to `nothing`. |
| 86 | + - `set/reset/increment_num_produce!` have become `set/reset/increment_num_produce!!` (note the second exclamation mark). They are no longer guaranteed to modify the `VarInfo` in place, and one should always use the return value. |
| 87 | + - `@addlogprob!` now _always_ adds to the log likelihood. Previously it added to the log probability that the execution context specified, e.g. the log prior when using `PriorContext`. |
| 88 | + - `getlogp` now returns a `NamedTuple` with keys `logprior` and `loglikelihood`. If you want the log joint probability, which is what `getlogp` used to return, use `getlogjoint`. |
| 89 | + - Correspondingly `setlogp!!` and `acclogp!!` should now be called with a `NamedTuple` with keys `logprior` and `loglikelihood`. The `acclogp!!` method with a single scalar value has been deprecated and falls back on `accloglikelihood!!`, and the single scalar version of `setlogp!!` has been removed. Corresponding setter/accumulator functions exist for the log prior as well. |
| 90 | + |
| 91 | +### Evaluation contexts |
| 92 | + |
| 93 | +Historically, evaluating a DynamicPPL model has required three arguments: a model, some kind of VarInfo, and a context. |
| 94 | +It's less known, though, that since DynamicPPL 0.14.0 the _model_ itself actually contains a context as well. |
| 95 | +This version therefore excises the context argument, and instead uses `model.context` as the evaluation context. |
| 96 | + |
| 97 | +The upshot of this is that many functions that previously took a context argument now no longer do. |
| 98 | +There were very few such functions where the context argument was actually used (most of them simply took `DefaultContext()` as the default value). |
| 99 | + |
| 100 | +`evaluate!!(model, varinfo, ext_context)` is removed, and broadly speaking you should replace calls to that with `new_model = contextualize(model, ext_context); evaluate!!(new_model, varinfo)`. |
| 101 | +If the 'external context' `ext_context` is a parent context, then you should wrap `model.context` appropriately to ensure that its information content is not lost. |
| 102 | +If, on the other hand, `ext_context` is a `DefaultContext`, then you can just drop the argument entirely. |
| 103 | + |
| 104 | +**To aid with this process, `contextualize` is now exported from DynamicPPL.** |
| 105 | + |
| 106 | +The main situation where one _did_ want to specify an additional evaluation context was when that context was a `SamplingContext`. |
| 107 | +Doing this would allow you to run the model and sample fresh values, instead of just using the values that existed in the VarInfo object. |
| 108 | +Thus, this release also introduces the **unexported** function `evaluate_and_sample!!`. |
| 109 | +Essentially, `evaluate_and_sample!!(rng, model, varinfo, sampler)` is a drop-in replacement for `evaluate!!(model, varinfo, SamplingContext(rng, sampler))`. |
| 110 | +**Do note that this is an internal method**, and its name or semantics are liable to change in the future without warning. |
| 111 | + |
| 112 | +There are many methods that no longer take a context argument, and listing them all would be too much. |
| 113 | +However, here are the more user-facing ones: |
| 114 | + |
| 115 | + - `LogDensityFunction` no longer has a context field (or type parameter) |
| 116 | + - `DynamicPPL.TestUtils.AD.run_ad` no longer uses a context (and the returned `ADResult` object no longer has a context field) |
| 117 | + - `VarInfo(rng, model, sampler)` and other VarInfo constructors / functions that made VarInfos (e.g. `typed_varinfo`) from a model |
| 118 | + - `(::Model)(args...)`: specifically, this now only takes `rng` and `varinfo` arguments (with both being optional) |
| 119 | + - If you are using the `__context__` special variable inside a model, you will now have to use `__model__.context` instead |
| 120 | + |
| 121 | +And a couple of more internal changes: |
| 122 | + |
| 123 | + - Just like `evaluate!!`, the other functions `_evaluate!!`, `evaluate_threadsafe!!`, and `evaluate_threadunsafe!!` now no longer accept context arguments |
| 124 | + - `evaluate!!` no longer takes rng and sampler (if you used this, you should use `evaluate_and_sample!!` instead, or construct your own `SamplingContext`) |
| 125 | + - The model evaluation function, `model.f` for some `model::Model`, no longer takes a context as an argument |
| 126 | + - The internal representation and API dealing with submodels (i.e., `ReturnedModelWrapper`, `Sampleable`, `should_auto_prefix`, `is_rhs_model`) has been simplified. If you need to check whether something is a submodel, just use `x isa DynamicPPL.Submodel`. Note that the public API i.e. `to_submodel` remains completely untouched. |
| 127 | + |
3 | 128 | ## 0.36.15
|
4 | 129 |
|
5 | 130 | Bumped minimum Julia version to 1.10.8 to avoid potential crashes with `Core.Compiler.widenconst` (which Mooncake uses).
|
|
0 commit comments