Skip to content

Turing.jl

Documentation for Turing.jl ↗

This page provides a fairly high-level overview of how to use FlexiChains with Turing.jl.

If you have a previous workflow that uses MCMCChains.jl and want to find out how to update it, you might also be interested in the MCMCChains migration guide.

Sampling

Since Turing.jl v0.45, FlexiChains is the default chain type returned by MCMC sampling.

Not on Turing v0.45 yet?

To obtain a FlexiChain with older versions of Turing.jl, you can specify the chain_type keyword argument when calling sample:

julia
using FlexiChains
sample(model, sampler, N; chain_type=VNChain)

Let's use a non-trivial model so that we can illustrate some features of FlexiChains.

julia
using Turing, FlexiChains

y = [28, 8, -3, 7, -1, 1, 18, 12]
sigma = [15, 10, 16, 11, 9, 11, 10, 18]
@model function eight_schools(y, sigma)
    mu ~ Normal(0, 5)
    tau ~ truncated(Cauchy(0, 5); lower=0)
    theta ~ MvNormal(fill(mu, length(y)), tau^2 * I)
    for i in eachindex(y)
        y[i] ~ Normal(theta[i], sigma[i])
    end
    return (mu=mu, tau=tau)
end
model = eight_schools(y, sigma)
chain = sample(model, NUTS(), 5)
╭─FlexiChain (5 iterations, 1 chain) ──────────────────────────────────────────
 ↓ iter  = 3:7
 → chain = 1:1

 Parameters (3) ── VarName
  Float64          mu, tau                                                    
  Vector{Float64}  theta (8,)

 Extras (14)
  Int64    n_steps, tree_depth                                                
  Bool     is_accept, numerical_error                                         
  Float64  acceptance_rate, log_density, hamiltonian_energy,                  
           hamiltonian_energy_error, max_hamiltonian_energy_error, step_size, 
           nom_step_size, logprior, loglikelihood, logjoint                   
╰──────────────────────────────────────────────────────────────────────────────╯

Note

We only run 5 MCMC iterations here to keep the output in the following sections small.

Key types

First, notice in the printout above that a FlexiChain stores 'parameters' and 'extra keys' separately. Parameters correspond to random variables of the model you sampled from, whereas other keys are extra data associated with the samples drawn (for example, the log-joint probability of each sample).

In FlexiChains, these are wrapped in the FlexiChains.Parameter and FlexiChains.Extra types respectively. Thus, the parameter mu is really stored as Parameter(@varname(mu)), and the log-joint probability is Extra(:logjoint).

For a FlexiChain{T}, all Parameter keys must wrap objects that subtype T. Extras on the other hand can wrap anything.

VNChain is an alias for FlexiChain{VarName}, i.e., the parameters are stored as VarNames, which is the natural output for Turing.jl.

SymChain

For other modelling packages it may sometimes be more natural to store parameters as Symbols instead of VarNames. In that case, you can use SymChain (an alias for FlexiChain{Symbol}) instead of VNChain.

Accessing data

FlexiChains provides multiple different ways to access the data for a given key.

Parameters

To access parameters, the recommended way is to use VarNames to index into the chain. VarName is a data structure defined in AbstractPPL.jl, and is what Turing.jl uses to represent the name of a random variable (appearing on the left-hand side of a tilde-statement).

VarNames are most easily constructed by applying the @varname macro to the name of the variable that you want to access. For example, this directly gives us the value of mu in each iteration as a plain old vector of floats.

julia
chain[@varname(mu)]
5×1 DimArray{Float64, 2} Parameter(mu)
├────────────────────────────────────────┴──────────── dims ┐
iter Sampled{Int64} 3:7 ForwardOrdered Regular Points,
chain Sampled{Int64} 1:1 ForwardOrdered Regular Points
└───────────────────────────────────────────────────────────┘
   1
 3    -0.71455
 4    -0.71455
 5    -0.71455
 6    -1.02371
 7    -1.02371

Wrapping in Parameter and Extra

When looking up a parameter, you do not need to wrap the VarName in FlexiChains.Parameter(...): this will be automatically done for you. Extra keys always need to be wrapped (but see also the shortcut for Symbol-based indexing below).

DimMatrix

Indexing into a FlexiChain returns a DimensionalData.DimMatrix. This behaves exactly like a regular Matrix, but additionally carries extra information about its dimensions.

