Hi all! ![]()
I’m excited to share AccessibleModels.jl – the easiest way to fit/optimize models with parameters being arbitrary Julia objects and create quick UIs for those objects.
I highlighted AccessibleModels.jl in my Accessors talk at this year JuliaCon – see the relevant part here:
Motivation and Design 
When fitting models, we often have parameters naturally represented as complex Julia objects (structs, nested data, etc), but optimization/sampling packages expect flat parameter vectors. This leads to lots of boilerplate code for parameter extraction and reconstruction.
The same applies to interactive UIs: it’s often natural to represent multiple relevant parameters as a single object, but UI libraries tend to work with individual independent controls or vectors.
AccessibleModels.jl uses Accessors.jl to automatically handle parameter management, letting you optimize/sample any Julia object directly. The exact same model and parameter definitions can be used for both model fitting and quick UIs!
Key features:
- Universal: Works with basically any Julia struct, flat or nested

- Wide ecosystem support: Works with Optimization.jl, Pigeons.jl (MCMC), and more

- Zero boilerplate: Just define your model - no parameter extraction/reconstruction code

- Bonus
: Can create instant interactive UIs for any AccessibleModels model using Makie!
Usage 
Define your model object as an arbitrary struct (no dependency on AccessibleModels required at this stage):
julia> struct ExpFunction{A,B}
scale::A
shift::B
end
julia> struct SumFunction{T}
comps::T
end
julia> (m::ExpFunction)(x) = m.scale * exp(-(x - m.shift)^2)
julia> (m::SumFunction)(x) = sum(c -> c(x), m.comps)
Create an interactive Makie UI for adjusting model parameters:
julia> using AccessibleModels, IntervalSets
julia> using GLMakie
julia> mod0 = SumFunction((
ExpFunction(1., 1.),
ExpFunction(2., 2.),
))
julia> amodel = AccessibleModel(mod0, (
(@o _.comps[∗].shift) => 0..10,
(@o _.comps[∗].scale) => 0..4,
))
julia> obj, = SliderGrid(fig[1,1], amodel)
julia> lines(fig[1,2], 0..10, @lift x -> $obj(x))
Use the same AccessibleModel for optimization by adding a loss function:
# Generate example data using a "true" model
julia> data = [(x=x, y=true_model(x) + 0.2 * randn()) for x in 0:0.5:10]
21-element Vector{@NamedTuple{x::Float64, y::Float64}}:
(x = 0.0, y = 0.158)
(x = 0.5, y = -0.172)
(x = 1.0, y = -0.138)
<...>
julia> loglike(m::SumFunction, data) = sum(r -> logpdf(Normal(m(r.x), 0.3), r.y), data)
# The only change: add the log-likelihood function
julia> amodel = AccessibleModel(Base.Fix2(loglike, data), mod0, (
(@o _.comps[∗].shift) => 0..10,
(@o _.comps[∗].scale) => 0..4,
))
julia> using Optimization, OptimizationMetaheuristics
julia> op = OptimizationProblem(amodel)
julia> sol = solve(op, ECA(), amodel)
# Get the fitted model:
julia> getobj(sol)
SumFunction((
ExpFunction(1.983, 3.098),
ExpFunction(1.574, 7.013)))
The same model definition works for MCMC sampling. This is especially convenient with MonteCarloMeasurements to hold the results: ![]()
julia> using Pigeons
julia> pt = pigeons(target=amodel, record=[traces; round_trip; record_default()])
julia> using MonteCarloMeasurements
julia> mcmc_fitted = samples(Particles, pt)
SumFunction((ExpFunction(1.5 ± 0.5, 5.35 ± 2.0), ExpFunction(1.6 ± 0.6, 4.66 ± 2.0)))
julia> lines(0..10, x -> mcmc_fitted(x))
julia> band!(0..10, x -> mcmc_fitted(x))
More examples and explanations in the docs.
More 
This package is a thin layer of plumbing that builds heavily on the Accessors.jl and AccessorsExtra.jl functionality. See the docs and code for more details.
Related works:
- AccessibleOptimization.jl: same concept, but Optimization.jl-only; effectively deprecated, all functionality is available in AccessibleModels.jl with a more unified design.
- PlutoTables.jl: same concept, but for Pluto notebook UIs. Currently, AccessibleModels.jl supports Makie UIs, Pluto backend can be added in the future.
Would love to hear your thoughts and feedback! ![]()
AccessibleModels.jl is now registrered in General. ![]()
