The package Morbit.jl
provides a local derivative-free solver for multiobjective optimization problems with possibly expensive objectives.
It is meant to find a single Pareto-critical point.
“Morbit” stands for Multiobjective Optimization by Radial Basis Function Interpolation in Trust-regions. The name was chosen so as to pay honors to the single objective algorithm ORBIT by Wild et. al. We have a paper explaining the algorithm!
This was my first project when I started using Julia and has since then undergone several rewrites.
This project was founded by the European Region Development Fund.
Constraints :)
- Box constraints are supported natively and respected during model construction.
- Relaxable linear constraints are supported natively, i.e., propagated to the internal solver.
- Relaxable nonlinear constraints are supported via a filter mechanism.
This package is not registered (yet), so please install via
using Pkg
Pkg.add(; url = "https://github.com/manuelbb-upb/Morbit.jl.git")
# or, using ssh:
# Pkg.add(; url = "[email protected]/manuelbb-upb/Morbit.jl.git" )
Let's find a critical point for the unconstrained minimization problem with objectives
The critical points coincide with globally Pareto optimal points and lie on the line connecting the individual minima (1,1) and (-1,-1).
Setting up Morbit for this problem is fairly easy:
using Morbit
f1 = x -> sum( (x .- 1 ).^2 )
f2 = x -> sum( (x .+ 1 ).^2 )
mop = MOP(2) # problem in 2 variables
add_exact_objective!(mop, f1)
add_exact_objective!(mop, f2)
x0 = [ π; -ℯ ]
x, fx, ret_code, database = optimize(mop, x0)
The optimize method accepts either an AlgorithmConfig
object via the
algo_config
keyword argument or concrete settings as keyword arguments.
E.g.,
x, fx, ret_code, database = optimize(mop, x0; max_iter=20, fx_tol_rel=1e-3)
sets two stopping criteria.
In the above case, both functions are treated as cheap and their gradients are determined using FiniteDiff
.
To use automatic differentiation (via ForwardDiff.jl
), use
add_objective!(mop, f1;
n_out=1, model_cfg=ExactConfig(),
diff_method=Morbit.AutoDiffWrapper)
Gradients can be provided with the gradients
keyword argument.
If you wanted to model a objective, say the function f₂
, using radial basis functions, you could pass a SurrogateConfig
:
rbf_cfg = RbfConfig(;kernel = :multiquadric)
add_objective!(mop, f1; n_out = 1, model_cfg = rbf_cfg)
Alternatively, there is
add_rbf_objective!(mop, f1)
for scalar objective functions using the default configuration RbfConfig()
.
Of course, vector-valued objectives are also supported:
F = x -> [f1(x); f2(x)]
add_rbf_objectives!(mop, F; n_out = 2)
# or
# add_objective!(mop, F; n_out = 2, model_cfg = RbfConfig())
Instead of RBF models, Lagrange models (LagrangeConfig()
) and Taylor polynomials (TaylorConfig()
) are also supported.
Box constraints can easily be defined at initialization of the MOP
:
lb = fill(-4, 2)
ub = -lb
mop_con = MOP(lb, ub)
Linear constraints of the form A * x <= b
or A * x == b
can be added via
add_eq_constraint!(mop, A, b)
add_ineq_constraint!(mop, A, b)
Nonlinear constraints g(x) <= 0
or h(x) == 0
are added like the objectives:
add_nl_ineq_constraint!(mop, g; n_out = 1 model_cfg = RbfConfig())
add_nl_eq_constraint!(mop, h; n_out = 1 model_cfg = TaylorConfig())
There are many options to configure both the algorithm behavior and the surrogate modelling techniques. Please see the docs.