Skip to content

Parameters guide

Here we will describe the AIBECS interface. This guide will take you through some examples of setting up model parameters.

Note

The parameters features in AIBECS essentially come from other packages. These include Parameters.jl, FieldMetadata.jl, Flatten.jl, Unitful.jl.

As usual, make sure you are using AIBECS

julia
using AIBECS
using DataFrames # required for `AIBECS.table` (loaded via package extension)

Abstract parameters type

The AIBECS provides a set of features to create and use parameters. This features are implemented as functions that must be provided with a parameters type. But each set of parameters is different, and AIBECS cannot know beforehand what parameters you want to use. The AIBECS thus provides an abstract parameters type, called AbstractParameters, upon which all the AIBECS functionality is built. This is why when you create a set of parameters in AIBECS, you must declare it as a subtype of AbstractParameters. Here is an example.

julia
struct SimpleParams{T} <: AbstractParameters{T}
    α::T
    β::T
end

Once the type, which here simply defines the symbols (α and β), is constructed, we can instantiate a parameter variable.

julia
p = SimpleParams(1.0, 2.0)
Main.SimpleParams{Float64}
  α = 1.0
  β = 2.0

As you can see, the AIBECS will display p as a table. This is because the show method converts p to a table (a DataFrame to be specific) under the hood:

julia
AIBECS.table(p)
2×2 DataFrame
RowSymbolValue
SymbolFloat64
1α1.0
2β2.0

If you do not remember the order in which you created the parameters (α is first, β is second), AIBECS has got your back: keyword arguments are supported.

julia
p = SimpleParams= 2.0, α = 1.0)
Main.SimpleParams{Float64}
  α = 1.0
  β = 2.0

Unpacking

Probably the most useful feature in AIBECS is the ability to elegantly unpack parameters, thanks to Parameters.jl.

julia
@unpack α, β = p
α, β
(1.0, 2.0)

unpacks the parameters on the left (α and β) from the parameter type p.

Units

One of the main features of parameters in AIBECS is that you can use units and let AIBECS do the conversions for you. Before you use units, though, you must import the @units and units functions from AIBECS. Here is an example.

julia
import AIBECS: @units, units
@units struct UnitfulParams{T} <: AbstractParameters{T}
    α::T | u"m/s"
    β::T | u"d"
end
units (generic function with 11 methods)

Cretaing an instance is just as easy

julia
p = UnitfulParams(1.0, 2.0)
Main.UnitfulParams{Float64}
  α = 1.0 (m s⁻¹)
  β = 2.0 (d)

And in this case the parameters are shown with units. You can rely on AIBECS to convert units on the fly.

julia
p = UnitfulParams(3.0u"km/hr", 24.0u"hr")
Main.UnitfulParams{Float64}
  α = 0.8333333333333334 (m s⁻¹)
  β = 1.0 (d)

And use keyword arguments

julia
p = UnitfulParams= 24.0u"hr", α = 3.0u"km/hr")
Main.UnitfulParams{Float64}
  α = 0.8333333333333334 (m s⁻¹)
  β = 1.0 (d)

Unpacking parameters that have units first converts them to SI units.

julia
@unpack β = p
β
86400.0

and β (which is equal to 1 day) here is expressed in seconds after being unpacked.

Initial values

Another useful feature is to set initial (or default) values. Again, you must import the functions for them to work properly

julia
import AIBECS: @initial_value, initial_value
@initial_value struct ParamsWithInitialValue{T} <: AbstractParameters{T}
    α::T | 1.0
    β::T | 2.0
end
initial_value (generic function with 11 methods)

This is handy in many applications.

You can instantiate p with the initial values as, well, its values.

julia
p = ParamsWithInitialValue()
Main.ParamsWithInitialValue{Float64}
  α = 1.0
  β = 2.0

You could also just set one parameter to a different value

julia
p = ParamsWithInitialValue= 10.0)
Main.ParamsWithInitialValue{Float64}
  α = 1.0
  β = 10.0

Combining initial values and units

You can combine both features in parameters.

julia
@initial_value @units struct UnitfulParamsWithInitialValue{T} <: AbstractParameters{T}
    α::T | 1.0 | u"m/s"
    β::T | 2.0 | u"d"
end
initial_value (generic function with 13 methods)

And instantiate p from just one parameter with its unit

julia
p = UnitfulParamsWithInitialValue= 1.0u"yr")
Main.UnitfulParamsWithInitialValue{Float64}
  α = 1.0 (m s⁻¹)
  β = 365.25 (d)

Optimizable parameters

julia
import AIBECS: @flattenable, flattenable
@flattenable struct OptimizableParams{T} <: AbstractParameters{T}
    α::T | true
    β::T | false
    γ::T | true
end
flattenable (generic function with 12 methods)

Then the "flattenable" parameters will be the only ones to remain when converting OptimizableParams to a vector

julia
p = OptimizableParams(1.0, 2.0, 3.0)
v = vec(p)
2-element Vector{Float64}:
 1.0
 3.0

The vec function uses the @unpack function in AIBECS, so that units are converted when vectorizing. Here is an example of that by first combining units and flattenable.

julia
@initial_value @units @flattenable struct OptimizableParamsWithUnits{T} <: AbstractParameters{T}
    α::T | 1.0 | u"m/s" | true
    β::T | 2.0 | u"d"   | false
    γ::T | 3.0 | u"km"  | true
end
p = OptimizableParamsWithUnits()
Main.OptimizableParamsWithUnits{Float64}
  α = 1.0 (m s⁻¹)
  β = 2.0 (d)
  γ = 3.0 (km)

And then vectorizing the parameters

julia
vec(p)
2-element Vector{Float64}:
    1.0
 3000.0

Note how γ (the third parameter, but the second flattenable one), is converted to meters.

Bounds, priors, and log-scaling

Optimisable parameters can carry per-field metadata that AIBECS uses during parameter optimisation: a Bayesian prior, hard bounds, and a flag marking the parameter as log-scaled when it spans many orders of magnitude. These are introduced via the @prior, @bounds, and @logscaled macros — usable on the same struct as @units and @flattenable. They require using Distributions so the AIBECSParametersDistributionsExt extension activates.

julia
using Distributions
@initial_value @units @flattenable @bounds @logscaled @prior struct OptParams{T} <: AbstractParameters{T}
    k::T | 1.0 | u"d^-1" | true  | (0.01, 100.0) | true  | LogNormal()
end

Vector ↔ parameter conversion

Once parameters are tagged optimisable, AIBECS round-trips between a struct and a flat vector via vec, p2λ, and λ2p:

  • vec(p) flattens the optimisable fields to SI units (shown above).

  • p2λ(p) does the same, then maps each parameter through its bijector so the vector is unconstrained (log-transformed when log-scaled, logit-transformed when bounded). Pair with λ2p(typeof(p), λ) to reconstruct a parameter struct from a flat unconstrained vector.

Tabular display

Beyond the default show, AIBECS exposes a few presentation helpers:

  • AIBECS.table(p) returns a DataFrame of the parameter values plus their metadata columns (units, prior, bounds, …). Requires using DataFrames.

  • latex(p) prints a LaTeX-formatted version of the same table, convenient for paper supplements.


This page was generated using Literate.jl.