This allows you to keep track of what each dimension means, and also allows for more advanced indexing operations, which are described in the 'indexing' page.

For vector-valued parameters like theta, this works in exactly the same way, except that you get a DimMatrix of vectors.

julia
chain[@varname(theta)]
5×1 DimArray{Vector{Float64}, 2} Parameter(theta)
├───────────────────────────────────────────────────┴─ dims ┐
iter Sampled{Int64} 3:7 ForwardOrdered Regular Points,
chain Sampled{Int64} 1:1 ForwardOrdered Regular Points
└───────────────────────────────────────────────────────────┘
1
 3        [1.36402, -1.31958, 1.4297, 0.747465, -1.72636, 0.213503, -1.61212, 1.18464]
 4        [1.36402, -1.31958, 1.4297, 0.747465, -1.72636, 0.213503, -1.61212, 1.18464]
 5        [1.36402, -1.31958, 1.4297, 0.747465, -1.72636, 0.213503, -1.61212, 1.18464]
 6        [2.08462, -0.607302, 0.0733724, 0.289852, -1.03698, -0.717279, -1.48806, 1.44727]
 7    …   [2.08462, -0.607302, 0.0733724, 0.289852, -1.03698, -0.717279, -1.48806, 1.44727]

This is probably the biggest difference between FlexiChains and MCMCChains. MCMCChains by default will break vector-valued parameters into multiple scalar-valued parameters called theta[1], theta[2], etc., whereas FlexiChains keeps them together as they were defined in the model.

If you want a 3D array...

If you want to access theta as a 3D array of shape (num_iterations, num_chains, length(theta)), you can also pass the keyword argument stack=true to getindex:

julia
chain[@varname(theta), stack=true]
5×1×8 DimArray{Float64, 3} Parameter(theta)
├─────────────────────────────────────────────┴───────── dims ┐
iter Sampled{Int64} 3:7 ForwardOrdered Regular Points,
chain Sampled{Int64} 1:1 ForwardOrdered Regular Points,
AnonDim Sampled{Int64} 1:8 ForwardOrdered Regular Points
└─────────────────────────────────────────────────────────────┘
[:, :, 1]
  1
 3    1.36402
 4    1.36402
 5    1.36402
 6    2.08462
 7    2.08462

If you want to obtain only the first element of theta, you don't need to manipulate the DimMatrix. You can just index into the chain with the corresponding VarName:

julia
chain[@varname(theta[1])]
5×1 DimArray{Float64, 2} Parameter(theta[1])
├──────────────────────────────────────────────┴────── dims ┐
iter Sampled{Int64} 3:7 ForwardOrdered Regular Points,
chain Sampled{Int64} 1:1 ForwardOrdered Regular Points
└───────────────────────────────────────────────────────────┘
  1
 3    1.36402
 4    1.36402
 5    1.36402
 6    2.08462
 7    2.08462

In this way, you can 'break down', or access nested fields of, larger parameters. That is, if your model has x ~ dist, FlexiChains will let you access some field or index of x.

Heterogeneous data

If some samples of x have one element and others have two elements, attempting to access x[2] will return an array with missing values for the samples where x only has one element.

You can also use keyword arguments when indexing to specify which chains or iterations you are interested in. Note that when using square brackets to index, keyword arguments must be separated from positional arguments by a comma, not a semicolon!

julia
chain[@varname(mu), iter=2:4, chain=1]
3-element DimArray{Float64, 1} Parameter(mu)
├──────────────────────────────────────────────┴────── dims ┐
iter Sampled{Int64} 4:6 ForwardOrdered Regular Points
└───────────────────────────────────────────────────────────┘
 4  -0.71455
 5  -0.71455
 6  -1.02371

You can also use selectors from DimensionalData.jl to specify which iterations or chains you want.

julia
chain[@varname(mu), iter=Not(At(7)), chain=At(1)]
4-element DimArray{Float64, 1} Parameter(mu)
├──────────────────────────────────────────────┴────────────── dims ┐
iter Sampled{Int64} [3, …, 6] ForwardOrdered Irregular Points
└───────────────────────────────────────────────────────────────────┘
 3  -0.71455
 4  -0.71455
 5  -0.71455
 6  -1.02371

The indexing behaviour of FlexiChains is described fully on the Indexing page.

Other keys

