The sci.fmin
and sci.fmax
modules deal respectively with the numerical minimization and numerical maximization of functions of one or more
variables. To simplify the exposition only the case of function minimization is discussed here. All algorithms are implemented symmetrically for both
sci.fmin
and sci.fmax
modules.
An adaptive version of the differential evolution global stochastic optimizer for non-linearly constrained real valued functions of multiple variables based on
Huang&Qin[06]. The function to be minimized,
f
, takes as input a single vector. Options is a table and the algorithm makes use of the following string keys (defaults in square brackets):
Key | Description |
---|---|
stop |
[1e-6] maximum range for the population or stop function(xm, fm, vm, xval, fval, vval) |
xl |
upper bound for the population (vector) |
xu |
lower bound for the population (vector) |
x0 |
initial population (matrix, individuals as rows) |
np |
[rule of thumb] population size |
constraint |
[nil] constraint function(x, lt) |
rng |
[prng.std()] pseudo random generator to be used |
If stop
is a positive quantity, the algorithm stops when the maximum range over each dimension of the population gets smaller than the specified quantity.
Otherwise stop
is a function which is invoked after each generation and returns true
to signal the end of the optimization. Its arguments are:
the current argmin, the current minimum, the violation (i.e. the evaluation of constraint(x, lt)
) for the current minimum, the current population matrix
(individuals as rows), the vector of evaluations of f(x)
corresponding to the current population, vector of violations corresponding to the current
population.
The algorithm requires either xl
and xu
or x0
. If x0
is present then the initial population is set equal to
x0
and np
is implicitly determined. Otherwise the hypercube defined by xl
, xu
is uniformly filled with
np
individuals.
If np
is not set then it's determined according to a rule of thumb based on the dimensionality of the problem.
The function constraint(x, lt)
allows to specify any kind of linear and non-linear constraint. Constraints must be expressed using
lt(x, y)
for x < y
, summed together and returned from the function. The use of lt()
results in
constraint()
returning a positive quantity whenever the constraint are not satisfied and 0
whenever they are. We refer to this quantity as
violation. If constratin()
is not specified, it is assumed to always return 0
, i.e. the range of the function to be optimized is unbounded.
The algorithm guarantees not to evaluate f(x)
at any point there the violation is positive, i.e. where the constraints are not satisfied.
It should be noted that in this variant of the Differential Evolution the population is allowed to move outside of the initial region if not constrained so by the use
of constraint()
.
If rng
is not set a new pseudo random generator is created via prng.std()
.
When the algorithm terminates it returns: the current argmin, the current minimum, the violation for the current minimum, the current population matrix (individuals as
rows), the vector of evaluations of f
corresponding to the current population, vector of violations corresponding to the current population.
In this example fmin.de()
is used to minimize the 6 dimensional version of the
Rosenbrock function which is defined as \( f(\mathbf{x}) = \sum_{i = 1}^{N-1} \left[ \left(1 - x_i
\right)^2 + 100 \left(x_{i+1} - x_i^2 \right)^2 \right] \) and has a banana shape (see plot at the end of this page):
-- Load required modules and localize math functions:
local alg = require "sci.alg"
local fmin = require "sci.fmin"
local min, max = math.min, math.max
local N = 6 -- Number of dimensions.
-- Rosenbrock function:
local function f(x)
local sum = 0
for i=1,N-1 do
sum = sum + (1 - x[i])^2 + 100*(x[i+1] - x[i]^2)^2
end
return sum
end
-- Wide initial range:
local xl, xu = alg.vec(N), alg.vec(N)
for i=1,N do
xl[i], xu[i] = -100, 100
end
local start = os.clock() -- For timing.
local xm, fm = fmin.de(f, {
xl = xl,
xu = xu,
})
local time = os.clock() - start
print("argmin : "..xm
print("f(argmin) : "..fm)
print("CPU secs : "..time)
--> argmin : +1.000000,+1.000000,+1.000000,+1.000000,+1.000000,+1.000000
--> f(argmin) : +1.247-15
--> CPU secs : +0.010166
Plot of the 2 dimensional version: