anneal is deprecated! Deprecated in scipy 0.14.0, use basinhopping instead
Minimize a function using simulated annealing.
Uses simulated annealing, a random algorithm that uses no derivative information from the function being optimized. Other names for this family of approaches include: “Monte Carlo”, “Metropolis”, “Metropolis-Hastings”, etc. They all involve (a) evaluating the objective function on a random set of points, (b) keeping those that pass their randomized evaluation critera, (c) cooling (i.e., tightening) the evaluation critera, and (d) repeating until their termination critera are met. In practice they have been used mainly in discrete rather than in continuous optimization.
Available annealing schedules are ‘fast’, ‘cauchy’ and ‘boltzmann’.
Parameters: | func : callable
|
---|---|
Returns: | xmin : ndarray
|
See also
Notes
Simulated annealing is a random algorithm which uses no derivative information from the function being optimized. In practice it has been more useful in discrete optimization than continuous optimization, as there are usually better algorithms for continuous optimization problems.
Some experimentation by trying the different temperature schedules and altering their parameters is likely required to obtain good performance.
The randomness in the algorithm comes from random sampling in numpy. To obtain the same results you can call numpy.random.seed with the same seed immediately before calling anneal.
We give a brief description of how the three temperature schedules generate new points and vary their temperature. Temperatures are only updated with iterations in the outer loop. The inner loop is over loop over xrange(dwell), and new points are generated for every iteration in the inner loop. Whether the proposed new points are accepted is probabilistic.
For readability, let d denote the dimension of the inputs to func. Also, let x_old denote the previous state, and k denote the iteration number of the outer loop. All other variables not defined below are input variables to anneal itself.
In the ‘fast’ schedule the updates are:
u ~ Uniform(0, 1, size = d)
y = sgn(u - 0.5) * T * ((1 + 1/T)**abs(2*u - 1) - 1.0)
xc = y * (upper - lower)
x_new = x_old + xc
c = n * exp(-n * quench)
T_new = T0 * exp(-c * k**quench)
In the ‘cauchy’ schedule the updates are:
u ~ Uniform(-pi/2, pi/2, size=d)
xc = learn_rate * T * tan(u)
x_new = x_old + xc
T_new = T0 / (1 + k)
In the ‘boltzmann’ schedule the updates are:
std = minimum(sqrt(T) * ones(d), (upper - lower) / (3*learn_rate))
y ~ Normal(0, std, size = d)
x_new = x_old + learn_rate * y
T_new = T0 / log(1 + k)
Examples
Example 1. We illustrate the use of anneal to seek the global minimum of a function of two variables that is equal to the sum of a positive- definite quadratic and two deep “Gaussian-shaped” craters. Specifically, define the objective function f as the sum of three other functions, f = f1 + f2 + f3. We suppose each of these has a signature (z, *params), where z = (x, y), params, and the functions are as defined below.
>>> params = (2, 3, 7, 8, 9, 10, 44, -1, 2, 26, 1, -2, 0.5)
>>> def f1(z, *params):
... x, y = z
... a, b, c, d, e, f, g, h, i, j, k, l, scale = params
... return (a * x**2 + b * x * y + c * y**2 + d*x + e*y + f)
>>> def f2(z, *params):
... x, y = z
... a, b, c, d, e, f, g, h, i, j, k, l, scale = params
... return (-g*np.exp(-((x-h)**2 + (y-i)**2) / scale))
>>> def f3(z, *params):
... x, y = z
... a, b, c, d, e, f, g, h, i, j, k, l, scale = params
... return (-j*np.exp(-((x-k)**2 + (y-l)**2) / scale))
>>> def f(z, *params):
... x, y = z
... a, b, c, d, e, f, g, h, i, j, k, l, scale = params
... return f1(z, *params) + f2(z, *params) + f3(z, *params)
>>> x0 = np.array([2., 2.]) # Initial guess.
>>> from scipy import optimize
>>> np.random.seed(555) # Seeded to allow replication.
>>> res = optimize.anneal(f, x0, args=params, schedule='boltzmann',
full_output=True, maxiter=500, lower=-10,
upper=10, dwell=250, disp=True)
Warning: Maximum number of iterations exceeded.
>>> res[0] # obtained minimum
array([-1.03914194, 1.81330654])
>>> res[1] # function value at minimum
-3.3817...
So this run settled on the point [-1.039, 1.813] with a minimum function value of about -3.382. The final temperature was about 212. The run used 125301 function evaluations, 501 iterations (including the initial guess as a iteration), and accepted 61162 points. The status flag of 3 also indicates that maxiter was reached.
This problem’s true global minimum lies near the point [-1.057, 1.808] and has a value of about -3.409. So these anneal results are pretty good and could be used as the starting guess in a local optimizer to seek a more exact local minimum.
Example 2. To minimize the same objective function using the minimize approach, we need to (a) convert the options to an “options dictionary” using the keys prescribed for this method, (b) call the minimize function with the name of the method (which in this case is ‘Anneal’), and (c) take account of the fact that the returned value will be a OptimizeResult object (i.e., a dictionary, as defined in optimize.py).
All of the allowable options for ‘Anneal’ when using the minimize approach are listed in the myopts dictionary given below, although in practice only the non-default values would be needed. Some of their names differ from those used in the anneal approach. We can proceed as follows:
>>> myopts = {
'schedule' : 'boltzmann', # Non-default value.
'maxfev' : None, # Default, formerly `maxeval`.
'maxiter' : 500, # Non-default value.
'maxaccept' : None, # Default value.
'ftol' : 1e-6, # Default, formerly `feps`.
'T0' : None, # Default value.
'Tf' : 1e-12, # Default value.
'boltzmann' : 1.0, # Default value.
'learn_rate' : 0.5, # Default value.
'quench' : 1.0, # Default value.
'm' : 1.0, # Default value.
'n' : 1.0, # Default value.
'lower' : -10, # Non-default value.
'upper' : +10, # Non-default value.
'dwell' : 250, # Non-default value.
'disp' : True # Default value.
}
>>> from scipy import optimize
>>> np.random.seed(777) # Seeded to allow replication.
>>> res2 = optimize.minimize(f, x0, args=params, method='Anneal',
options=myopts)
Warning: Maximum number of iterations exceeded.
>>> res2
status: 3
success: False
accept: 61742
nfev: 125301
T: 214.20624873839623
fun: -3.4084065576676053
x: array([-1.05757366, 1.8071427 ])
message: 'Maximum cooling iterations reached'
nit: 501