In general Turing.jl tries to package up some extra metadata into the chain that may be helpful. For example, the log-joint probability of each sample is stored with the key :logjoint. To access non-parameter information like this in an unambiguous fashion, you should use the Extra wrapper.

julia
using FlexiChains: Extra

chain[Extra(:logjoint)]
5×1 DimArray{Float64, 2} Extra(:logjoint)
├───────────────────────────────────────────┴───────── dims ┐
iter Sampled{Int64} 3:7 ForwardOrdered Regular Points,
chain Sampled{Int64} 1:1 ForwardOrdered Regular Points
└───────────────────────────────────────────────────────────┘
    1
 3    -58.6722
 4    -58.6722
 5    -58.6722
 6    -51.0991
 7    -51.0991

Warning

In older versions of Turing.jl, MCMCChains would store the log-joint probability as :lp. FlexiChains uses :logjoint instead, which is clearer. Since Turing v0.42, both MCMCChains and FlexiChains use :logjoint.

If there is no ambiguity in the symbol :logjoint, then you can use a shortcut which is described in the next section.

Indexing by Symbol: a shortcut

If you are used to MCMCChains.jl, you may find this more cumbersome than before. So, FlexiChains provides some shortcuts for accessing data. You can index into a FlexiChain with a single Symbol, and as long as it is unambiguous, it will return the corresponding data.

julia
chain[:mu] # parameter
5×1 DimArray{Float64, 2} Parameter(mu)
├────────────────────────────────────────┴──────────── dims ┐
iter Sampled{Int64} 3:7 ForwardOrdered Regular Points,
chain Sampled{Int64} 1:1 ForwardOrdered Regular Points
└───────────────────────────────────────────────────────────┘
   1
 3    -0.71455
 4    -0.71455
 5    -0.71455
 6    -1.02371
 7    -1.02371

What does unambiguous mean?

In this case, because the only key k for which Symbol(k.name) == :mu is Parameter(@varname(mu)), we can safely identify Parameter(@varname(mu)) as the key that we want. If this chain also had an extra key called Extra(:mu), then this would be ambiguous, and FlexiChains would throw an error.

No sub-varnames

You cannot use chain[Symbol("theta[1]")] as a replacement for chain[@varname(theta[1])].

Likewise, we can omit wrapping :logjoint in Extra(...):

julia
chain[:logjoint] # other key
5×1 DimArray{Float64, 2} Extra(:logjoint)
├───────────────────────────────────────────┴───────── dims ┐
iter Sampled{Int64} 3:7 ForwardOrdered Regular Points,
chain Sampled{Int64} 1:1 ForwardOrdered Regular Points
└───────────────────────────────────────────────────────────┘
    1
 3    -58.6722
 4    -58.6722
 5    -58.6722
 6    -51.0991
 7    -51.0991

Prefix-agnostic indexing

If you want to access a variable that has been prefixed (e.g. because it is part of a submodel) but you don't want to specify the full prefix, you can use FlexiChains.Prefixed:

julia
using Turing
using FlexiChains: Prefixed, VNChain

@model inner() = x ~ MvNormal(zeros(2), I)

@model function outer()
    a ~ to_submodel(inner())
    return nothing
end

pfx_chain = sample(outer(), MH(), 5)

# Inside the chain, the actual key is `@varname(a.x)`;
# this will pick out that key.
pfx_chain[Prefixed(@varname(x))]
5×1 DimArray{Vector{Float64}, 2} Parameter(a.x)
├─────────────────────────────────────────────────┴─── dims ┐
iter Sampled{Int64} 1:5 ForwardOrdered Regular Points,
chain Sampled{Int64} 1:1 ForwardOrdered Regular Points
└───────────────────────────────────────────────────────────┘
  1
 1     [-0.380862, 0.367807]
 2     [-0.282887, 1.27161]
 3     [0.506013, 0.288514]
 4     [-0.169375, 2.97253]
 5     [0.719639, 0.374298]

Sub-VarNames are also supported:

julia
pfx_chain[Prefixed(@varname(x[1]))]
5×1 DimArray{Float64, 2} Parameter(a.x[1])
├────────────────────────────────────────────┴──────── dims ┐
iter Sampled{Int64} 1:5 ForwardOrdered Regular Points,
chain Sampled{Int64} 1:1 ForwardOrdered Regular Points
└───────────────────────────────────────────────────────────┘
   1
 1    -0.380862
 2    -0.282887
 3     0.506013
 4    -0.169375
 5     0.719639

