Skip to content

Commit dfa6915

Browse files
committed
fix transforms
1 parent 6c2260d commit dfa6915

File tree

1 file changed

+10
-36
lines changed

1 file changed

+10
-36
lines changed

developers/transforms/dynamicppl/index.qmd

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -126,50 +126,24 @@ This sequence of events is summed up in the following diagram, where `f(..., arg
126126
In the final part of this article, we will take a more in-depth look at the internal DynamicPPL machinery that allows us to convert between representations and obtain the correct probability densities.
127127
Before that, though, we will take a quick high-level look at how the HMC sampler in Turing.jl uses the functions introduced so far.
128128

129-
## Case study: HMC in Turing.jl
129+
## HMC in Turing.jl
130130

131131
While DynamicPPL provides the _functionality_ for transforming variables, the transformation itself happens at an even higher level, i.e. in the sampler itself.
132132
The HMC sampler in Turing.jl is in [this file](https://github.com/TuringLang/Turing.jl/blob/5b24cebe773922e0f3d5c4cb7f53162eb758b04d/src/mcmc/hmc.jl).
133-
In the first step of sampling, it calls `link` on the sampler.
134-
This transformation is preserved throughout the sampling process, meaning that `is_transformed()` always returns true.
135-
136-
We can observe this by inserting print statements into the model.
137-
Here, `__varinfo__` is the internal symbol for the `VarInfo` object used in model evaluation:
138-
139-
```{julia}
140-
setprogress!(false)
141-
142-
@model function demo2()
143-
x ~ LogNormal()
144-
if x isa AbstractFloat
145-
println("-----------")
146-
println("model repn: $(DynamicPPL.getindex(__varinfo__, @varname(x)))")
147-
println("internal repn: $(DynamicPPL.getindex_internal(__varinfo__, @varname(x)))")
148-
println("is_transformed: $(is_transformed(__varinfo__, @varname(x)))")
149-
end
150-
end
151-
152-
sample(demo2(), HMC(0.1, 3), 3);
153-
```
154-
155-
156-
(Here, the check on `if x isa AbstractFloat` prevents the printing from occurring during computation of the derivative.)
157-
You can see that during the three sampling steps, `is_transformed` is always kept as `true`.
158-
159-
::: {.callout-note}
160-
The first two model evaluations where `is_transformed` is `false` occur prior to the actual sampling.
161-
One occurs when the model is checked for correctness (using [`DynamicPPL.check_model_and_trace`](https://github.com/TuringLang/DynamicPPL.jl/blob/ba490bf362653e1aaefe298364fe3379b60660d3/src/debug_utils.jl#L582-L612)).
162-
The second occurs because the model is evaluated once to generate a set of initial parameters inside [DynamicPPL's implementation of `AbstractMCMC.step`](https://github.com/TuringLang/DynamicPPL.jl/blob/ba490bf362653e1aaefe298364fe3379b60660d3/src/sampler.jl#L98-L117).
163-
Both of these steps occur with all samplers in Turing.jl, so are not specific to the HMC example shown here.
164-
:::
165133

134+
In the first step of sampling, it calls `link` on the sampler.
166135
What this means is that from the perspective of the HMC sampler, it _never_ sees the constrained variable: it always thinks that it is sampling from an unconstrained distribution.
167136

168137
The biggest prerequisite for this to work correctly is that the potential energy term in the Hamiltonian—or in other words, the model log density—must be programmed correctly to include the Jacobian term.
169138
This is exactly the same as how we had to make sure to define `logq(y)` correctly in the toy HMC example above.
170139

171-
Within Turing.jl, this is correctly handled because a statement like `x ~ LogNormal()` in the model definition above is translated into `assume(LogNormal(), @varname(x), __varinfo__)`, defined [here](https://github.com/TuringLang/DynamicPPL.jl/blob/ba490bf362653e1aaefe298364fe3379b60660d3/src/context_implementations.jl#L225-L229).
172-
If you follow the trail of function calls, you can verify that the `assume` function does indeed check for the presence of the `is_transformed` flag and adds the Jacobian term accordingly.
140+
Within Turing.jl, this is correctly handled by calling
141+
142+
```julia
143+
x, inv_logjac = with_logabsdet_jacobian(y, inverse_transform)
144+
```
145+
146+
and then passing `inv_logjac` to DynamicPPL's `LogJacobianAccumulator`.
173147

174148
## A deeper dive into DynamicPPL's internal machinery
175149

@@ -415,4 +389,4 @@ In this chapter of the Turing docs, we've looked at:
415389
- the higher-level usage of transforms in DynamicPPL and Turing.
416390

417391
This will hopefully have equipped you with a better understanding of how constrained variables are handled in the Turing framework.
418-
With this knowledge, you should especially find it easier to navigate DynamicPPL's `VarInfo` type, which forms the backbone of model evaluation.
392+
With this knowledge, you should especially find it easier to navigate DynamicPPL's `VarInfo` type, which forms the backbone of model evaluation.

0 commit comments

Comments
 (0)