AccessibleOptimization
Combining
Accessors.jl
+Optimization.jl
to enable function optimization with arbitrary structs. Vary struct parameters, combinations and transformations of them. Uniform and composable, zero overhead.
Motivation
Suppose you need to optimize parameters of a function that takes some Julia object as its input. Maybe it’s a model you want to fit to some data, maybe it’s just an optimization problem.
There are various optimization packages in Julia, surely they can help here. But they typically work with parameters as a vector, or at least a vector-like object, so arbitrary user/package-defined structs should be manually converted to/from vectors.
Several packages aim to help with this conversion. AFAIK, the most generic, composable and extensible approach is optics by Accessors.jl
. Even with Accessors
, there is a lot of boilerplate back/forth conversions, especially noticeable for small adhoc problems.
AccessibleOptimization
is a thin wrapper around Optimization.jl
, that uses Accessors
+ AccessorsExtra
machinery to flexibly define target parameters for optimization, using arbitrary structs as function inputs.
Usage
A simple example, using just (named)tuples:
# define a model (sum-of-sqexps) and a loss function to optimize:
expsum(m::Tuple, x) = sum(c -> c.scale * exp(-(x - c.shift)^2), m)
loss(m, data) = sum(d -> abs2(d.y - expsum(m, d.x)), data)
data = ... # collection of points with x and y
using IntervalSets
using AccessibleOptimization # reexports everything from Optimization and AccessorsExtra
# define which parameters to optimize, and what are their bounds
vars = OptArgs(
# component shifts - values from 0..10:
@o(_[∗].shift) => 0..10.,
# component scales - positive-only (log10 transformation), from 10^-1 to 10^1:
@o(log10(_[∗].scale)) => -1..1,
)
# create and solve the optimization problem, interface very similar to Optimization.jl
mod0 = ((
(scale=1, shift=1),
(scale=1, shift=2),
(scale=1, shift=3),
))
ops = OptProblemSpec(Base.Fix2(loss, data), mod0, vars)
sol = solve(ops, ECA(), maxiters=300)
sol.uobj # the optimal model
loss(sol.uobj, data)
See the README for more details, and the Pluto notebook for examples that include custom structs, more involved parameter transformations, and constraints.
See also
- Accessors and AccessorsExtra packages for generically referring to parts and transformations of arbitrary objects (so-called “optics”)
- PlutoTables using a very similar approach to define tabular editing UI for Julia objects
AccessibleOptimization.jl
has just been registered in General
.