Summary statistics

Overall summaries

For a very quick summary of the chain, you can use StatsBase.summarystats (which FlexiChains reexports):

julia
using FlexiChains: summarystats

summarystats(chain)
╭─FlexiSummary (9 statistics) ─────────────────────────────────────────────────
   iter    collapsed
   chain   collapsed
 ↓ stat  = [mean, std, mcse, ess_bulk, ess_tail, rhat, q5, q50, q95]

 Parameters (10) ── VarName
  Float64  mu, tau, theta[1], theta[2], theta[3], theta[4], theta[5],         
           theta[6], theta[7], theta[8]                                       

 Extras (14)
  Float64  n_steps, is_accept, acceptance_rate, log_density,                  
           hamiltonian_energy, hamiltonian_energy_error,                      
           max_hamiltonian_energy_error, tree_depth, numerical_error,         
           step_size, nom_step_size, logprior, loglikelihood, logjoint        

 Summary
      param     mean     std  mcse  ess_bulk  ess_tail  rhat       q5
         mu  -0.8382  0.1693   NaN       NaN       NaN   Inf  -1.0237
        tau   3.9983  2.3766   NaN       NaN       NaN   Inf   1.3949
   theta[1]   1.6523  0.3947   NaN       NaN       NaN   Inf   1.3640
   theta[2]  -1.0347  0.3901   NaN       NaN       NaN   Inf  -1.3196
   theta[3]   0.8872  0.7429   NaN       NaN       NaN   Inf   0.0734
   theta[4]   0.5644  0.2506   NaN       NaN       NaN   Inf   0.2899
   theta[5]  -1.4506  0.3776   NaN       NaN       NaN   Inf  -1.7264
   theta[6]  -0.1588  0.5098   NaN       NaN       NaN   Inf  -0.7173
   theta[7]  -1.5625  0.0679   NaN       NaN       NaN   Inf  -1.6121
   theta[8]   1.2897  0.1438   NaN       NaN       NaN   Inf   1.1846
╰──────────────────────────────────────────────────────────────────────────────╯

Note

The large number of NaN's and Inf's here are just because of the very short chain length. On a real chain you would get proper statistics.

To index into this, you can use a similar syntax as for FlexiChains:

julia
ss = summarystats(chain)
ss[@varname(mu)]
9-element DimArray{Float64, 1} Parameter(mu)
├──────────────────────────────────────────────┴──── dims ┐
stat Categorical{Symbol} [:mean, …, :q95] Unordered
└─────────────────────────────────────────────────────────┘
 :mean       -0.838216
 :std         0.169336
 :mcse      NaN
 :ess_bulk  NaN
 :ess_tail  NaN
 :rhat       Inf
 :q5         -1.02371
 :q50        -0.71455
 :q95        -0.71455

or to access an individual statistic

julia
ss[@varname(mu), stat=:mean]
-0.8382155888990226

Alternatively, you can directly convert the FlexiSummary into an array and manipulate it as you would any other array. (Note that Array(ss) also works, but you lose the dimension information in that case.)

julia
DimArray(ss)
9×10 DimArray{Float64, 2}
├───────────────────────────┴───────────────────────── dims ┐
stat Categorical{Symbol} [:mean, …, :q95] Unordered,
param Categorical{VarName} [mu, …, theta[8]] Unordered
└───────────────────────────────────────────────────────────┘
            mu          tau        theta[1]theta[7]     theta[8]
  :mean       -0.838216    3.99832    1.65226        -1.5625       1.28969
  :std         0.169336    2.37659    0.394688        0.0679462    0.143846
  :mcse      NaN         NaN        NaN             NaN          NaN
  :ess_bulk  NaN         NaN        NaN             NaN          NaN
  :ess_tail  NaN         NaN        NaN          …  NaN          NaN
  :rhat       Inf         Inf        Inf             Inf          Inf
  :q5         -1.02371     1.39489    1.36402        -1.61212      1.18464
  :q50        -0.71455     5.73393    1.36402        -1.61212      1.18464
  :q95        -0.71455     5.73393    2.08462        -1.48806      1.44727

By default, summarystats will split VarNames up. This is done because summary statistics often only make sense for scalar-valued parameters. If you want to avoid this, you can set split_varnames=false:

