Optimization

To show how the Evolutionary package can be used, we minimize the Rosenbrock function, a classical test problem for numerical optimization. We'll assume that you've already installed the Evolutionary package using Julia's package manager.

First, we load Evolutionary and define the Rosenbrock function:

using Evolutionary
f(x) = (1.0 - x[1])^2 + 100.0 * (x[2] - x[1]^2)^2

Once we've defined this function, we can find the minimizer (the input that minimizes the objective) and the minimum (the value of the objective at the minimizer) using any of our favorite optimization algorithms. With a function defined, we just specify a form of an individual x of the population for an evolutionary algorithm, and call optimize with a starting individual x0 and a particular optimization algorithm, e.g. CMAES():

x0 = [0.0, 0.0];
Evolutionary.optimize(f, x0, CMAES())

Configurable options

There are several options that simply take on some default values if the user doesn't provide any.

Algorithm options

There are different algorithms available in Evolutionary, and they are all listed below. Notice that the constructors are written without input here, but they generally take keywords to tweak the way they work. See the pages describing each solver for more detail.

General options

In addition to the algorithm, you can alter the behavior of the optimization procedure by using the following Options keyword arguments:

Evolutionary.OptionsType

Configurable options with defaults:

abstol::Float64 = 1e-32
reltol::Float64 = 1e-32
successive_f_tol::Integer = 10
iterations::Integer = 1000
store_trace::Bool = false
show_trace::Bool  = false
show_every::Integer = 1
callback::TCallback = nothing
source

We currently recommend the statically dispatched interface by using the Evolutionary.Options constructor:

res = Evolutionary.optimize(x->-sum(x),
                            BitVector(zeros(3)),
                            GA(),
                            Evolutionary.Options(iterations=10))

Obtaining results

After we have our results in res object, we can use the API for getting optimization results. This consists of a collection of functions. They are not exported, so they have to be prefixed by Evolutionary.. Say we do the following optimization:

julia> res = Evolutionary.optimize(x->-sum(x), BitVector(zeros(3)), GA())

 * Status: success

 * Candidate solution
    Minimizer:  [false, false, false]
    Minimum:    0
    Iterations: 11

 * Found with
    Algorithm: GA

You can inspect the result by using a collection of the auxiliary functions, e.g. the minimizer and minimum of the objective functions, which can be found using

julia> Evolutionary.minimizer(res)
3-element BitArray{1}:
 0
 0
 0

julia> Evolutionary.minimum(res)
0

Complete list of functions

An OptimizationResults interface for representing an optimization result.

Base.summaryMethod
summary(result)

Shows the optimization algorithm that produced this result.

source

An implementation of the result object for evolutionary optimizations.

Trace

When store_trace and/or show_trace options are set to true in the Option(@ref) object, an optimization trace is either captured and/or shown on the screen. By default, only the current state minimum value is displayed in the trace. In order to extend trace record, you need to override trace! function providing specialize function behavior on one of specific parameters.

Evolutionary.trace!Method
trace!(record::Dict{String,Any}, objfun, state, population, method, options)

Update the trace record. This function allows to supplement an additional information into the optimization algorithm trace by modifing a trace record. It can be overwiden by specifing particular parameter types.

source

Commonly, you would define a specializations of a state, population, or method parameters of trace! function, e.g.

function trace!(record::Dict{String,Any}, objfun, state, population, method::CMAES, options)
    record["σ"] = state.σ
end