Skip to content

Converting to/from MCMCChains

You can convert FlexiChain objects to and from MCMCChains.Chains.

MCMCChains to FlexiChain

For example, let's make an MCMCChains object from a Turing model:

julia
using DynamicPPL, MCMCChains, AbstractMCMC, Distributions, LinearAlgebra

@model function f()
    x ~ Normal()
    y ~ Bernoulli()
    z ~ MvNormal(zeros(2), I)
end

# Sample from the prior, and tack on a random log-probability.
samples = [DynamicPPL.ParamsWithStats(rand(f()), (; logp=rand())) for _ in 1:100, _ in 1:3]
# Bundle them into a Chain
mc = AbstractMCMC.from_samples(MCMCChains.Chains, samples)
Chains MCMC chain (100×5×3 Array{Float64, 3}):

Iterations        = 1:1:100
Number of chains  = 3
Samples per chain = 100
parameters        = x, y, z[1], z[2]
internals         = logp

Use `describe(chains)` for summary statistics and quantiles.

The most naive conversion will create a FlexiChain{Symbol}, since that is the key type of MCMCChains:

julia
using FlexiChains

FlexiChains.from_mcmcchains(mc)
╭─FlexiChain (100 iterations, 3 chains) ───────────────────────────────────────
 ↓ iter  = [1 … 100]
 → chain = [1, 2, 3]

 Parameters (4) ── Symbol
  Float64  x, y, z[1], z[2]                                                   

 Extras (1)
  Float64  logp                                                               
╰──────────────────────────────────────────────────────────────────────────────╯

However, notice that we don't have the vector structure of the parameter z, and instead have two different scalar parameters z[1] and z[2]. We can add the structure back by specifying a set of keys as the second argument:

julia
using FlexiChains: Parameter, Extra

fc = FlexiChains.from_mcmcchains(
    mc,
    (
        Parameter(@varname(x)),
        Parameter(@varname(y)),
        Parameter(@varname(z)) => (2,),
        Extra(:logp),
    ),
)
╭─FlexiChain (100 iterations, 3 chains) ───────────────────────────────────────
 ↓ iter  = [1 … 100]
 → chain = [1, 2, 3]

 Parameters (3) ── VarName
  Float64          x, y                                                       
  Vector{Float64}  z (2,)

 Extras (1)
  Float64  logp                                                               
╰──────────────────────────────────────────────────────────────────────────────╯

By passing VarName parameters instead of Symbols, we can 'upconvert' this into a VNChain rather than a SymChain (which is harder to index into, for example, if you want to get z[1]). Furthermore, by specifying the shape of z as (2,), we can recover the vector structure of that parameter. In general array-valued parameters can be 'recovered' in this way, but more complicated structs cannot be.

Order of parameters

The parameters provided as the second argument must add up to exactly the same number of columns in the original MCMCChains object, and must also be in the same order as they appear in the original object. The order of the parameters in an MCMCChains object is not always obvious, so be careful with this.

After that, you might also want to convert y back into a Boolean:

julia
fc = FlexiChains.transform_values(fc, @varname(y) => Bool)
╭─FlexiChain (100 iterations, 3 chains) ───────────────────────────────────────
 ↓ iter  = [1 … 100]
 → chain = [1, 2, 3]

 Parameters (3) ── VarName
  Float64          x                                                          
  Bool             y                                                          
  Vector{Float64}  z (2,)

 Extras (1)
  Float64  logp                                                               
╰──────────────────────────────────────────────────────────────────────────────╯

FlexiChain to MCMCChains

The conversion from FlexiChain to MCMCChains is much more straightforward. Since MCMCChains just throws away all the information, there is no behaviour to customise:

julia
MCMCChains.Chains(fc)
Chains MCMC chain (100×5×3 Array{Float64, 3}):

Iterations        = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
Number of chains  = 3
Samples per chain = 100
parameters        = x, y, z[1], z[2]
internals         = logp

Use `describe(chains)` for summary statistics and quantiles.

Docstrings

FlexiChains.from_mcmcchains Function
julia
FlexiChains.from_mcmcchains(chains::MCMCChains.Chains, key_spec=nothing)

Convert an MCMCChains.Chains object to a FlexiChain.

If key_spec is not provided, parameters in the :parameters section are stored as Parameter{Symbol} keys and all other sections (e.g. :internals) as Extra{Symbol} keys, giving a FlexiChain{Symbol}.

If key_spec is provided, it is passed directly to the FlexiChain from-array constructor. The key type of the resulting FlexiChain is inferred from key_spec. You can pass this argument if you want to override the parameter names stored in the MCMCChains.Chains object, or to group array-valued parameters together, for example.

Please see the FlexiChain constructor documentation for details on what key_spec is allowed.

Iteration indices, chain indices, and per-chain sampling times are inherited from the MCMCChains.Chains object. If the MCMCChains.Chains object contains a samplerstate field in its info NamedTuple, this is also preserved in the resulting FlexiChain.

source
MCMCChains.Chains Method
julia
MCMCChains.Chains(chain::FlexiChain)

Convert a FlexiChain to an MCMCChains.Chains object.

Array-valued parameters are split up into their individual real-valued elements, much like the output that you get directly from sampling with Turing + MCMCChains.

source