julia
summarystats(chain; split_varnames=false)
╭─FlexiSummary (9 statistics) ─────────────────────────────────────────────────
   iter    collapsed
   chain   collapsed
 ↓ stat  = [mean, std, mcse, ess_bulk, ess_tail, rhat, q5, q50, q95]

 Parameters (3) ── VarName
  Float64                          mu, tau                                    
  Union{Missing, Vector{Float64}}  theta                                      

 Extras (14)
  Float64  n_steps, is_accept, acceptance_rate, log_density,                  
           hamiltonian_energy, hamiltonian_energy_error,                      
           max_hamiltonian_energy_error, tree_depth, numerical_error,         
           step_size, nom_step_size, logprior, loglikelihood, logjoint        

 Summary
   param          mean           std     mcse  ess_bulk  ess_tail     rhat
      mu       -0.8382        0.1693      NaN       NaN       NaN      Inf
     tau        3.9983        2.3766      NaN       NaN       NaN      Inf
   theta  [1.7,-1.0,0…  [0.4,0.4,0.…  missing   missing   missing  missing
╰──────────────────────────────────────────────────────────────────────────────╯

Notice how many of the statistics for theta are now missing. This is because statistics like the quantiles cannot be meaningfully calculated for vector-valued parameters.

Individual summaries

You can obtain, for example, the mean of each key in the chain using Statistics.mean. This returns a FlexiSummary object:

julia
using Statistics: mean

mn = mean(chain)
╭─FlexiSummary ────────────────────────────────────────────────────────────────
   iter    collapsed
   chain   collapsed
   stat    collapsed

 Parameters (10) ── VarName
  Float64  mu, tau, theta[1], theta[2], theta[3], theta[4], theta[5],         
           theta[6], theta[7], theta[8]                                       

 Extras (14)
  Float64  n_steps, is_accept, acceptance_rate, log_density,                  
           hamiltonian_energy, hamiltonian_energy_error,                      
           max_hamiltonian_energy_error, tree_depth, numerical_error,         
           step_size, nom_step_size, logprior, loglikelihood, logjoint        

 Summary
      param
         mu  -0.8382                                                          
        tau   3.9983                                                          
   theta[1]   1.6523                                                          
   theta[2]  -1.0347                                                          
   theta[3]   0.8872                                                          
   theta[4]   0.5644                                                          
   theta[5]  -1.4506                                                          
   theta[6]  -0.1588                                                          
   theta[7]  -1.5625                                                          
   theta[8]   1.2897                                                          
╰──────────────────────────────────────────────────────────────────────────────╯

You can index into a FlexiSummary in exactly the same ways as a FlexiChain.

julia
mn[@varname(mu)]
-0.8382155888990226

Out of the box, FlexiChains provides many commonly used summary functions, such as Statistics.mean and Statistics.std (a full list is given in the Summarising page). These functions can all be applied to a FlexiChain with their usual signatures (for example, Statistics.quantile will require a second argument). Keyword arguments of the original functions are also supported, for example MCMCDiagnosticTools.ess(chain; kind=:tail) returns the tail ESS.

Other summary functions

If you want to apply a summary function that isn't listed above, you can manually use FlexiChains.collapse. If it is something that is worth appearing in FlexiChains proper, please do open an issue!

Collapsed dimensions

By default, applying summary functions will collapse the data in both the iteration and chain dimensions (the latter is only relevant if multiple chains are present).

To only collapse over one dimension you can use

julia
mean(chain; dims=:iter)[@varname(mu)]
1-element DimArray{Float64, 1} Parameter(mu)
├──────────────────────────────────────────────┴─────── dims ┐
chain Sampled{Int64} 1:1 ForwardOrdered Regular Points
└────────────────────────────────────────────────────────────┘
 1  -0.838216

or dims=:chain (although that is probably less useful).

Saving and resuming MCMC sampling progress

If you want to sample a fewer number of iterations first and then resume it later, you can use the following:

julia
chn1 = sample(model, NUTS(), 10; save_state=true)
chn2 = sample(model, NUTS(), 10; initial_state=only(FlexiChains.last_sampler_state(chn1)))
╭─FlexiChain (10 iterations, 1 chain) ─────────────────────────────────────────
 ↓ iter  = 1:10
 → chain = 1:1

 Parameters (3) ── VarName
  Float64          mu, tau                                                    
  Vector{Float64}  theta (8,)

 Extras (14)
  Int64    n_steps, tree_depth                                                
  Bool     is_accept, numerical_error                                         
  Float64  acceptance_rate, log_density, hamiltonian_energy,                  
           hamiltonian_energy_error, max_hamiltonian_energy_error, step_size, 
           nom_step_size, logprior, loglikelihood, logjoint                   
╰──────────────────────────────────────────────────────────────────────────────╯

The chains can be combined using vcat:

julia
combined_chn = vcat(chn1, chn2)
╭─FlexiChain (20 iterations, 1 chain) ─────────────────────────────────────────
 ↓ iter  = [6 … 25]
 → chain = 1:1

 Parameters (3) ── VarName
  Float64          mu, tau                                                    
  Vector{Float64}  theta (8,)

 Extras (14)
  Int64    n_steps, tree_depth                                                
  Bool     is_accept, numerical_error                                         
  Float64  acceptance_rate, log_density, hamiltonian_energy,                  
           hamiltonian_energy_error, max_hamiltonian_energy_error, step_size, 
           nom_step_size, logprior, loglikelihood, logjoint                   
╰──────────────────────────────────────────────────────────────────────────────╯

The initial_state argument

When performing single-chain sampling with sample(model, spl, N; initial_state=state), initial_state should be either nothing (to start a new chain) or the state to resume from. For multiple-chain sampling with sample(model, spl, MCMCThreads(), N, C), initial_state should be a vector of length C, where initial_state[i] is the state to resume the i-th chain from (or nothing to start a new chain).

To obtain the saved final state of a chain, you can use FlexiChains.last_sampler_state. This always returns a vector of states with length equal to the number of chains. Note that this applies also if you only sampled a single chain, in which case the returned value is a vector of length 1: you will therefore have to use only() to extract the state itself.

The above applies equally to MCMCSerial() and MCMCDistributed().

Initialising MCMC sampling from a FlexiChain

You can also use the parameters stored in a FlexiChain to initialise MCMC sampling. Note that this is different from resuming sampling from a saved sampler state, because all other sampler information (e.g. momentum, ...) will be re-initialised.

For example, to start a new chain from the fifth iteration and first chain contained inside chain, you can do

julia
chn3 = sample(model, MH(), 5; initial_params=InitFromParams(chain, 5, 1))
╭─FlexiChain (5 iterations, 1 chain) ──────────────────────────────────────────
 ↓ iter  = 1:5
 → chain = 1:1

 Parameters (3) ── VarName
  Float64          mu, tau                                                    
  Vector{Float64}  theta (8,)

 Extras (4)
  Bool     accepted                                                           
  Float64  logprior, loglikelihood, logjoint                                  
╰──────────────────────────────────────────────────────────────────────────────╯

Since this only uses the parameters which are already part of the chain, this does not require you to use save_state=true for the original chain.

Posterior predictions and friends

The functions predict, returned, logjoint, loglikelihood, and logprior all work 'as expected' using FlexiChains with exactly the same signatures that you are used to.

julia
returned(model, chain)
5×1 DimArray{@NamedTuple{mu::Float64, tau::Float64}, 2}
├─────────────────────────────────────────────────────────┴ dims ┐
iter Sampled{Int64} 3:7 ForwardOrdered Regular Points,
chain Sampled{Int64} 1:1 ForwardOrdered Regular Points
└────────────────────────────────────────────────────────────────┘
  1
 3     (mu = -0.71455, tau = 5.73393)
 4     (mu = -0.71455, tau = 5.73393)
 5     (mu = -0.71455, tau = 5.73393)
 6     (mu = -1.02371, tau = 1.39489)
 7     (mu = -1.02371, tau = 1.39489)

pointwise_logdensities, pointwise_loglikelihoods, and pointwise_prior_logdensities are also supported, and will return a new FlexiChain containing the log-probabilities for each variable.

LOO-CV is also supported if you load PosteriorStats.jl: see the PosteriorStats.jl integration for more details.

Plotting

FlexiChains contains a few recipes for plotting with Plots.jl and Makie.jl; please see the plotting pages for more details. Here we demonstrate usage with Plots.jl.

When plotting a VNChain, array-valued parameters will automatically be split up into their individual components. In this example we plot only tau and theta[1] to save space, but if you were to plot theta, you would get eight separate plots for each element of theta.

julia
using StatsPlots

# Or omit the second argument to plot all parameters.
plot(chain, [@varname(tau), @varname(theta[1])])
savefig("plot_ex.svg");