optimagic is a Python package for numerical optimization. It is a unified interface to optimizers from SciPy, NlOpt and other packages. optimagic's minimize function works just like SciPy's, so you don't have to adjust your code. You simply get more optimizers for free. On top you get diagnostic tools, parallel numerical derivatives and more.
We have some local optimizers that can deal with some level of noise (pounders, pybobyqa and dfols) but they fail on very noisy problems such as our covid simulation model. In particular, the covid simulation model has the following features
Substantial noise that leads to local minima; Even if we could make a huge gridsearch, we would not want to take the lowest obtained value as minimum because it is probably just an outlier.
Up to 10 free parameters, all with tight bounds; Gridsearch is infeasible, but random algorithms that try to explore the full parameter space are possible
Long runtime and hard to parallelize internally; This we need to parallelize over function evaluations
Components we need
Exploration algorithms
By exploration algorithms I mean algorithms that evaluate the criterion function at random or deterministic points in the parameter space and save the results in a database. They can be simple or sophisticated in the sense that they try to focus on promising regions. Those algorithms might also produce an estimate of the function minimum, but we will probably not use that and instead analyze the function evaluations ourselves.
The key insight is that we do not have to implement any of the sophisticated algorithms ourselves. We can just use add existing optimization algorithms that explore the parameter space and use the fact that estimagic stores all parameters and function evaluations in a database.
I suggest the following algorithms:
Random search (own implementation)
Genetic algorithms (taken from pygmo. We can recycle part of the old implementation but need parallelism)
Bayesian optimization (taken from scikit optimize, GPyOpt and others)
We can of course also check out all the other algorithms available in those libraries. Typically, it is easy to add all at once.
Simply adding the exploration algorithms as optimizers, also answers basically all possible design questions we might have had (database layout, user interface, ...).
Meta analysis algorithms
While most of the above algorithms should in principle be able to produce a good parameter estimate on their own, we need our own meta analysis for the following reasons:
Combine all available information: If we run several optimizers, we want to be able to use all function evaluations ever made to produce the best possible estimate. The same holds if we find out that we need to extend the bounds (or can shrink them)
Get early feedback: We want to get a first estimate of the best parameters while the estimation is still running.
Do unconstrained exploration but constrained estimation: For example we might have generated the exploration sample without equality constraints on parameters but then want to see what happens when we impose them.
While it would be possible to add the meta algorithms as optimizers, it does not make much sense. The interface is just very different (bunch of databases instead of criterion function, never need any derivatives, no logging needed, ...). Instead I propose a meta_minimize and meta_maximize function. Internally implemented as meta_optimize with direction argument.
def meta_minimize(databases, params=None, algorithm="auto", constraints=None):
"""Minimize a function, given evaluations stored in databases.
Args:
databases (str, pathlib.Path or lists thereof): Paths to databases.
params (pd.DataFrame): Can be used to provide bounds or start values for any local
optimization that might be run on a surrogate model.
algorithm (str): One of ["auto", "gaussian_process", "ols"]. Determines which kind of surrogate
model is used for the meta analysis.
algo_options (dict): Further keyword options for the algorithm
constraints (list): Same as during optimization.
"""
This would just be the user interface to internal meta minimization algorithms with an interface that abstracts aways from constraints, parameters labels and so on:
def take_best(criterion_values, parameter_values, lower_bounds, upper_bounds): # others might have more algo_options
"""Return the minimal entry of y and the corresponding entry in x.
Args:
criterion_values (numpy.ndarray): 1d numpy array with function evaluations.
parameter_values (numpy.ndarray): 2d numpy array. Each row is a parameter vector. This is already
the internal parameter vector that is only subject to lower and upper bounds, no other
constraints.
lower_bounds (numpy.ndarray): 1d numpy array with lower bounds. Has length parameter_values.shape[1]
upper_bounds (numpy.ndarray): 1d numpy array with upper bounds. Has length parameter_values.shape[1]
Returns:
dict: Similar dictionary as an optimization algorithm would return.
"""
Problem description
We have some local optimizers that can deal with some level of noise (pounders, pybobyqa and dfols) but they fail on very noisy problems such as our covid simulation model. In particular, the covid simulation model has the following features
Components we need
Exploration algorithms
By exploration algorithms I mean algorithms that evaluate the criterion function at random or deterministic points in the parameter space and save the results in a database. They can be simple or sophisticated in the sense that they try to focus on promising regions. Those algorithms might also produce an estimate of the function minimum, but we will probably not use that and instead analyze the function evaluations ourselves.
The key insight is that we do not have to implement any of the sophisticated algorithms ourselves. We can just use add existing optimization algorithms that explore the parameter space and use the fact that estimagic stores all parameters and function evaluations in a database.
I suggest the following algorithms:
We can of course also check out all the other algorithms available in those libraries. Typically, it is easy to add all at once.
Simply adding the exploration algorithms as optimizers, also answers basically all possible design questions we might have had (database layout, user interface, ...).
Meta analysis algorithms
While most of the above algorithms should in principle be able to produce a good parameter estimate on their own, we need our own meta analysis for the following reasons:
While it would be possible to add the meta algorithms as optimizers, it does not make much sense. The interface is just very different (bunch of databases instead of criterion function, never need any derivatives, no logging needed, ...). Instead I propose a
meta_minimize
andmeta_maximize
function. Internally implemented asmeta_optimize
with direction argument.This would just be the user interface to internal meta minimization algorithms with an interface that abstracts aways from constraints, parameters labels and so on: