-
Notifications
You must be signed in to change notification settings - Fork 2
Home
Welcome to the Diver wiki!
This description of the codebase is a work in progress. It describes Version 1.1, and supersedes the description in our original paper.
Diver is a fast and robust numerical optimiser using differential evolution. It's written in Fortran 2008 with C and C++ wrappers and optional parallelisation using MPI.
Diver is invoked by calling the Fortran function diver()
defined in de.f90
module. For C/C++, call the function cdiver()
with the header files diver.h
or diver.hpp
. At minimum, you must pass it an objective function func()
, which diver will attempt to find the minimum of, and two arrays lowerbounds
and upperbounds
, which define the allowed range of parameter space. We also recommend specifying the population size control parameter NP
and a path for saving output files path
(or set disableIO
to true).
For a complete description of the input arguments, including the required arguments of func()
, see Arguments.
Diver will return a double-precision real, which represents the minimum value found for the objective function. Values of the best fit parameters and derived parameters can be retrieved by providing diver()
or cdiver()
with empty arrays bestFitParams
and bestFitDerived
of the correct size. However, by default Diver also generates output files containing complete information about the evolution of the population over the run as well as the runtime options passed to Diver. These files can be used to map out the objective function, resume stopped runs, and more. For more information see Output Files.
For examples of driving programs for Diver, please see example_f
, example_c
, and example_cpp
in the main directory of the repository.
For a description of the codebase, see Source Code
Differential evoloution is a population-based global optimisation heuristic. Members of the population are chosen using vector addition, reminiscent of the Nelder-Mead simplex method. The population is then "evolved" over many generations. The distribution and diversity of population members play a vital role in the success of the algorithm. For a complete description, please see section 10 of the paper included in this repository.
Classical differential evolution typically uses three control parameters: F
(scale factor), Cr
(crossover rate), and NP
(population size).
The population size NP
is the most important parameter, since the extent of the population is what allows the algorithm to explore parameter space. Too small a population will lead to premature convergence; we recommend a population several orders of magnitude larger than the number of dimensions, but this depends on the complexity of the objective function. See the paper for examples.
The scale factor F
is the next most important parameter. It is generally a number between 0 and 1, noninclusive, but we offer the ability to use multiple scale factors as well. This parameter controls contraction of the population, and thus strongly affects convergence behaviour. If the scale factor is too large, the population will take a long time to converge; if too small, the population may prematurely converge.
Finally, the crossover rate Cr
allows for searching in a way that decouples the dimensions. It is a number between 0 and 1, inclusive. A small crossover rate will tend to produce new points differing from the previous points along a small number of dimensions, and may be superior if the dimensions in your problem are highly uncorrelated. Conversely, a large crossover rate will mostly search across all dimensions simultaneously, and may be superior if the dimensions in your objective function are highly correlated.
Diver can be run using any of the classic differential evolution strategies, but we recommend using the self-adaptive variants jDE (less agressive) and lambdajDE (more agressive) instead. The default behaviour of diver is to use lambdajDE.
With self-adaptive differential evolution, the control parameters F
and Cr
no longer need to be specified. Instead they are initially chosen randomly for each member of the population, with a 10% chance of being updated each generation, but are otherwise inherited. In principle, this should allow "good" choices of F
and Cr
to be preferentially retained, while increasing diversity. In practice we have found this strategy to be robust.
We also offer a more agressive self-adaptive strategy, which we call lambdajDE. This works as in standard jDE, but additionally uses the location of the current best-fit member of the population to suggest new population members via the self-adaptive parameter lambda
(ie using a rand-to-best/1/bin evolution strategy instead of rand/1/bin). This will lead to faster convergence, which may or may not be desirable, depending on the complexity of your objective function.
For details on how to specify control parameters, mutation and crossover strategies, and boundary conditions, see this section of the Arguments wiki page.
- Interaction between the objective function and Diver
- mandatory arguments to the function
- context pointer
- Discrete Parameters
- Population Diversity
- Approximate posterior and evidence estimates
- note that differential evolution does not automatically provide this (unlike MCMC/Nested Sampling), but it is an algorithm suited to mapping out objective functions
- Parallelisation with MPI
- As a population-based algorithm, differential evolution is a natural choice for parallelisation. We divide the population evenly along the different MPI processes. Using MPI does not add much overhead, and will greatly improve runtime if objective function calls are expensive.
- pitfalls? if the objective function uses MPI itself, there may be conflicts with some options (eg
discard_unfit_points
)